package at.procon.ted.model.entity; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.time.LocalDateTime; import java.util.UUID; /** * Entity for tracking processed mail attachments. * Uses content hash for idempotent processing to avoid duplicate handling. * * @author Martin.Schweitzer@procon.co.at and claude.ai */ @Entity @Table(name = "processed_attachment", schema = "ted", indexes = { @Index(name = "idx_processed_attachment_hash", columnList = "content_hash", unique = true), @Index(name = "idx_processed_attachment_status", columnList = "processing_status"), @Index(name = "idx_processed_attachment_type", columnList = "file_type") }) @Data @Builder @NoArgsConstructor @AllArgsConstructor public class ProcessedAttachment { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; /** * SHA-256 hash of the attachment content for idempotent processing. */ @Column(name = "content_hash", nullable = false, unique = true, length = 64) private String contentHash; /** * Original filename of the attachment. */ @Column(name = "original_filename", nullable = false, length = 500) private String originalFilename; /** * Detected or declared file type (e.g., PDF, ZIP, XML). */ @Column(name = "file_type", length = 50) private String fileType; /** * MIME content type. */ @Column(name = "content_type", length = 255) private String contentType; /** * File size in bytes. */ @Column(name = "file_size") private Long fileSize; /** * Processing status of the attachment. */ @Enumerated(EnumType.STRING) @Column(name = "processing_status", nullable = false, length = 20) private ProcessingStatus processingStatus; /** * Extracted text content (for PDF, etc.). */ @Column(name = "extracted_text", columnDefinition = "TEXT") private String extractedText; /** * Path where the attachment was saved. */ @Column(name = "saved_path", length = 1000) private String savedPath; /** * Email subject from which the attachment was extracted. */ @Column(name = "mail_subject", length = 500) private String mailSubject; /** * Email sender. */ @Column(name = "mail_from", length = 500) private String mailFrom; /** * Parent attachment hash (for files extracted from ZIP). */ @Column(name = "parent_hash", length = 64) private String parentHash; /** * Error message if processing failed. */ @Column(name = "error_message", columnDefinition = "TEXT") private String errorMessage; /** * Number of child attachments (for ZIP files). */ @Column(name = "child_count") private Integer childCount; /** * When the attachment was first received. */ @Column(name = "received_at", nullable = false) private LocalDateTime receivedAt; /** * When processing was completed. */ @Column(name = "processed_at") private LocalDateTime processedAt; /** * Processing status enum. */ public enum ProcessingStatus { PENDING, PROCESSING, COMPLETED, FAILED, DUPLICATE } @PrePersist protected void onCreate() { if (receivedAt == null) { receivedAt = LocalDateTime.now(); } if (processingStatus == null) { processingStatus = ProcessingStatus.PENDING; } } }