ProjectConflict.java

package com.taxonomy.portfolio.model;

import com.taxonomy.portfolio.model.PortfolioTypes.ConflictStatus;
import com.taxonomy.portfolio.model.PortfolioTypes.ConflictType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import jakarta.persistence.Version;

import java.time.Instant;

/** Evidence-backed, human-reviewable conflict hypothesis between requirements. */
@Entity
@Table(name = "project_conflict", indexes = {
        @Index(name = "idx_conf_project", columnList = "project_id,status"),
        @Index(name = "idx_conf_req_a", columnList = "requirement_a_id"),
        @Index(name = "idx_conf_req_b", columnList = "requirement_b_id")
}, uniqueConstraints = {
        @UniqueConstraint(name = "uq_conf_signature",
                columnNames = {"project_id", "requirement_a_id", "requirement_b_id", "conflict_type", "fingerprint"})
})
public class ProjectConflict {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "project_id", nullable = false)
    private ArchitectureProject project;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "requirement_a_id", nullable = false)
    private ProjectRequirement requirementA;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "requirement_b_id", nullable = false)
    private ProjectRequirement requirementB;

    @Enumerated(EnumType.STRING)
    @Column(name = "conflict_type", nullable = false, length = 32)
    private ConflictType conflictType;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 32)
    private ConflictStatus status = ConflictStatus.PROPOSED;

    @Column(nullable = false, length = 64)
    private String fingerprint;

    @Column(nullable = false, length = 500)
    private String title;

    @Lob
    @Column(nullable = false)
    private String evidence;

    @Column(nullable = false)
    private double confidence;

    @Column(name = "resolution_note", length = 4000)
    private String resolutionNote;

    @Column(name = "detected_at", nullable = false)
    private Instant detectedAt;

    @Column(name = "reviewed_by", length = 160)
    private String reviewedBy;

    @Column(name = "reviewed_at")
    private Instant reviewedAt;

    @Version
    @Column(name = "row_version", nullable = false)
    private long rowVersion;

    protected ProjectConflict() {
    }

    public ProjectConflict(ArchitectureProject project,
                           ProjectRequirement requirementA,
                           ProjectRequirement requirementB,
                           ConflictType conflictType,
                           String fingerprint,
                           String title,
                           String evidence,
                           double confidence,
                           Instant detectedAt) {
        this.project = project;
        this.requirementA = requirementA;
        this.requirementB = requirementB;
        this.conflictType = conflictType;
        this.fingerprint = fingerprint;
        this.title = title;
        this.evidence = evidence;
        this.confidence = confidence;
        this.detectedAt = detectedAt;
    }

    public void review(ConflictStatus status,
                       String resolutionNote,
                       String reviewedBy,
                       Instant reviewedAt) {
        if (status != null) this.status = status;
        this.resolutionNote = resolutionNote;
        this.reviewedBy = reviewedBy;
        this.reviewedAt = reviewedAt;
    }

    public Long getId() { return id; }
    public ArchitectureProject getProject() { return project; }
    public ProjectRequirement getRequirementA() { return requirementA; }
    public ProjectRequirement getRequirementB() { return requirementB; }
    public ConflictType getConflictType() { return conflictType; }
    public ConflictStatus getStatus() { return status; }
    public String getFingerprint() { return fingerprint; }
    public String getTitle() { return title; }
    public String getEvidence() { return evidence; }
    public double getConfidence() { return confidence; }
    public String getResolutionNote() { return resolutionNote; }
    public Instant getDetectedAt() { return detectedAt; }
    public String getReviewedBy() { return reviewedBy; }
    public Instant getReviewedAt() { return reviewedAt; }
    public long getRowVersion() { return rowVersion; }
}