PartnerCommandService.java

package de.mirkosertic.powerstaff.partner.command;

import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@Service
@Transactional
public class PartnerCommandService {

    private final PartnerRepository partnerRepository;
    private final PartnerContactRepository contactRepository;
    private final PartnerHistoryRepository historyRepository;
    private final JdbcClient jdbcClient;

    public PartnerCommandService(final PartnerRepository partnerRepository,
                                 final PartnerContactRepository contactRepository,
                                 final PartnerHistoryRepository historyRepository,
                                 final JdbcClient jdbcClient) {
        this.partnerRepository = partnerRepository;
        this.contactRepository = contactRepository;
        this.historyRepository = historyRepository;
        this.jdbcClient = jdbcClient;
    }

    /**
     * Speichert nur die Partner-Stammdaten (ohne Kontakte/Historie).
     */
    public Partner save(final Partner partner) {
        return partnerRepository.save(partner);
    }

    /**
     * Speichert Partner-Stammdaten und verarbeitet Kontakt- und Historien-Delta-Commands.
     * Nur Einträge mit op="ADD" oder op="DELETE" werden verarbeitet;
     * unveränderte Einträge erhalten keinen neuen Audit-Timestamp.
     */
    public Partner save(final Partner partner,
                        final List<PartnerContactEntry> contactChanges,
                        final List<PartnerHistoryEntry> historyChanges) {
        final Partner saved = partnerRepository.save(partner);
        final long partnerId = saved.getId();

        // Kontakt-Delta verarbeiten
        for (final PartnerContactEntry cmd : contactChanges) {
            if ("ADD".equals(cmd.op())) {
                final PartnerContact contact = new PartnerContact();
                contact.setType(cmd.type());
                contact.setValue(cmd.value());
                contact.setPartnerId(partnerId);
                contactRepository.save(contact);
            } else if ("DELETE".equals(cmd.op())) {
                if (cmd.id() == null) {
                    throw new IllegalArgumentException("DELETE Kontakt erfordert eine ID");
                }
                contactRepository.deleteById(cmd.id());
            }
        }

        // Historie-Delta verarbeiten
        for (final PartnerHistoryEntry cmd : historyChanges) {
            if ("ADD".equals(cmd.op())) {
                final PartnerHistory history = new PartnerHistory();
                history.setDescription(cmd.description());
                history.setTypeId(cmd.typeId());
                history.setPartnerId(partnerId);
                historyRepository.save(history);
            } else if ("UPDATE".equals(cmd.op())) {
                if (cmd.id() == null) {
                    throw new IllegalArgumentException("UPDATE Kontakthistorie erfordert eine ID");
                }
                historyRepository.findById(cmd.id()).ifPresent(history -> {
                    history.setDescription(cmd.description());
                    history.setTypeId(cmd.typeId());
                    historyRepository.save(history);
                });
            } else if ("DELETE".equals(cmd.op())) {
                if (cmd.id() == null) {
                    throw new IllegalArgumentException("DELETE Kontakthistorie erfordert eine ID");
                }
                historyRepository.deleteById(cmd.id());
            }
        }

        return saved;
    }

    @Transactional(readOnly = true)
    public Optional<Partner> findById(final long id) {
        return partnerRepository.findById(id);
    }

    /**
     * Löscht einen Partner. Wirft {@link PartnerHasProjectsException} wenn Projekte
     * auf diesen Partner verweisen (RESTRICT-Check vor dem eigentlichen Delete).
     */
    public void deleteById(final long id) {
        final List<Long> linkedProjectIds = jdbcClient
                .sql("SELECT id FROM project WHERE partner_id = :partnerId")
                .param("partnerId", id)
                .query(Long.class)
                .list();

        if (!linkedProjectIds.isEmpty()) {
            throw new PartnerHasProjectsException(linkedProjectIds);
        }

        partnerRepository.deleteById(id);
    }
}