From 7b3bdd4075834c1e38a096a29fb427362d898bb0 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Tue, 25 Aug 2026 14:21:08 +0200 Subject: [PATCH] feat: add organization entity, repository, and service --- .../slotline/organization/Organization.java | 53 +++++++++++++++++++ .../organization/OrganizationRepository.java | 15 ++++++ .../organization/OrganizationService.java | 51 ++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 src/main/java/com/slotline/organization/Organization.java create mode 100644 src/main/java/com/slotline/organization/OrganizationRepository.java create mode 100644 src/main/java/com/slotline/organization/OrganizationService.java diff --git a/src/main/java/com/slotline/organization/Organization.java b/src/main/java/com/slotline/organization/Organization.java new file mode 100644 index 0000000..2047b42 --- /dev/null +++ b/src/main/java/com/slotline/organization/Organization.java @@ -0,0 +1,53 @@ +package com.slotline.organization; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +import java.time.Instant; + +@Entity +@Table(name = "organization") +public class Organization { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, length = 128) + private String name; + + @Column(nullable = false, unique = true, length = 64) + private String slug; + + @Column(name = "created_at", nullable = false) + private Instant createdAt; + + protected Organization() { + } + + public Organization(String name, String slug, Instant createdAt) { + this.name = name; + this.slug = slug; + this.createdAt = createdAt; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getSlug() { + return slug; + } + + public Instant getCreatedAt() { + return createdAt; + } +} diff --git a/src/main/java/com/slotline/organization/OrganizationRepository.java b/src/main/java/com/slotline/organization/OrganizationRepository.java new file mode 100644 index 0000000..f7e1811 --- /dev/null +++ b/src/main/java/com/slotline/organization/OrganizationRepository.java @@ -0,0 +1,15 @@ +package com.slotline.organization; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; +import java.util.Optional; + +public interface OrganizationRepository extends JpaRepository { + + Optional findBySlug(String slug); + + boolean existsBySlug(String slug); + + List findAllByOrderByCreatedAtAsc(); +} diff --git a/src/main/java/com/slotline/organization/OrganizationService.java b/src/main/java/com/slotline/organization/OrganizationService.java new file mode 100644 index 0000000..1554427 --- /dev/null +++ b/src/main/java/com/slotline/organization/OrganizationService.java @@ -0,0 +1,51 @@ +package com.slotline.organization; + +import com.slotline.common.NotFoundException; +import com.slotline.common.SlugTakenException; +import com.slotline.organization.dto.OrganizationRequest; +import com.slotline.organization.dto.OrganizationResponse; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.util.List; + +@Service +public class OrganizationService { + + public static final String defaultSlug = "default"; + + private final OrganizationRepository organizations; + + public OrganizationService(OrganizationRepository organizations) { + this.organizations = organizations; + } + + @Transactional + public OrganizationResponse create(OrganizationRequest request) { + if (organizations.existsBySlug(request.slug())) { + throw new SlugTakenException(request.slug()); + } + Organization organization = new Organization(request.name(), request.slug(), Instant.now()); + return OrganizationResponse.from(organizations.save(organization)); + } + + @Transactional(readOnly = true) + public List findAll() { + return organizations.findAllByOrderByCreatedAtAsc().stream() + .map(OrganizationResponse::from) + .toList(); + } + + @Transactional(readOnly = true) + public Organization require(Long id) { + return organizations.findById(id) + .orElseThrow(() -> new NotFoundException("Organization " + id + " not found")); + } + + @Transactional(readOnly = true) + public Organization requireBySlug(String slug) { + return organizations.findBySlug(slug) + .orElseThrow(() -> new NotFoundException("Organization '" + slug + "' not found")); + } +}