From f00085ba229cce7cbce6740979e0334e1c9bb639 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Mon, 24 Aug 2026 16:38:57 +0200 Subject: [PATCH] feat: add resource entity, repository, and service --- .../java/com/slotline/resource/Resource.java | 112 ++++++++++++++++++ .../slotline/resource/ResourceRepository.java | 25 ++++ .../slotline/resource/ResourceService.java | 68 +++++++++++ 3 files changed, 205 insertions(+) create mode 100644 src/main/java/com/slotline/resource/Resource.java create mode 100644 src/main/java/com/slotline/resource/ResourceRepository.java create mode 100644 src/main/java/com/slotline/resource/ResourceService.java diff --git a/src/main/java/com/slotline/resource/Resource.java b/src/main/java/com/slotline/resource/Resource.java new file mode 100644 index 0000000..9defe47 --- /dev/null +++ b/src/main/java/com/slotline/resource/Resource.java @@ -0,0 +1,112 @@ +package com.slotline.resource; + +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 jakarta.persistence.Version; + +import java.time.Instant; + +@Entity +@Table(name = "resource") +public class Resource { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "organization_id", nullable = false) + private Long organizationId; + + @Column(name = "group_id") + private Long groupId; + + @Column(nullable = false, length = 128) + private String name; + + @Column(length = 512) + private String description; + + @Column(name = "zone_id", nullable = false, length = 64) + private String zoneId; + + @Column(name = "buffer_minutes", nullable = false) + private int bufferMinutes; + + @Column(name = "created_at", nullable = false) + private Instant createdAt; + + @Version + private Long version; + + protected Resource() { + } + + public Resource(Long organizationId, String name, String description, Instant createdAt) { + this.organizationId = organizationId; + this.name = name; + this.description = description; + this.zoneId = "UTC"; + this.bufferMinutes = 0; + this.createdAt = createdAt; + } + + public Long getId() { + return id; + } + + public Long getOrganizationId() { + return organizationId; + } + + public Long getGroupId() { + return groupId; + } + + public void setGroupId(Long groupId) { + this.groupId = groupId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getZoneId() { + return zoneId; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public int getBufferMinutes() { + return bufferMinutes; + } + + public void setBufferMinutes(int bufferMinutes) { + this.bufferMinutes = bufferMinutes; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public Long getVersion() { + return version; + } +} diff --git a/src/main/java/com/slotline/resource/ResourceRepository.java b/src/main/java/com/slotline/resource/ResourceRepository.java new file mode 100644 index 0000000..b0cc80e --- /dev/null +++ b/src/main/java/com/slotline/resource/ResourceRepository.java @@ -0,0 +1,25 @@ +package com.slotline.resource; + +import jakarta.persistence.LockModeType; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Lock; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; +import java.util.Optional; + +public interface ResourceRepository extends JpaRepository { + + List findAllByOrderByCreatedAtDesc(); + + List findByOrganizationIdOrderByCreatedAtDesc(Long organizationId); + + List findByGroupIdOrderByCreatedAtDesc(Long groupId); + + Optional findByIdAndOrganizationId(Long id, Long organizationId); + + @Lock(LockModeType.PESSIMISTIC_WRITE) + @Query("select r from Resource r where r.id = :id") + Optional findByIdForBooking(@Param("id") Long id); +} diff --git a/src/main/java/com/slotline/resource/ResourceService.java b/src/main/java/com/slotline/resource/ResourceService.java new file mode 100644 index 0000000..440c17f --- /dev/null +++ b/src/main/java/com/slotline/resource/ResourceService.java @@ -0,0 +1,68 @@ +package com.slotline.resource; + +import com.slotline.common.NotFoundException; +import com.slotline.resource.dto.ResourceRequest; +import com.slotline.resource.dto.ResourceResponse; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.util.List; + +@Service +public class ResourceService { + + private final ResourceRepository resources; + + public ResourceService(ResourceRepository resources) { + this.resources = resources; + } + + @Transactional + public ResourceResponse create(Long organizationId, ResourceRequest request) { + Resource resource = new Resource(organizationId, request.name(), request.description(), Instant.now()); + applyPolicy(resource, request); + return ResourceResponse.from(resources.save(resource)); + } + + @Transactional(readOnly = true) + public List findAll(Long organizationId) { + return resources.findByOrganizationIdOrderByCreatedAtDesc(organizationId).stream() + .map(ResourceResponse::from) + .toList(); + } + + @Transactional(readOnly = true) + public ResourceResponse findById(Long organizationId, Long id) { + return ResourceResponse.from(require(organizationId, id)); + } + + @Transactional + public ResourceResponse update(Long organizationId, Long id, ResourceRequest request) { + Resource resource = require(organizationId, id); + resource.setName(request.name()); + resource.setDescription(request.description()); + applyPolicy(resource, request); + return ResourceResponse.from(resource); + } + + private void applyPolicy(Resource resource, ResourceRequest request) { + if (request.zoneId() != null && !request.zoneId().isBlank()) { + resource.setZoneId(request.zoneId()); + } + if (request.bufferMinutes() != null) { + resource.setBufferMinutes(request.bufferMinutes()); + } + } + + @Transactional + public void delete(Long organizationId, Long id) { + resources.delete(require(organizationId, id)); + } + + @Transactional(readOnly = true) + public Resource require(Long organizationId, Long id) { + return resources.findByIdAndOrganizationId(id, organizationId) + .orElseThrow(() -> new NotFoundException("Resource " + id + " not found")); + } +}