package models import ( "time" "github.com/google/uuid" ) // User represents a user with full authentication support type User struct { ID uuid.UUID `json:"id" db:"id"` ExternalID *string `json:"external_id,omitempty" db:"external_id"` Email string `json:"email" db:"email"` PasswordHash *string `json:"-" db:"password_hash"` // Never exposed in JSON Name *string `json:"name,omitempty" db:"name"` Role string `json:"role" db:"role"` // 'user', 'admin', 'super_admin', 'data_protection_officer' EmailVerified bool `json:"email_verified" db:"email_verified"` EmailVerifiedAt *time.Time `json:"email_verified_at,omitempty" db:"email_verified_at"` AccountStatus string `json:"account_status" db:"account_status"` // 'active', 'suspended', 'locked' LastLoginAt *time.Time `json:"last_login_at,omitempty" db:"last_login_at"` FailedLoginAttempts int `json:"failed_login_attempts" db:"failed_login_attempts"` LockedUntil *time.Time `json:"locked_until,omitempty" db:"locked_until"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // LegalDocument represents a type of legal document (e.g., Terms, Privacy Policy) type LegalDocument struct { ID uuid.UUID `json:"id" db:"id"` Type string `json:"type" db:"type"` // 'terms', 'privacy', 'cookies', 'community' Name string `json:"name" db:"name"` Description *string `json:"description" db:"description"` IsMandatory bool `json:"is_mandatory" db:"is_mandatory"` IsActive bool `json:"is_active" db:"is_active"` SortOrder int `json:"sort_order" db:"sort_order"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // DocumentVersion represents a specific version of a legal document type DocumentVersion struct { ID uuid.UUID `json:"id" db:"id"` DocumentID uuid.UUID `json:"document_id" db:"document_id"` Version string `json:"version" db:"version"` // Semver: 1.0.0, 1.1.0 Language string `json:"language" db:"language"` // ISO 639-1: de, en Title string `json:"title" db:"title"` Content string `json:"content" db:"content"` // HTML or Markdown Summary *string `json:"summary" db:"summary"` // Summary of changes Status string `json:"status" db:"status"` // 'draft', 'review', 'approved', 'scheduled', 'published', 'archived' PublishedAt *time.Time `json:"published_at" db:"published_at"` ScheduledPublishAt *time.Time `json:"scheduled_publish_at" db:"scheduled_publish_at"` CreatedBy *uuid.UUID `json:"created_by" db:"created_by"` ApprovedBy *uuid.UUID `json:"approved_by" db:"approved_by"` ApprovedAt *time.Time `json:"approved_at" db:"approved_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // UserConsent represents a user's consent to a document version type UserConsent struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` DocumentVersionID uuid.UUID `json:"document_version_id" db:"document_version_id"` Consented bool `json:"consented" db:"consented"` IPAddress *string `json:"ip_address" db:"ip_address"` UserAgent *string `json:"user_agent" db:"user_agent"` ConsentedAt time.Time `json:"consented_at" db:"consented_at"` WithdrawnAt *time.Time `json:"withdrawn_at" db:"withdrawn_at"` } // CookieCategory represents a category of cookies type CookieCategory struct { ID uuid.UUID `json:"id" db:"id"` Name string `json:"name" db:"name"` // 'necessary', 'functional', 'analytics', 'marketing' DisplayNameDE string `json:"display_name_de" db:"display_name_de"` DisplayNameEN *string `json:"display_name_en" db:"display_name_en"` DescriptionDE *string `json:"description_de" db:"description_de"` DescriptionEN *string `json:"description_en" db:"description_en"` IsMandatory bool `json:"is_mandatory" db:"is_mandatory"` SortOrder int `json:"sort_order" db:"sort_order"` IsActive bool `json:"is_active" db:"is_active"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // CookieConsent represents a user's cookie preferences type CookieConsent struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` CategoryID uuid.UUID `json:"category_id" db:"category_id"` Consented bool `json:"consented" db:"consented"` ConsentedAt time.Time `json:"consented_at" db:"consented_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // AuditLog represents an audit trail entry for GDPR compliance type AuditLog struct { ID uuid.UUID `json:"id" db:"id"` UserID *uuid.UUID `json:"user_id" db:"user_id"` Action string `json:"action" db:"action"` // 'consent_given', 'consent_withdrawn', 'data_export', 'data_delete' EntityType *string `json:"entity_type" db:"entity_type"` // 'document', 'cookie_category' EntityID *uuid.UUID `json:"entity_id" db:"entity_id"` Details *string `json:"details" db:"details"` // JSON string IPAddress *string `json:"ip_address" db:"ip_address"` UserAgent *string `json:"user_agent" db:"user_agent"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // DataExportRequest represents a user's request to export their data type DataExportRequest struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` Status string `json:"status" db:"status"` // 'pending', 'processing', 'completed', 'failed' DownloadURL *string `json:"download_url" db:"download_url"` ExpiresAt *time.Time `json:"expires_at" db:"expires_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` CompletedAt *time.Time `json:"completed_at" db:"completed_at"` } // DataDeletionRequest represents a user's request to delete their data type DataDeletionRequest struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` Status string `json:"status" db:"status"` // 'pending', 'processing', 'completed', 'failed' Reason *string `json:"reason" db:"reason"` CreatedAt time.Time `json:"created_at" db:"created_at"` ProcessedAt *time.Time `json:"processed_at" db:"processed_at"` ProcessedBy *uuid.UUID `json:"processed_by" db:"processed_by"` } // ======================================== // DTOs (Data Transfer Objects) // ======================================== // CreateConsentRequest is the request body for creating a consent type CreateConsentRequest struct { DocumentType string `json:"document_type" binding:"required"` VersionID string `json:"version_id" binding:"required"` Consented bool `json:"consented"` } // CookieConsentRequest is the request body for setting cookie preferences type CookieConsentRequest struct { Categories []CookieCategoryConsent `json:"categories" binding:"required"` } // CookieCategoryConsent represents consent for a single cookie category type CookieCategoryConsent struct { CategoryID string `json:"category_id" binding:"required"` Consented bool `json:"consented"` } // ConsentCheckResponse is the response for checking consent status type ConsentCheckResponse struct { HasConsent bool `json:"has_consent"` CurrentVersionID *string `json:"current_version_id,omitempty"` ConsentedVersion *string `json:"consented_version,omitempty"` NeedsUpdate bool `json:"needs_update"` ConsentedAt *time.Time `json:"consented_at,omitempty"` } // DocumentWithVersion combines document info with its latest published version type DocumentWithVersion struct { Document LegalDocument `json:"document"` LatestVersion *DocumentVersion `json:"latest_version,omitempty"` } // ConsentHistory represents a user's consent history for a document type ConsentHistory struct { Document LegalDocument `json:"document"` Version DocumentVersion `json:"version"` Consent UserConsent `json:"consent"` } // ConsentStats represents statistics about consents type ConsentStats struct { TotalUsers int `json:"total_users"` ConsentedUsers int `json:"consented_users"` ConsentRate float64 `json:"consent_rate"` RecentConsents int `json:"recent_consents"` // Last 7 days RecentWithdrawals int `json:"recent_withdrawals"` } // CookieStats represents statistics about cookie consents type CookieStats struct { Category string `json:"category"` TotalUsers int `json:"total_users"` ConsentedUsers int `json:"consented_users"` ConsentRate float64 `json:"consent_rate"` } // MyDataResponse represents all data we have about a user type MyDataResponse struct { User User `json:"user"` Consents []ConsentHistory `json:"consents"` CookieConsents []CookieConsent `json:"cookie_consents"` AuditLog []AuditLog `json:"audit_log"` ExportedAt time.Time `json:"exported_at"` } // CreateDocumentRequest is the request body for creating a document type CreateDocumentRequest struct { Type string `json:"type" binding:"required"` Name string `json:"name" binding:"required"` Description *string `json:"description"` IsMandatory bool `json:"is_mandatory"` } // CreateVersionRequest is the request body for creating a document version type CreateVersionRequest struct { DocumentID string `json:"document_id" binding:"required"` Version string `json:"version" binding:"required"` Language string `json:"language" binding:"required"` Title string `json:"title" binding:"required"` Content string `json:"content" binding:"required"` Summary *string `json:"summary"` } // UpdateVersionRequest is the request body for updating a version type UpdateVersionRequest struct { Title *string `json:"title"` Content *string `json:"content"` Summary *string `json:"summary"` Status *string `json:"status"` } // CreateCookieCategoryRequest is the request body for creating a cookie category type CreateCookieCategoryRequest struct { Name string `json:"name" binding:"required"` DisplayNameDE string `json:"display_name_de" binding:"required"` DisplayNameEN *string `json:"display_name_en"` DescriptionDE *string `json:"description_de"` DescriptionEN *string `json:"description_en"` IsMandatory bool `json:"is_mandatory"` SortOrder int `json:"sort_order"` } // ======================================== // Phase 1: Authentication Models // ======================================== // EmailVerificationToken for email verification type EmailVerificationToken struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` Token string `json:"token" db:"token"` ExpiresAt time.Time `json:"expires_at" db:"expires_at"` UsedAt *time.Time `json:"used_at,omitempty" db:"used_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // PasswordResetToken for password reset type PasswordResetToken struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` Token string `json:"token" db:"token"` ExpiresAt time.Time `json:"expires_at" db:"expires_at"` UsedAt *time.Time `json:"used_at,omitempty" db:"used_at"` IPAddress *string `json:"ip_address,omitempty" db:"ip_address"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // UserSession for session management type UserSession struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` TokenHash string `json:"-" db:"token_hash"` DeviceInfo *string `json:"device_info,omitempty" db:"device_info"` IPAddress *string `json:"ip_address,omitempty" db:"ip_address"` UserAgent *string `json:"user_agent,omitempty" db:"user_agent"` ExpiresAt time.Time `json:"expires_at" db:"expires_at"` RevokedAt *time.Time `json:"revoked_at,omitempty" db:"revoked_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` LastActivityAt time.Time `json:"last_activity_at" db:"last_activity_at"` } // RegisterRequest for user registration type RegisterRequest struct { Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required,min=8"` Name *string `json:"name"` } // LoginRequest for user login type LoginRequest struct { Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required"` } // LoginResponse after successful login type LoginResponse struct { User User `json:"user"` AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` ExpiresIn int `json:"expires_in"` // seconds } // RefreshTokenRequest for token refresh type RefreshTokenRequest struct { RefreshToken string `json:"refresh_token" binding:"required"` } // VerifyEmailRequest for email verification type VerifyEmailRequest struct { Token string `json:"token" binding:"required"` } // ForgotPasswordRequest for password reset request type ForgotPasswordRequest struct { Email string `json:"email" binding:"required,email"` } // ResetPasswordRequest for password reset type ResetPasswordRequest struct { Token string `json:"token" binding:"required"` NewPassword string `json:"new_password" binding:"required,min=8"` } // ChangePasswordRequest for changing password type ChangePasswordRequest struct { CurrentPassword string `json:"current_password" binding:"required"` NewPassword string `json:"new_password" binding:"required,min=8"` } // UpdateProfileRequest for profile updates type UpdateProfileRequest struct { Name *string `json:"name"` } // ======================================== // Phase 3: Version Approval Models // ======================================== // VersionApproval tracks the approval workflow type VersionApproval struct { ID uuid.UUID `json:"id" db:"id"` VersionID uuid.UUID `json:"version_id" db:"version_id"` ApproverID uuid.UUID `json:"approver_id" db:"approver_id"` Action string `json:"action" db:"action"` // 'submitted_for_review', 'approved', 'rejected', 'published' Comment *string `json:"comment,omitempty" db:"comment"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // SubmitForReviewRequest for submitting a version for review type SubmitForReviewRequest struct { Comment *string `json:"comment"` } // ApproveVersionRequest for approving a version (DSB) type ApproveVersionRequest struct { Comment *string `json:"comment"` ScheduledPublishAt *string `json:"scheduled_publish_at"` // ISO 8601 datetime for scheduled publishing } // RejectVersionRequest for rejecting a version type RejectVersionRequest struct { Comment string `json:"comment" binding:"required"` } // VersionCompareResponse for comparing versions type VersionCompareResponse struct { Published *DocumentVersion `json:"published,omitempty"` Draft *DocumentVersion `json:"draft"` Diff *string `json:"diff,omitempty"` Approvals []VersionApproval `json:"approvals"` } // ======================================== // Phase 4: Notification Models // ======================================== // Notification represents a user notification type Notification struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` Type string `json:"type" db:"type"` // 'new_version', 'consent_reminder', 'account_warning' Channel string `json:"channel" db:"channel"` // 'email', 'in_app', 'push' Title string `json:"title" db:"title"` Body string `json:"body" db:"body"` Data *string `json:"data,omitempty" db:"data"` // JSON string ReadAt *time.Time `json:"read_at,omitempty" db:"read_at"` SentAt *time.Time `json:"sent_at,omitempty" db:"sent_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // PushSubscription for Web Push notifications type PushSubscription struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` Endpoint string `json:"endpoint" db:"endpoint"` P256dh string `json:"p256dh" db:"p256dh"` Auth string `json:"auth" db:"auth"` UserAgent *string `json:"user_agent,omitempty" db:"user_agent"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // NotificationPreferences for user notification settings type NotificationPreferences struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` EmailEnabled bool `json:"email_enabled" db:"email_enabled"` PushEnabled bool `json:"push_enabled" db:"push_enabled"` InAppEnabled bool `json:"in_app_enabled" db:"in_app_enabled"` ReminderFrequency string `json:"reminder_frequency" db:"reminder_frequency"` // 'daily', 'weekly', 'never' CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // SubscribePushRequest for subscribing to push notifications type SubscribePushRequest struct { Endpoint string `json:"endpoint" binding:"required"` P256dh string `json:"p256dh" binding:"required"` Auth string `json:"auth" binding:"required"` } // UpdateNotificationPreferencesRequest for updating preferences type UpdateNotificationPreferencesRequest struct { EmailEnabled *bool `json:"email_enabled"` PushEnabled *bool `json:"push_enabled"` InAppEnabled *bool `json:"in_app_enabled"` ReminderFrequency *string `json:"reminder_frequency"` } // ======================================== // Phase 6: OAuth 2.0 Authorization Code Flow // ======================================== // OAuthClient represents a registered OAuth 2.0 client application type OAuthClient struct { ID uuid.UUID `json:"id" db:"id"` ClientID string `json:"client_id" db:"client_id"` ClientSecret string `json:"-" db:"client_secret"` // Never expose in JSON Name string `json:"name" db:"name"` Description *string `json:"description,omitempty" db:"description"` RedirectURIs []string `json:"redirect_uris" db:"redirect_uris"` // JSON array Scopes []string `json:"scopes" db:"scopes"` // Allowed scopes GrantTypes []string `json:"grant_types" db:"grant_types"` // authorization_code, refresh_token IsPublic bool `json:"is_public" db:"is_public"` // Public clients (SPAs) don't have secret IsActive bool `json:"is_active" db:"is_active"` CreatedBy *uuid.UUID `json:"created_by,omitempty" db:"created_by"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // OAuthAuthorizationCode represents an authorization code for the OAuth flow type OAuthAuthorizationCode struct { ID uuid.UUID `json:"id" db:"id"` Code string `json:"-" db:"code"` // Hashed ClientID string `json:"client_id" db:"client_id"` UserID uuid.UUID `json:"user_id" db:"user_id"` RedirectURI string `json:"redirect_uri" db:"redirect_uri"` Scopes []string `json:"scopes" db:"scopes"` CodeChallenge *string `json:"-" db:"code_challenge"` // For PKCE CodeChallengeMethod *string `json:"-" db:"code_challenge_method"` // S256 or plain ExpiresAt time.Time `json:"expires_at" db:"expires_at"` UsedAt *time.Time `json:"used_at,omitempty" db:"used_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // OAuthAccessToken represents an OAuth access token type OAuthAccessToken struct { ID uuid.UUID `json:"id" db:"id"` TokenHash string `json:"-" db:"token_hash"` ClientID string `json:"client_id" db:"client_id"` UserID uuid.UUID `json:"user_id" db:"user_id"` Scopes []string `json:"scopes" db:"scopes"` ExpiresAt time.Time `json:"expires_at" db:"expires_at"` RevokedAt *time.Time `json:"revoked_at,omitempty" db:"revoked_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // OAuthRefreshToken represents an OAuth refresh token type OAuthRefreshToken struct { ID uuid.UUID `json:"id" db:"id"` TokenHash string `json:"-" db:"token_hash"` AccessTokenID uuid.UUID `json:"access_token_id" db:"access_token_id"` ClientID string `json:"client_id" db:"client_id"` UserID uuid.UUID `json:"user_id" db:"user_id"` Scopes []string `json:"scopes" db:"scopes"` ExpiresAt time.Time `json:"expires_at" db:"expires_at"` RevokedAt *time.Time `json:"revoked_at,omitempty" db:"revoked_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // OAuthAuthorizeRequest for the authorization endpoint type OAuthAuthorizeRequest struct { ResponseType string `form:"response_type" binding:"required"` // Must be "code" ClientID string `form:"client_id" binding:"required"` RedirectURI string `form:"redirect_uri" binding:"required"` Scope string `form:"scope"` // Space-separated scopes State string `form:"state" binding:"required"` // CSRF protection CodeChallenge string `form:"code_challenge"` // PKCE CodeChallengeMethod string `form:"code_challenge_method"` // S256 (recommended) or plain } // OAuthTokenRequest for the token endpoint type OAuthTokenRequest struct { GrantType string `form:"grant_type" binding:"required"` // authorization_code or refresh_token Code string `form:"code"` // For authorization_code grant RedirectURI string `form:"redirect_uri"` // For authorization_code grant ClientID string `form:"client_id" binding:"required"` ClientSecret string `form:"client_secret"` // For confidential clients CodeVerifier string `form:"code_verifier"` // For PKCE RefreshToken string `form:"refresh_token"` // For refresh_token grant Scope string `form:"scope"` // For refresh_token grant (optional) } // OAuthTokenResponse for successful token requests type OAuthTokenResponse struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` // Always "Bearer" ExpiresIn int `json:"expires_in"` // Seconds until expiration RefreshToken string `json:"refresh_token,omitempty"` Scope string `json:"scope,omitempty"` } // OAuthErrorResponse for OAuth errors (RFC 6749) type OAuthErrorResponse struct { Error string `json:"error"` ErrorDescription string `json:"error_description,omitempty"` ErrorURI string `json:"error_uri,omitempty"` } // ======================================== // Phase 7: Two-Factor Authentication (2FA/TOTP) // ======================================== // UserTOTP stores 2FA TOTP configuration for a user type UserTOTP struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` Secret string `json:"-" db:"secret"` // Encrypted TOTP secret Verified bool `json:"verified" db:"verified"` // Has 2FA been verified/activated RecoveryCodes []string `json:"-" db:"recovery_codes"` // Encrypted backup codes EnabledAt *time.Time `json:"enabled_at,omitempty" db:"enabled_at"` LastUsedAt *time.Time `json:"last_used_at,omitempty" db:"last_used_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // TwoFactorChallenge represents a pending 2FA challenge during login type TwoFactorChallenge struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` ChallengeID string `json:"challenge_id" db:"challenge_id"` // Temporary token IPAddress *string `json:"ip_address,omitempty" db:"ip_address"` UserAgent *string `json:"user_agent,omitempty" db:"user_agent"` ExpiresAt time.Time `json:"expires_at" db:"expires_at"` UsedAt *time.Time `json:"used_at,omitempty" db:"used_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // Setup2FAResponse when initiating 2FA setup type Setup2FAResponse struct { Secret string `json:"secret"` // Base32 encoded secret for manual entry QRCodeDataURL string `json:"qr_code"` // Data URL for QR code image RecoveryCodes []string `json:"recovery_codes"` // One-time backup codes } // Verify2FARequest for verifying 2FA setup or login type Verify2FARequest struct { Code string `json:"code" binding:"required"` // 6-digit TOTP code ChallengeID string `json:"challenge_id,omitempty"` // For login flow } // TwoFactorLoginResponse when 2FA is required during login type TwoFactorLoginResponse struct { RequiresTwoFactor bool `json:"requires_two_factor"` ChallengeID string `json:"challenge_id"` // Use this to complete 2FA Message string `json:"message"` } // Complete2FALoginRequest to complete login with 2FA type Complete2FALoginRequest struct { ChallengeID string `json:"challenge_id" binding:"required"` Code string `json:"code" binding:"required"` // 6-digit TOTP or recovery code } // Disable2FARequest for disabling 2FA type Disable2FARequest struct { Password string `json:"password" binding:"required"` // Require password confirmation Code string `json:"code" binding:"required"` // Current TOTP code } // RecoveryCodeUseRequest for using a recovery code type RecoveryCodeUseRequest struct { ChallengeID string `json:"challenge_id" binding:"required"` RecoveryCode string `json:"recovery_code" binding:"required"` } // TwoFactorStatusResponse for checking 2FA status type TwoFactorStatusResponse struct { Enabled bool `json:"enabled"` Verified bool `json:"verified"` EnabledAt *time.Time `json:"enabled_at,omitempty"` RecoveryCodesCount int `json:"recovery_codes_count"` } // Verify2FAChallengeRequest for verifying a 2FA challenge during login type Verify2FAChallengeRequest struct { ChallengeID string `json:"challenge_id" binding:"required"` Code string `json:"code,omitempty"` // 6-digit TOTP code RecoveryCode string `json:"recovery_code,omitempty"` // Alternative: recovery code } // ======================================== // Phase 5: Consent Deadline Models // ======================================== // ConsentDeadline tracks consent deadlines per user type ConsentDeadline struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` DocumentVersionID uuid.UUID `json:"document_version_id" db:"document_version_id"` DeadlineAt time.Time `json:"deadline_at" db:"deadline_at"` ReminderCount int `json:"reminder_count" db:"reminder_count"` LastReminderAt *time.Time `json:"last_reminder_at,omitempty" db:"last_reminder_at"` ConsentGivenAt *time.Time `json:"consent_given_at,omitempty" db:"consent_given_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // AccountSuspension tracks account suspensions type AccountSuspension struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` Reason string `json:"reason" db:"reason"` // 'consent_deadline_exceeded' Details *string `json:"details,omitempty" db:"details"` // JSON SuspendedAt time.Time `json:"suspended_at" db:"suspended_at"` LiftedAt *time.Time `json:"lifted_at,omitempty" db:"lifted_at"` LiftedReason *string `json:"lifted_reason,omitempty" db:"lifted_reason"` } // PendingConsentResponse for pending consents with deadline info type PendingConsentResponse struct { Document LegalDocument `json:"document"` Version DocumentVersion `json:"version"` DeadlineAt time.Time `json:"deadline_at"` DaysLeft int `json:"days_left"` IsOverdue bool `json:"is_overdue"` } // AccountStatusResponse for account status check type AccountStatusResponse struct { Status string `json:"status"` // 'active', 'suspended' PendingConsents []PendingConsentResponse `json:"pending_consents,omitempty"` SuspensionReason *string `json:"suspension_reason,omitempty"` CanAccess bool `json:"can_access"` } // ======================================== // Phase 8: E-Mail Templates (Transactional) // ======================================== // EmailTemplateType defines the types of transactional emails // These are like document types but for emails const ( // Auth & Security EmailTypeWelcome = "welcome" EmailTypeEmailVerification = "email_verification" EmailTypePasswordReset = "password_reset" EmailTypePasswordChanged = "password_changed" EmailType2FAEnabled = "2fa_enabled" EmailType2FADisabled = "2fa_disabled" EmailTypeNewDeviceLogin = "new_device_login" EmailTypeSuspiciousActivity = "suspicious_activity" EmailTypeAccountLocked = "account_locked" EmailTypeAccountUnlocked = "account_unlocked" // Account Lifecycle EmailTypeDeletionRequested = "deletion_requested" EmailTypeDeletionConfirmed = "deletion_confirmed" EmailTypeDataExportReady = "data_export_ready" EmailTypeEmailChanged = "email_changed" EmailTypeEmailChangeVerify = "email_change_verify" // Consent-related EmailTypeNewVersionPublished = "new_version_published" EmailTypeConsentReminder = "consent_reminder" EmailTypeConsentDeadlineWarning = "consent_deadline_warning" EmailTypeAccountSuspended = "account_suspended" ) // EmailTemplate represents a template for transactional emails (like LegalDocument) type EmailTemplate struct { ID uuid.UUID `json:"id" db:"id"` Type string `json:"type" db:"type"` // One of EmailType constants Name string `json:"name" db:"name"` // Human-readable name Description *string `json:"description" db:"description"` IsActive bool `json:"is_active" db:"is_active"` SortOrder int `json:"sort_order" db:"sort_order"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // EmailTemplateVersion represents a specific version of an email template (like DocumentVersion) type EmailTemplateVersion struct { ID uuid.UUID `json:"id" db:"id"` TemplateID uuid.UUID `json:"template_id" db:"template_id"` Version string `json:"version" db:"version"` // Semver: 1.0.0 Language string `json:"language" db:"language"` // ISO 639-1: de, en Subject string `json:"subject" db:"subject"` // Email subject line BodyHTML string `json:"body_html" db:"body_html"` // HTML version BodyText string `json:"body_text" db:"body_text"` // Plain text version Summary *string `json:"summary" db:"summary"` // Change summary Status string `json:"status" db:"status"` // draft, review, approved, published, archived PublishedAt *time.Time `json:"published_at" db:"published_at"` ScheduledPublishAt *time.Time `json:"scheduled_publish_at" db:"scheduled_publish_at"` CreatedBy *uuid.UUID `json:"created_by" db:"created_by"` ApprovedBy *uuid.UUID `json:"approved_by" db:"approved_by"` ApprovedAt *time.Time `json:"approved_at" db:"approved_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // EmailTemplateApproval tracks approval workflow for email templates type EmailTemplateApproval struct { ID uuid.UUID `json:"id" db:"id"` VersionID uuid.UUID `json:"version_id" db:"version_id"` ApproverID uuid.UUID `json:"approver_id" db:"approver_id"` Action string `json:"action" db:"action"` // submitted_for_review, approved, rejected, published Comment *string `json:"comment,omitempty" db:"comment"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // EmailSendLog tracks sent emails for audit purposes type EmailSendLog struct { ID uuid.UUID `json:"id" db:"id"` UserID *uuid.UUID `json:"user_id,omitempty" db:"user_id"` VersionID uuid.UUID `json:"version_id" db:"version_id"` Recipient string `json:"recipient" db:"recipient"` // Email address Subject string `json:"subject" db:"subject"` Status string `json:"status" db:"status"` // queued, sent, delivered, bounced, failed ErrorMsg *string `json:"error_msg,omitempty" db:"error_msg"` Variables *string `json:"variables,omitempty" db:"variables"` // JSON of template variables used SentAt *time.Time `json:"sent_at,omitempty" db:"sent_at"` DeliveredAt *time.Time `json:"delivered_at,omitempty" db:"delivered_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // EmailTemplateSettings stores global email settings (logo, signature, etc.) type EmailTemplateSettings struct { ID uuid.UUID `json:"id" db:"id"` LogoURL *string `json:"logo_url" db:"logo_url"` LogoBase64 *string `json:"logo_base64" db:"logo_base64"` // For embedding in emails CompanyName string `json:"company_name" db:"company_name"` SenderName string `json:"sender_name" db:"sender_name"` SenderEmail string `json:"sender_email" db:"sender_email"` ReplyToEmail *string `json:"reply_to_email" db:"reply_to_email"` FooterHTML *string `json:"footer_html" db:"footer_html"` FooterText *string `json:"footer_text" db:"footer_text"` PrimaryColor string `json:"primary_color" db:"primary_color"` // Hex color SecondaryColor string `json:"secondary_color" db:"secondary_color"` // Hex color UpdatedAt time.Time `json:"updated_at" db:"updated_at"` UpdatedBy *uuid.UUID `json:"updated_by" db:"updated_by"` } // ======================================== // E-Mail Template DTOs // ======================================== // CreateEmailTemplateRequest for creating a new email template type type CreateEmailTemplateRequest struct { Type string `json:"type" binding:"required"` Name string `json:"name" binding:"required"` Description *string `json:"description"` } // CreateEmailTemplateVersionRequest for creating a new version of an email template type CreateEmailTemplateVersionRequest struct { TemplateID string `json:"template_id" binding:"required"` Version string `json:"version" binding:"required"` Language string `json:"language" binding:"required"` Subject string `json:"subject" binding:"required"` BodyHTML string `json:"body_html" binding:"required"` BodyText string `json:"body_text" binding:"required"` Summary *string `json:"summary"` } // UpdateEmailTemplateVersionRequest for updating a version type UpdateEmailTemplateVersionRequest struct { Subject *string `json:"subject"` BodyHTML *string `json:"body_html"` BodyText *string `json:"body_text"` Summary *string `json:"summary"` Status *string `json:"status"` } // UpdateEmailTemplateSettingsRequest for updating global settings type UpdateEmailTemplateSettingsRequest struct { LogoURL *string `json:"logo_url"` LogoBase64 *string `json:"logo_base64"` CompanyName *string `json:"company_name"` SenderName *string `json:"sender_name"` SenderEmail *string `json:"sender_email"` ReplyToEmail *string `json:"reply_to_email"` FooterHTML *string `json:"footer_html"` FooterText *string `json:"footer_text"` PrimaryColor *string `json:"primary_color"` SecondaryColor *string `json:"secondary_color"` } // EmailTemplateWithVersion combines template info with its latest published version type EmailTemplateWithVersion struct { Template EmailTemplate `json:"template"` LatestVersion *EmailTemplateVersion `json:"latest_version,omitempty"` } // SendTestEmailRequest for sending a test email type SendTestEmailRequest struct { VersionID string `json:"version_id" binding:"required"` Recipient string `json:"recipient" binding:"required,email"` Variables map[string]string `json:"variables"` // Template variable overrides } // EmailPreviewResponse for previewing an email type EmailPreviewResponse struct { Subject string `json:"subject"` BodyHTML string `json:"body_html"` BodyText string `json:"body_text"` } // EmailTemplateVariables defines available variables for each template type type EmailTemplateVariables struct { TemplateType string `json:"template_type"` Variables []string `json:"variables"` Descriptions map[string]string `json:"descriptions"` } // EmailStats represents statistics about email sends type EmailStats struct { TotalSent int `json:"total_sent"` Delivered int `json:"delivered"` Bounced int `json:"bounced"` Failed int `json:"failed"` DeliveryRate float64 `json:"delivery_rate"` RecentSent int `json:"recent_sent"` // Last 7 days } // ======================================== // Phase 9: Schulverwaltung / School Management // Matrix-basierte Kommunikation für Schulen // ======================================== // SchoolRole defines roles within the school system const ( SchoolRoleTeacher = "teacher" SchoolRoleClassTeacher = "class_teacher" SchoolRoleParent = "parent" SchoolRoleParentRep = "parent_representative" SchoolRoleStudent = "student" SchoolRoleAdmin = "school_admin" SchoolRolePrincipal = "principal" SchoolRoleSecretary = "secretary" ) // AttendanceStatus defines the status of student attendance const ( AttendancePresent = "present" AttendanceAbsent = "absent" AttendanceAbsentExcused = "excused" AttendanceAbsentUnexcused = "unexcused" AttendanceLate = "late" AttendanceLateExcused = "late_excused" AttendancePending = "pending_confirmation" ) // School represents a school/educational institution type School struct { ID uuid.UUID `json:"id" db:"id"` Name string `json:"name" db:"name"` ShortName *string `json:"short_name,omitempty" db:"short_name"` Type string `json:"type" db:"type"` // 'grundschule', 'hauptschule', 'realschule', 'gymnasium', 'gesamtschule', 'berufsschule' Address *string `json:"address,omitempty" db:"address"` City *string `json:"city,omitempty" db:"city"` PostalCode *string `json:"postal_code,omitempty" db:"postal_code"` State *string `json:"state,omitempty" db:"state"` // Bundesland Country string `json:"country" db:"country"` // Default: DE Phone *string `json:"phone,omitempty" db:"phone"` Email *string `json:"email,omitempty" db:"email"` Website *string `json:"website,omitempty" db:"website"` MatrixServerName *string `json:"matrix_server_name,omitempty" db:"matrix_server_name"` // Optional: eigener Matrix-Server LogoURL *string `json:"logo_url,omitempty" db:"logo_url"` IsActive bool `json:"is_active" db:"is_active"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // SchoolYear represents an academic year type SchoolYear struct { ID uuid.UUID `json:"id" db:"id"` SchoolID uuid.UUID `json:"school_id" db:"school_id"` Name string `json:"name" db:"name"` // e.g., "2024/2025" StartDate time.Time `json:"start_date" db:"start_date"` EndDate time.Time `json:"end_date" db:"end_date"` IsCurrent bool `json:"is_current" db:"is_current"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // Class represents a school class type Class struct { ID uuid.UUID `json:"id" db:"id"` SchoolID uuid.UUID `json:"school_id" db:"school_id"` SchoolYearID uuid.UUID `json:"school_year_id" db:"school_year_id"` Name string `json:"name" db:"name"` // e.g., "5a", "10b" Grade int `json:"grade" db:"grade"` // Klassenstufe: 1-13 Section *string `json:"section,omitempty" db:"section"` // e.g., "a", "b", "c" Room *string `json:"room,omitempty" db:"room"` // Klassenzimmer MatrixInfoRoom *string `json:"matrix_info_room,omitempty" db:"matrix_info_room"` // Broadcast-Raum MatrixRepRoom *string `json:"matrix_rep_room,omitempty" db:"matrix_rep_room"` // Elternvertreter-Raum IsActive bool `json:"is_active" db:"is_active"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // Subject represents a school subject type Subject struct { ID uuid.UUID `json:"id" db:"id"` SchoolID uuid.UUID `json:"school_id" db:"school_id"` Name string `json:"name" db:"name"` // e.g., "Mathematik", "Deutsch" ShortName string `json:"short_name" db:"short_name"` // e.g., "Ma", "De" Color *string `json:"color,omitempty" db:"color"` // Hex color for display IsActive bool `json:"is_active" db:"is_active"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // Student represents a student type Student struct { ID uuid.UUID `json:"id" db:"id"` SchoolID uuid.UUID `json:"school_id" db:"school_id"` ClassID uuid.UUID `json:"class_id" db:"class_id"` UserID *uuid.UUID `json:"user_id,omitempty" db:"user_id"` // Optional: linked account StudentNumber *string `json:"student_number,omitempty" db:"student_number"` // Internal ID FirstName string `json:"first_name" db:"first_name"` LastName string `json:"last_name" db:"last_name"` DateOfBirth *time.Time `json:"date_of_birth,omitempty" db:"date_of_birth"` Gender *string `json:"gender,omitempty" db:"gender"` // 'm', 'f', 'd' MatrixUserID *string `json:"matrix_user_id,omitempty" db:"matrix_user_id"` MatrixDMRoom *string `json:"matrix_dm_room,omitempty" db:"matrix_dm_room"` // Kind-Dialograum IsActive bool `json:"is_active" db:"is_active"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // Teacher represents a teacher type Teacher struct { ID uuid.UUID `json:"id" db:"id"` SchoolID uuid.UUID `json:"school_id" db:"school_id"` UserID uuid.UUID `json:"user_id" db:"user_id"` // Linked user account TeacherCode *string `json:"teacher_code,omitempty" db:"teacher_code"` // e.g., "MÜL" for Müller Title *string `json:"title,omitempty" db:"title"` // e.g., "Dr.", "StR" FirstName string `json:"first_name" db:"first_name"` LastName string `json:"last_name" db:"last_name"` MatrixUserID *string `json:"matrix_user_id,omitempty" db:"matrix_user_id"` IsActive bool `json:"is_active" db:"is_active"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // ClassTeacher assigns teachers to classes (Klassenlehrer) type ClassTeacher struct { ID uuid.UUID `json:"id" db:"id"` ClassID uuid.UUID `json:"class_id" db:"class_id"` TeacherID uuid.UUID `json:"teacher_id" db:"teacher_id"` IsPrimary bool `json:"is_primary" db:"is_primary"` // Hauptklassenlehrer vs. Stellvertreter CreatedAt time.Time `json:"created_at" db:"created_at"` } // TeacherSubject assigns subjects to teachers type TeacherSubject struct { ID uuid.UUID `json:"id" db:"id"` TeacherID uuid.UUID `json:"teacher_id" db:"teacher_id"` SubjectID uuid.UUID `json:"subject_id" db:"subject_id"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // Parent represents a parent/guardian type Parent struct { ID uuid.UUID `json:"id" db:"id"` UserID uuid.UUID `json:"user_id" db:"user_id"` // Linked user account MatrixUserID *string `json:"matrix_user_id,omitempty" db:"matrix_user_id"` FirstName string `json:"first_name" db:"first_name"` LastName string `json:"last_name" db:"last_name"` Phone *string `json:"phone,omitempty" db:"phone"` EmergencyContact bool `json:"emergency_contact" db:"emergency_contact"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // StudentParent links students to their parents type StudentParent struct { ID uuid.UUID `json:"id" db:"id"` StudentID uuid.UUID `json:"student_id" db:"student_id"` ParentID uuid.UUID `json:"parent_id" db:"parent_id"` Relationship string `json:"relationship" db:"relationship"` // 'mother', 'father', 'guardian', 'other' IsPrimary bool `json:"is_primary" db:"is_primary"` // Hauptansprechpartner HasCustody bool `json:"has_custody" db:"has_custody"` // Sorgeberechtigt CreatedAt time.Time `json:"created_at" db:"created_at"` } // ParentRepresentative assigns parent representatives to classes type ParentRepresentative struct { ID uuid.UUID `json:"id" db:"id"` ClassID uuid.UUID `json:"class_id" db:"class_id"` ParentID uuid.UUID `json:"parent_id" db:"parent_id"` Role string `json:"role" db:"role"` // 'first_rep', 'second_rep', 'substitute' ElectedAt time.Time `json:"elected_at" db:"elected_at"` ExpiresAt *time.Time `json:"expires_at,omitempty" db:"expires_at"` IsActive bool `json:"is_active" db:"is_active"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // ======================================== // Stundenplan / Timetable // ======================================== // TimetableSlot represents a time slot in the timetable type TimetableSlot struct { ID uuid.UUID `json:"id" db:"id"` SchoolID uuid.UUID `json:"school_id" db:"school_id"` SlotNumber int `json:"slot_number" db:"slot_number"` // 1, 2, 3... (Stunde) StartTime string `json:"start_time" db:"start_time"` // "08:00" EndTime string `json:"end_time" db:"end_time"` // "08:45" IsBreak bool `json:"is_break" db:"is_break"` // Pause Name *string `json:"name,omitempty" db:"name"` // e.g., "1. Stunde", "Große Pause" } // TimetableEntry represents a single lesson in the timetable type TimetableEntry struct { ID uuid.UUID `json:"id" db:"id"` SchoolYearID uuid.UUID `json:"school_year_id" db:"school_year_id"` ClassID uuid.UUID `json:"class_id" db:"class_id"` SubjectID uuid.UUID `json:"subject_id" db:"subject_id"` TeacherID uuid.UUID `json:"teacher_id" db:"teacher_id"` SlotID uuid.UUID `json:"slot_id" db:"slot_id"` DayOfWeek int `json:"day_of_week" db:"day_of_week"` // 1=Monday, 5=Friday Room *string `json:"room,omitempty" db:"room"` ValidFrom time.Time `json:"valid_from" db:"valid_from"` ValidUntil *time.Time `json:"valid_until,omitempty" db:"valid_until"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // TimetableSubstitution represents a substitution/replacement lesson type TimetableSubstitution struct { ID uuid.UUID `json:"id" db:"id"` OriginalEntryID uuid.UUID `json:"original_entry_id" db:"original_entry_id"` Date time.Time `json:"date" db:"date"` SubstituteTeacherID *uuid.UUID `json:"substitute_teacher_id,omitempty" db:"substitute_teacher_id"` SubstituteSubjectID *uuid.UUID `json:"substitute_subject_id,omitempty" db:"substitute_subject_id"` Room *string `json:"room,omitempty" db:"room"` Type string `json:"type" db:"type"` // 'substitution', 'cancelled', 'room_change', 'supervision' Note *string `json:"note,omitempty" db:"note"` CreatedAt time.Time `json:"created_at" db:"created_at"` CreatedBy uuid.UUID `json:"created_by" db:"created_by"` } // ======================================== // Abwesenheit / Attendance // ======================================== // AttendanceRecord represents a student's attendance for a specific lesson type AttendanceRecord struct { ID uuid.UUID `json:"id" db:"id"` StudentID uuid.UUID `json:"student_id" db:"student_id"` TimetableEntryID *uuid.UUID `json:"timetable_entry_id,omitempty" db:"timetable_entry_id"` Date time.Time `json:"date" db:"date"` SlotID uuid.UUID `json:"slot_id" db:"slot_id"` Status string `json:"status" db:"status"` // AttendanceStatus constants RecordedBy uuid.UUID `json:"recorded_by" db:"recorded_by"` // Teacher who recorded Note *string `json:"note,omitempty" db:"note"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // AbsenceReport represents a full absence report (one or more days) type AbsenceReport struct { ID uuid.UUID `json:"id" db:"id"` StudentID uuid.UUID `json:"student_id" db:"student_id"` StartDate time.Time `json:"start_date" db:"start_date"` EndDate time.Time `json:"end_date" db:"end_date"` Reason *string `json:"reason,omitempty" db:"reason"` ReasonCategory string `json:"reason_category" db:"reason_category"` // 'illness', 'family', 'appointment', 'other' Status string `json:"status" db:"status"` // 'reported', 'confirmed', 'excused', 'unexcused' ReportedBy uuid.UUID `json:"reported_by" db:"reported_by"` // Parent or student ReportedAt time.Time `json:"reported_at" db:"reported_at"` ConfirmedBy *uuid.UUID `json:"confirmed_by,omitempty" db:"confirmed_by"` // Teacher ConfirmedAt *time.Time `json:"confirmed_at,omitempty" db:"confirmed_at"` MedicalCertificate bool `json:"medical_certificate" db:"medical_certificate"` // Attestpflicht CertificateUploaded bool `json:"certificate_uploaded" db:"certificate_uploaded"` MatrixNotificationSent bool `json:"matrix_notification_sent" db:"matrix_notification_sent"` EmailNotificationSent bool `json:"email_notification_sent" db:"email_notification_sent"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // AbsenceNotification tracks notifications sent to parents about absences type AbsenceNotification struct { ID uuid.UUID `json:"id" db:"id"` AttendanceRecordID uuid.UUID `json:"attendance_record_id" db:"attendance_record_id"` ParentID uuid.UUID `json:"parent_id" db:"parent_id"` Channel string `json:"channel" db:"channel"` // 'matrix', 'email', 'push' MessageContent string `json:"message_content" db:"message_content"` SentAt *time.Time `json:"sent_at,omitempty" db:"sent_at"` ReadAt *time.Time `json:"read_at,omitempty" db:"read_at"` ResponseReceived bool `json:"response_received" db:"response_received"` ResponseContent *string `json:"response_content,omitempty" db:"response_content"` ResponseAt *time.Time `json:"response_at,omitempty" db:"response_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // ======================================== // Notenspiegel / Grades // ======================================== // GradeType defines the type of grade const ( GradeTypeExam = "exam" // Klassenarbeit/Klausur GradeTypeTest = "test" // Test/Kurzarbeit GradeTypeOral = "oral" // Mündlich GradeTypeHomework = "homework" // Hausaufgabe GradeTypeProject = "project" // Projekt GradeTypeParticipation = "participation" // Mitarbeit GradeTypeSemester = "semester" // Halbjahres-/Semesternote GradeTypeFinal = "final" // Endnote/Zeugnisnote ) // GradeScale represents the grading scale used type GradeScale struct { ID uuid.UUID `json:"id" db:"id"` SchoolID uuid.UUID `json:"school_id" db:"school_id"` Name string `json:"name" db:"name"` // e.g., "1-6", "Punkte 0-15" MinValue float64 `json:"min_value" db:"min_value"` // e.g., 1 or 0 MaxValue float64 `json:"max_value" db:"max_value"` // e.g., 6 or 15 PassingValue float64 `json:"passing_value" db:"passing_value"` // e.g., 4 or 5 IsAscending bool `json:"is_ascending" db:"is_ascending"` // true: higher=better (Punkte), false: lower=better (Noten) IsDefault bool `json:"is_default" db:"is_default"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // Grade represents a single grade for a student type Grade struct { ID uuid.UUID `json:"id" db:"id"` StudentID uuid.UUID `json:"student_id" db:"student_id"` SubjectID uuid.UUID `json:"subject_id" db:"subject_id"` TeacherID uuid.UUID `json:"teacher_id" db:"teacher_id"` SchoolYearID uuid.UUID `json:"school_year_id" db:"school_year_id"` GradeScaleID uuid.UUID `json:"grade_scale_id" db:"grade_scale_id"` Type string `json:"type" db:"type"` // GradeType constants Value float64 `json:"value" db:"value"` Weight float64 `json:"weight" db:"weight"` // Gewichtung: 1.0, 2.0, 0.5 Date time.Time `json:"date" db:"date"` Title *string `json:"title,omitempty" db:"title"` // e.g., "1. Klassenarbeit" Description *string `json:"description,omitempty" db:"description"` IsVisible bool `json:"is_visible" db:"is_visible"` // Für Eltern/Schüler sichtbar Semester int `json:"semester" db:"semester"` // 1 or 2 CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // GradeComment represents a teacher comment on a student's grade type GradeComment struct { ID uuid.UUID `json:"id" db:"id"` GradeID uuid.UUID `json:"grade_id" db:"grade_id"` TeacherID uuid.UUID `json:"teacher_id" db:"teacher_id"` Comment string `json:"comment" db:"comment"` IsPrivate bool `json:"is_private" db:"is_private"` // Only visible to teachers CreatedAt time.Time `json:"created_at" db:"created_at"` } // ======================================== // Klassenbuch / Class Diary // ======================================== // ClassDiaryEntry represents an entry in the digital class diary type ClassDiaryEntry struct { ID uuid.UUID `json:"id" db:"id"` ClassID uuid.UUID `json:"class_id" db:"class_id"` Date time.Time `json:"date" db:"date"` SlotID uuid.UUID `json:"slot_id" db:"slot_id"` SubjectID uuid.UUID `json:"subject_id" db:"subject_id"` TeacherID uuid.UUID `json:"teacher_id" db:"teacher_id"` Topic *string `json:"topic,omitempty" db:"topic"` // Unterrichtsthema Homework *string `json:"homework,omitempty" db:"homework"` // Hausaufgabe HomeworkDueDate *time.Time `json:"homework_due_date,omitempty" db:"homework_due_date"` Materials *string `json:"materials,omitempty" db:"materials"` // Benötigte Materialien Notes *string `json:"notes,omitempty" db:"notes"` // Besondere Vorkommnisse IsCancelled bool `json:"is_cancelled" db:"is_cancelled"` CancellationReason *string `json:"cancellation_reason,omitempty" db:"cancellation_reason"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // ======================================== // Elterngespräche / Parent Meetings // ======================================== // ParentMeetingSlot represents available time slots for parent meetings type ParentMeetingSlot struct { ID uuid.UUID `json:"id" db:"id"` TeacherID uuid.UUID `json:"teacher_id" db:"teacher_id"` Date time.Time `json:"date" db:"date"` StartTime string `json:"start_time" db:"start_time"` // "14:00" EndTime string `json:"end_time" db:"end_time"` // "14:15" Location *string `json:"location,omitempty" db:"location"` // Room or "Online" IsOnline bool `json:"is_online" db:"is_online"` MeetingLink *string `json:"meeting_link,omitempty" db:"meeting_link"` IsBooked bool `json:"is_booked" db:"is_booked"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // ParentMeeting represents a booked parent-teacher meeting type ParentMeeting struct { ID uuid.UUID `json:"id" db:"id"` SlotID uuid.UUID `json:"slot_id" db:"slot_id"` ParentID uuid.UUID `json:"parent_id" db:"parent_id"` StudentID uuid.UUID `json:"student_id" db:"student_id"` Topic *string `json:"topic,omitempty" db:"topic"` Notes *string `json:"notes,omitempty" db:"notes"` // Teacher notes (private) Status string `json:"status" db:"status"` // 'scheduled', 'completed', 'cancelled', 'no_show' CancelledAt *time.Time `json:"cancelled_at,omitempty" db:"cancelled_at"` CancelledBy *uuid.UUID `json:"cancelled_by,omitempty" db:"cancelled_by"` CancelReason *string `json:"cancel_reason,omitempty" db:"cancel_reason"` CompletedAt *time.Time `json:"completed_at,omitempty" db:"completed_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // ======================================== // Matrix / Communication Integration // ======================================== // MatrixRoom tracks Matrix rooms created for school communication type MatrixRoom struct { ID uuid.UUID `json:"id" db:"id"` SchoolID uuid.UUID `json:"school_id" db:"school_id"` MatrixRoomID string `json:"matrix_room_id" db:"matrix_room_id"` // e.g., "!abc123:breakpilot.local" Type string `json:"type" db:"type"` // 'class_info', 'class_rep', 'student_dm', 'teacher_dm', 'announcement' ClassID *uuid.UUID `json:"class_id,omitempty" db:"class_id"` StudentID *uuid.UUID `json:"student_id,omitempty" db:"student_id"` Name string `json:"name" db:"name"` IsEncrypted bool `json:"is_encrypted" db:"is_encrypted"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // MatrixRoomMember tracks membership in Matrix rooms type MatrixRoomMember struct { ID uuid.UUID `json:"id" db:"id"` MatrixRoomID uuid.UUID `json:"matrix_room_id" db:"matrix_room_id"` // FK to MatrixRoom MatrixUserID string `json:"matrix_user_id" db:"matrix_user_id"` // e.g., "@user:breakpilot.local" UserID *uuid.UUID `json:"user_id,omitempty" db:"user_id"` // FK to User (if known) PowerLevel int `json:"power_level" db:"power_level"` // Matrix power level (0, 50, 100) CanWrite bool `json:"can_write" db:"can_write"` JoinedAt time.Time `json:"joined_at" db:"joined_at"` LeftAt *time.Time `json:"left_at,omitempty" db:"left_at"` } // ParentOnboardingToken for QR-code based parent onboarding type ParentOnboardingToken struct { ID uuid.UUID `json:"id" db:"id"` SchoolID uuid.UUID `json:"school_id" db:"school_id"` ClassID uuid.UUID `json:"class_id" db:"class_id"` StudentID uuid.UUID `json:"student_id" db:"student_id"` Token string `json:"token" db:"token"` // Unique token for QR code Role string `json:"role" db:"role"` // 'parent' or 'parent_representative' ExpiresAt time.Time `json:"expires_at" db:"expires_at"` UsedAt *time.Time `json:"used_at,omitempty" db:"used_at"` UsedByUserID *uuid.UUID `json:"used_by_user_id,omitempty" db:"used_by_user_id"` CreatedAt time.Time `json:"created_at" db:"created_at"` CreatedBy uuid.UUID `json:"created_by" db:"created_by"` // Teacher who created } // ======================================== // Schulverwaltung DTOs // ======================================== // CreateSchoolRequest for creating a new school type CreateSchoolRequest struct { Name string `json:"name" binding:"required"` ShortName *string `json:"short_name"` Type string `json:"type" binding:"required"` Address *string `json:"address"` City *string `json:"city"` PostalCode *string `json:"postal_code"` State *string `json:"state"` Phone *string `json:"phone"` Email *string `json:"email"` Website *string `json:"website"` } // CreateClassRequest for creating a new class type CreateClassRequest struct { SchoolYearID string `json:"school_year_id" binding:"required"` Name string `json:"name" binding:"required"` Grade int `json:"grade" binding:"required"` Section *string `json:"section"` Room *string `json:"room"` } // CreateStudentRequest for creating a new student type CreateStudentRequest struct { ClassID string `json:"class_id" binding:"required"` StudentNumber *string `json:"student_number"` FirstName string `json:"first_name" binding:"required"` LastName string `json:"last_name" binding:"required"` DateOfBirth *string `json:"date_of_birth"` // ISO 8601 Gender *string `json:"gender"` } // RecordAttendanceRequest for recording attendance type RecordAttendanceRequest struct { StudentID string `json:"student_id" binding:"required"` Date string `json:"date" binding:"required"` // ISO 8601 SlotID string `json:"slot_id" binding:"required"` Status string `json:"status" binding:"required"` // AttendanceStatus Note *string `json:"note"` } // ReportAbsenceRequest for parents reporting absence type ReportAbsenceRequest struct { StudentID string `json:"student_id" binding:"required"` StartDate string `json:"start_date" binding:"required"` // ISO 8601 EndDate string `json:"end_date" binding:"required"` // ISO 8601 Reason *string `json:"reason"` ReasonCategory string `json:"reason_category" binding:"required"` } // CreateGradeRequest for creating a grade type CreateGradeRequest struct { StudentID string `json:"student_id" binding:"required"` SubjectID string `json:"subject_id" binding:"required"` SchoolYearID string `json:"school_year_id" binding:"required"` Type string `json:"type" binding:"required"` // GradeType Value float64 `json:"value" binding:"required"` Weight float64 `json:"weight"` Date string `json:"date" binding:"required"` // ISO 8601 Title *string `json:"title"` Description *string `json:"description"` Semester int `json:"semester" binding:"required"` } // StudentGradeOverview provides a summary of all grades for a student in a subject type StudentGradeOverview struct { Student Student `json:"student"` Subject Subject `json:"subject"` Grades []Grade `json:"grades"` Average float64 `json:"average"` OralAverage float64 `json:"oral_average"` ExamAverage float64 `json:"exam_average"` Semester int `json:"semester"` } // ClassAttendanceOverview provides attendance summary for a class type ClassAttendanceOverview struct { Class Class `json:"class"` Date time.Time `json:"date"` TotalStudents int `json:"total_students"` PresentCount int `json:"present_count"` AbsentCount int `json:"absent_count"` LateCount int `json:"late_count"` Records []AttendanceRecord `json:"records"` } // ParentDashboard provides a parent's view of their children's data type ParentDashboard struct { Children []StudentOverview `json:"children"` UnreadMessages int `json:"unread_messages"` UpcomingMeetings []ParentMeeting `json:"upcoming_meetings"` RecentGrades []Grade `json:"recent_grades"` PendingActions []string `json:"pending_actions"` // e.g., "Entschuldigung ausstehend" } // StudentOverview provides summary info about a student type StudentOverview struct { Student Student `json:"student"` Class Class `json:"class"` ClassTeacher *Teacher `json:"class_teacher,omitempty"` AttendanceRate float64 `json:"attendance_rate"` // Percentage UnexcusedAbsences int `json:"unexcused_absences"` GradeAverage float64 `json:"grade_average"` } // TimetableView provides a formatted timetable for display type TimetableView struct { Class Class `json:"class"` Week string `json:"week"` // ISO week: "2025-W01" Days []TimetableDayView `json:"days"` } // TimetableDayView represents a single day in the timetable type TimetableDayView struct { Date time.Time `json:"date"` DayName string `json:"day_name"` // "Montag" Lessons []TimetableLessonView `json:"lessons"` } // TimetableLessonView represents a single lesson in the timetable view type TimetableLessonView struct { Slot TimetableSlot `json:"slot"` Subject *Subject `json:"subject,omitempty"` Teacher *Teacher `json:"teacher,omitempty"` Room *string `json:"room,omitempty"` IsSubstitution bool `json:"is_substitution"` IsCancelled bool `json:"is_cancelled"` Note *string `json:"note,omitempty"` } // ======================================== // Phase 10: DSGVO Betroffenenanfragen (DSR) // Data Subject Request Management // Art. 15, 16, 17, 18, 20 DSGVO // ======================================== // DSRRequestType defines the GDPR article for the request type DSRRequestType string const ( DSRTypeAccess DSRRequestType = "access" // Art. 15 - Auskunftsrecht DSRTypeRectification DSRRequestType = "rectification" // Art. 16 - Berichtigungsrecht DSRTypeErasure DSRRequestType = "erasure" // Art. 17 - Löschungsrecht DSRTypeRestriction DSRRequestType = "restriction" // Art. 18 - Einschränkungsrecht DSRTypePortability DSRRequestType = "portability" // Art. 20 - Datenübertragbarkeit ) // DSRStatus defines the workflow state of a DSR type DSRStatus string const ( DSRStatusIntake DSRStatus = "intake" // Eingegangen DSRStatusIdentityVerification DSRStatus = "identity_verification" // Identitätsprüfung DSRStatusProcessing DSRStatus = "processing" // In Bearbeitung DSRStatusCompleted DSRStatus = "completed" // Abgeschlossen DSRStatusRejected DSRStatus = "rejected" // Abgelehnt DSRStatusCancelled DSRStatus = "cancelled" // Storniert ) // DSRPriority defines the priority level of a DSR type DSRPriority string const ( DSRPriorityNormal DSRPriority = "normal" DSRPriorityExpedited DSRPriority = "expedited" // Art. 16, 17, 18 - beschleunigt DSRPriorityUrgent DSRPriority = "urgent" ) // DSRSource defines where the request came from type DSRSource string const ( DSRSourceAPI DSRSource = "api" // Über API/Self-Service DSRSourceAdminPanel DSRSource = "admin_panel" // Manuell im Admin DSRSourceEmail DSRSource = "email" // Per E-Mail DSRSourcePostal DSRSource = "postal" // Per Post ) // DataSubjectRequest represents a GDPR data subject request type DataSubjectRequest struct { ID uuid.UUID `json:"id" db:"id"` UserID *uuid.UUID `json:"user_id,omitempty" db:"user_id"` RequestNumber string `json:"request_number" db:"request_number"` RequestType DSRRequestType `json:"request_type" db:"request_type"` Status DSRStatus `json:"status" db:"status"` Priority DSRPriority `json:"priority" db:"priority"` Source DSRSource `json:"source" db:"source"` RequesterEmail string `json:"requester_email" db:"requester_email"` RequesterName *string `json:"requester_name,omitempty" db:"requester_name"` RequesterPhone *string `json:"requester_phone,omitempty" db:"requester_phone"` IdentityVerified bool `json:"identity_verified" db:"identity_verified"` IdentityVerifiedAt *time.Time `json:"identity_verified_at,omitempty" db:"identity_verified_at"` IdentityVerifiedBy *uuid.UUID `json:"identity_verified_by,omitempty" db:"identity_verified_by"` IdentityVerificationMethod *string `json:"identity_verification_method,omitempty" db:"identity_verification_method"` RequestDetails map[string]interface{} `json:"request_details" db:"request_details"` DeadlineAt time.Time `json:"deadline_at" db:"deadline_at"` LegalDeadlineDays int `json:"legal_deadline_days" db:"legal_deadline_days"` ExtendedDeadlineAt *time.Time `json:"extended_deadline_at,omitempty" db:"extended_deadline_at"` ExtensionReason *string `json:"extension_reason,omitempty" db:"extension_reason"` AssignedTo *uuid.UUID `json:"assigned_to,omitempty" db:"assigned_to"` ProcessingNotes *string `json:"processing_notes,omitempty" db:"processing_notes"` CompletedAt *time.Time `json:"completed_at,omitempty" db:"completed_at"` CompletedBy *uuid.UUID `json:"completed_by,omitempty" db:"completed_by"` ResultSummary *string `json:"result_summary,omitempty" db:"result_summary"` ResultData map[string]interface{} `json:"result_data,omitempty" db:"result_data"` RejectedAt *time.Time `json:"rejected_at,omitempty" db:"rejected_at"` RejectedBy *uuid.UUID `json:"rejected_by,omitempty" db:"rejected_by"` RejectionReason *string `json:"rejection_reason,omitempty" db:"rejection_reason"` RejectionLegalBasis *string `json:"rejection_legal_basis,omitempty" db:"rejection_legal_basis"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` CreatedBy *uuid.UUID `json:"created_by,omitempty" db:"created_by"` } // DSRStatusHistory tracks status changes for audit trail type DSRStatusHistory struct { ID uuid.UUID `json:"id" db:"id"` RequestID uuid.UUID `json:"request_id" db:"request_id"` FromStatus *DSRStatus `json:"from_status,omitempty" db:"from_status"` ToStatus DSRStatus `json:"to_status" db:"to_status"` ChangedBy *uuid.UUID `json:"changed_by,omitempty" db:"changed_by"` Comment *string `json:"comment,omitempty" db:"comment"` Metadata map[string]interface{} `json:"metadata,omitempty" db:"metadata"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // DSRCommunication tracks all communications related to a DSR type DSRCommunication struct { ID uuid.UUID `json:"id" db:"id"` RequestID uuid.UUID `json:"request_id" db:"request_id"` Direction string `json:"direction" db:"direction"` // 'outbound', 'inbound' Channel string `json:"channel" db:"channel"` // 'email', 'in_app', 'postal' CommunicationType string `json:"communication_type" db:"communication_type"` // Template type used TemplateVersionID *uuid.UUID `json:"template_version_id,omitempty" db:"template_version_id"` Subject *string `json:"subject,omitempty" db:"subject"` BodyHTML *string `json:"body_html,omitempty" db:"body_html"` BodyText *string `json:"body_text,omitempty" db:"body_text"` RecipientEmail *string `json:"recipient_email,omitempty" db:"recipient_email"` SentAt *time.Time `json:"sent_at,omitempty" db:"sent_at"` ErrorMessage *string `json:"error_message,omitempty" db:"error_message"` Attachments []map[string]interface{} `json:"attachments,omitempty" db:"attachments"` CreatedAt time.Time `json:"created_at" db:"created_at"` CreatedBy *uuid.UUID `json:"created_by,omitempty" db:"created_by"` } // DSRTemplate represents a template type for DSR communications type DSRTemplate struct { ID uuid.UUID `json:"id" db:"id"` TemplateType string `json:"template_type" db:"template_type"` Name string `json:"name" db:"name"` Description *string `json:"description,omitempty" db:"description"` RequestTypes []string `json:"request_types" db:"request_types"` // Which DSR types use this template IsActive bool `json:"is_active" db:"is_active"` SortOrder int `json:"sort_order" db:"sort_order"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // DSRTemplateVersion represents a versioned template for DSR communications type DSRTemplateVersion struct { ID uuid.UUID `json:"id" db:"id"` TemplateID uuid.UUID `json:"template_id" db:"template_id"` Version string `json:"version" db:"version"` Language string `json:"language" db:"language"` Subject string `json:"subject" db:"subject"` BodyHTML string `json:"body_html" db:"body_html"` BodyText string `json:"body_text" db:"body_text"` Status string `json:"status" db:"status"` // draft, review, approved, published, archived PublishedAt *time.Time `json:"published_at,omitempty" db:"published_at"` CreatedBy *uuid.UUID `json:"created_by,omitempty" db:"created_by"` ApprovedBy *uuid.UUID `json:"approved_by,omitempty" db:"approved_by"` ApprovedAt *time.Time `json:"approved_at,omitempty" db:"approved_at"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // DSRExceptionCheck tracks Art. 17(3) exception evaluations for erasure requests type DSRExceptionCheck struct { ID uuid.UUID `json:"id" db:"id"` RequestID uuid.UUID `json:"request_id" db:"request_id"` ExceptionType string `json:"exception_type" db:"exception_type"` // Type of exception (Art. 17(3) a-e) Description string `json:"description" db:"description"` Applies *bool `json:"applies,omitempty" db:"applies"` // nil = not checked, true/false = result CheckedBy *uuid.UUID `json:"checked_by,omitempty" db:"checked_by"` CheckedAt *time.Time `json:"checked_at,omitempty" db:"checked_at"` Notes *string `json:"notes,omitempty" db:"notes"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // Art. 17(3) Exception Types const ( DSRExceptionFreedomExpression = "freedom_expression" // Art. 17(3)(a) DSRExceptionLegalObligation = "legal_obligation" // Art. 17(3)(b) DSRExceptionPublicInterest = "public_interest" // Art. 17(3)(c) DSRExceptionPublicHealth = "public_health" // Art. 17(3)(c) DSRExceptionArchiving = "archiving" // Art. 17(3)(d) DSRExceptionLegalClaims = "legal_claims" // Art. 17(3)(e) ) // ======================================== // DSR DTOs (Data Transfer Objects) // ======================================== // CreateDSRRequest for creating a new data subject request type CreateDSRRequest struct { RequestType string `json:"request_type" binding:"required"` RequesterEmail string `json:"requester_email" binding:"required,email"` RequesterName *string `json:"requester_name"` RequesterPhone *string `json:"requester_phone"` Source string `json:"source"` RequestDetails map[string]interface{} `json:"request_details"` Priority string `json:"priority"` } // UpdateDSRRequest for updating a DSR type UpdateDSRRequest struct { Status *string `json:"status"` AssignedTo *string `json:"assigned_to"` // UUID string ProcessingNotes *string `json:"processing_notes"` ExtendDeadline *bool `json:"extend_deadline"` ExtensionReason *string `json:"extension_reason"` RequestDetails map[string]interface{} `json:"request_details"` Priority *string `json:"priority"` } // VerifyDSRIdentityRequest for verifying identity of requester type VerifyDSRIdentityRequest struct { Method string `json:"method" binding:"required"` // email_confirmation, id_document, in_person Comment *string `json:"comment"` } // CompleteDSRRequest for completing a DSR type CompleteDSRRequest struct { ResultSummary string `json:"result_summary" binding:"required"` ResultData map[string]interface{} `json:"result_data"` } // RejectDSRRequest for rejecting a DSR type RejectDSRRequest struct { Reason string `json:"reason" binding:"required"` LegalBasis string `json:"legal_basis" binding:"required"` // Art. 12(5), Art. 17(3)(a-e), etc. } // ExtendDSRDeadlineRequest for extending a DSR deadline type ExtendDSRDeadlineRequest struct { Reason string `json:"reason" binding:"required"` Days int `json:"days"` // Optional: custom extension days } // AssignDSRRequest for assigning a DSR to a handler type AssignDSRRequest struct { AssigneeID string `json:"assignee_id" binding:"required"` Comment *string `json:"comment"` } // SendDSRCommunicationRequest for sending a communication type SendDSRCommunicationRequest struct { CommunicationType string `json:"communication_type" binding:"required"` TemplateVersionID *string `json:"template_version_id"` CustomSubject *string `json:"custom_subject"` CustomBody *string `json:"custom_body"` Variables map[string]string `json:"variables"` } // UpdateDSRExceptionCheckRequest for updating an exception check type UpdateDSRExceptionCheckRequest struct { Applies bool `json:"applies"` Notes *string `json:"notes"` } // DSRListFilters for filtering DSR list type DSRListFilters struct { Status *string `form:"status"` RequestType *string `form:"request_type"` AssignedTo *string `form:"assigned_to"` Priority *string `form:"priority"` OverdueOnly bool `form:"overdue_only"` FromDate *time.Time `form:"from_date"` ToDate *time.Time `form:"to_date"` Search *string `form:"search"` // Search in request number, email, name } // DSRDashboardStats for the admin dashboard type DSRDashboardStats struct { TotalRequests int `json:"total_requests"` PendingRequests int `json:"pending_requests"` OverdueRequests int `json:"overdue_requests"` CompletedThisMonth int `json:"completed_this_month"` AverageProcessingDays float64 `json:"average_processing_days"` ByType map[string]int `json:"by_type"` ByStatus map[string]int `json:"by_status"` UpcomingDeadlines []DataSubjectRequest `json:"upcoming_deadlines"` } // DSRWithDetails combines DSR with related data type DSRWithDetails struct { Request DataSubjectRequest `json:"request"` StatusHistory []DSRStatusHistory `json:"status_history"` Communications []DSRCommunication `json:"communications"` ExceptionChecks []DSRExceptionCheck `json:"exception_checks,omitempty"` AssigneeName *string `json:"assignee_name,omitempty"` CreatorName *string `json:"creator_name,omitempty"` } // DSRTemplateWithVersions combines template with versions type DSRTemplateWithVersions struct { Template DSRTemplate `json:"template"` LatestVersion *DSRTemplateVersion `json:"latest_version,omitempty"` Versions []DSRTemplateVersion `json:"versions,omitempty"` } // CreateDSRTemplateVersionRequest for creating a template version type CreateDSRTemplateVersionRequest struct { TemplateID string `json:"template_id" binding:"required"` Version string `json:"version" binding:"required"` Language string `json:"language" binding:"required"` Subject string `json:"subject" binding:"required"` BodyHTML string `json:"body_html" binding:"required"` BodyText string `json:"body_text" binding:"required"` } // UpdateDSRTemplateVersionRequest for updating a template version type UpdateDSRTemplateVersionRequest struct { Subject *string `json:"subject"` BodyHTML *string `json:"body_html"` BodyText *string `json:"body_text"` Status *string `json:"status"` } // PreviewDSRTemplateRequest for previewing a template with variables type PreviewDSRTemplateRequest struct { Variables map[string]string `json:"variables"` } // DSRTemplatePreviewResponse for template preview type DSRTemplatePreviewResponse struct { Subject string `json:"subject"` BodyHTML string `json:"body_html"` BodyText string `json:"body_text"` } // GetRequestTypeLabel returns German label for request type func (rt DSRRequestType) Label() string { switch rt { case DSRTypeAccess: return "Auskunftsanfrage (Art. 15)" case DSRTypeRectification: return "Berichtigungsanfrage (Art. 16)" case DSRTypeErasure: return "Löschanfrage (Art. 17)" case DSRTypeRestriction: return "Einschränkungsanfrage (Art. 18)" case DSRTypePortability: return "Datenübertragung (Art. 20)" default: return string(rt) } } // GetDeadlineDays returns the legal deadline in days for request type func (rt DSRRequestType) DeadlineDays() int { switch rt { case DSRTypeAccess, DSRTypePortability: return 30 // 1 month case DSRTypeRectification, DSRTypeErasure, DSRTypeRestriction: return 14 // 2 weeks (expedited per BDSG) default: return 30 } } // IsExpedited returns whether this request type should be processed expeditiously func (rt DSRRequestType) IsExpedited() bool { switch rt { case DSRTypeRectification, DSRTypeErasure, DSRTypeRestriction: return true default: return false } } // GetStatusLabel returns German label for status func (s DSRStatus) Label() string { switch s { case DSRStatusIntake: return "Eingang" case DSRStatusIdentityVerification: return "Identitätsprüfung" case DSRStatusProcessing: return "In Bearbeitung" case DSRStatusCompleted: return "Abgeschlossen" case DSRStatusRejected: return "Abgelehnt" case DSRStatusCancelled: return "Storniert" default: return string(s) } } // IsValidDSRRequestType checks if a string is a valid DSR request type func IsValidDSRRequestType(reqType string) bool { switch DSRRequestType(reqType) { case DSRTypeAccess, DSRTypeRectification, DSRTypeErasure, DSRTypeRestriction, DSRTypePortability: return true default: return false } } // IsValidDSRStatus checks if a string is a valid DSR status func IsValidDSRStatus(status string) bool { switch DSRStatus(status) { case DSRStatusIntake, DSRStatusIdentityVerification, DSRStatusProcessing, DSRStatusCompleted, DSRStatusRejected, DSRStatusCancelled: return true default: return false } }