Files
breakpilot-core/consent-service/internal/models/dsr.go
Benjamin Admin 92c86ec6ba [split-required] [guardrail-change] Enforce 500 LOC budget across all services
Install LOC guardrails (check-loc.sh, architecture.md, pre-commit hook)
and split all 44 files exceeding 500 LOC into domain-focused modules:

- consent-service (Go): models, handlers, services, database splits
- backend-core (Python): security_api, rbac_api, pdf_service, auth splits
- admin-core (TypeScript): 5 page.tsx + sidebar extractions
- pitch-deck (TypeScript): 6 slides, 3 UI components, engine.ts splits
- voice-service (Python): enhanced_task_orchestrator split

Result: 0 violations, 36 exempted (pipeline, tests, pure-data files).
Go build verified clean. No behavior changes — pure structural splits.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 00:09:30 +02:00

404 lines
18 KiB
Go

package models
import (
"time"
"github.com/google/uuid"
)
// ========================================
// 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
)
// 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)
)
// 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"`
Channel string `json:"channel" db:"channel"`
CommunicationType string `json:"communication_type" db:"communication_type"`
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"`
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"`
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"`
Description string `json:"description" db:"description"`
Applies *bool `json:"applies,omitempty" db:"applies"`
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"`
}
// ========================================
// DSR DTOs
// ========================================
// 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"`
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"`
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"`
}
// ExtendDSRDeadlineRequest for extending a DSR deadline
type ExtendDSRDeadlineRequest struct {
Reason string `json:"reason" binding:"required"`
Days int `json:"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"`
}
// 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"`
}
// ========================================
// DSR Helper Methods
// ========================================
// Label 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)
}
}
// DeadlineDays 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
}
}
// Label 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
}
}