autocomit
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
package com.ldpv2.controller;
|
||||
|
||||
import com.ldpv2.dto.request.CreateContactRequest;
|
||||
import com.ldpv2.dto.response.ContactResponse;
|
||||
import com.ldpv2.service.ContactService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/contacts")
|
||||
@Tag(name = "Contacts", description = "Contact management endpoints")
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class ContactController {
|
||||
|
||||
@Autowired
|
||||
private ContactService contactService;
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "Create contact", description = "Create a new contact")
|
||||
public ResponseEntity<ContactResponse> create(@Valid @RequestBody CreateContactRequest request) {
|
||||
ContactResponse response = contactService.create(request);
|
||||
return new ResponseEntity<>(response, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "Get contact", description = "Get contact by ID with persons")
|
||||
public ResponseEntity<ContactResponse> getById(@PathVariable UUID id) {
|
||||
ContactResponse response = contactService.findById(id);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "List contacts", description = "Get all contacts")
|
||||
public ResponseEntity<List<ContactResponse>> getAll() {
|
||||
List<ContactResponse> response = contactService.findAll();
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@PostMapping("/{contactId}/persons/{personId}")
|
||||
@Operation(summary = "Add person to contact", description = "Add a person to a contact")
|
||||
public ResponseEntity<ContactResponse> addPerson(
|
||||
@PathVariable UUID contactId,
|
||||
@PathVariable UUID personId,
|
||||
@RequestParam(defaultValue = "false") boolean isPrimary) {
|
||||
ContactResponse response = contactService.addPerson(contactId, personId, isPrimary);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{contactId}/persons/{personId}")
|
||||
@Operation(summary = "Remove person from contact", description = "Remove a person from a contact")
|
||||
public ResponseEntity<ContactResponse> removePerson(
|
||||
@PathVariable UUID contactId,
|
||||
@PathVariable UUID personId) {
|
||||
ContactResponse response = contactService.removePerson(contactId, personId);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@PatchMapping("/{contactId}/persons/{personId}/primary")
|
||||
@Operation(summary = "Set primary person", description = "Set a person as primary contact")
|
||||
public ResponseEntity<ContactResponse> setPrimary(
|
||||
@PathVariable UUID contactId,
|
||||
@PathVariable UUID personId) {
|
||||
ContactResponse response = contactService.setPrimary(contactId, personId);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "Delete contact", description = "Delete a contact")
|
||||
public ResponseEntity<Void> delete(@PathVariable UUID id) {
|
||||
contactService.delete(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.ldpv2.controller;
|
||||
|
||||
import com.ldpv2.dto.request.CreateContactRoleRequest;
|
||||
import com.ldpv2.dto.response.ContactRoleResponse;
|
||||
import com.ldpv2.service.ContactRoleService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/contact-roles")
|
||||
@Tag(name = "Contact Roles", description = "Contact role management endpoints")
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class ContactRoleController {
|
||||
|
||||
@Autowired
|
||||
private ContactRoleService contactRoleService;
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@Operation(summary = "Create contact role", description = "Create a new contact role (Admin only)")
|
||||
public ResponseEntity<ContactRoleResponse> create(@Valid @RequestBody CreateContactRoleRequest request) {
|
||||
ContactRoleResponse response = contactRoleService.create(request);
|
||||
return new ResponseEntity<>(response, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "List contact roles", description = "Get all contact roles")
|
||||
public ResponseEntity<List<ContactRoleResponse>> getAll() {
|
||||
List<ContactRoleResponse> response = contactRoleService.findAll();
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.ldpv2.controller;
|
||||
|
||||
import com.ldpv2.dto.request.CreatePersonRequest;
|
||||
import com.ldpv2.dto.request.UpdatePersonRequest;
|
||||
import com.ldpv2.dto.response.PersonResponse;
|
||||
import com.ldpv2.service.PersonService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/persons")
|
||||
@Tag(name = "Persons", description = "Person management endpoints")
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class PersonController {
|
||||
|
||||
@Autowired
|
||||
private PersonService personService;
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "Create person", description = "Create a new person")
|
||||
public ResponseEntity<PersonResponse> create(@Valid @RequestBody CreatePersonRequest request) {
|
||||
PersonResponse response = personService.create(request);
|
||||
return new ResponseEntity<>(response, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "Update person", description = "Update an existing person")
|
||||
public ResponseEntity<PersonResponse> update(
|
||||
@PathVariable UUID id,
|
||||
@Valid @RequestBody UpdatePersonRequest request) {
|
||||
PersonResponse response = personService.update(id, request);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "Get person", description = "Get person by ID")
|
||||
public ResponseEntity<PersonResponse> getById(@PathVariable UUID id) {
|
||||
PersonResponse response = personService.findById(id);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "List persons", description = "Get paginated list of persons")
|
||||
public ResponseEntity<Page<PersonResponse>> getAll(
|
||||
@RequestParam(required = false) String name,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "20") int size,
|
||||
@RequestParam(defaultValue = "lastName") String sortBy,
|
||||
@RequestParam(defaultValue = "asc") String sortDirection) {
|
||||
|
||||
Sort sort = sortDirection.equalsIgnoreCase("desc")
|
||||
? Sort.by(sortBy).descending()
|
||||
: Sort.by(sortBy).ascending();
|
||||
|
||||
Pageable pageable = PageRequest.of(page, size, sort);
|
||||
|
||||
Page<PersonResponse> response = (name != null && !name.trim().isEmpty())
|
||||
? personService.search(name, pageable)
|
||||
: personService.findAll(pageable);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "Delete person", description = "Delete a person")
|
||||
public ResponseEntity<Void> delete(@PathVariable UUID id) {
|
||||
personService.delete(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.ldpv2.domain.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Contact entity representing functional roles with associated persons
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "contact")
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true, exclude = {"contactPersons"})
|
||||
public class Contact extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "contact_role_id", nullable = false)
|
||||
private ContactRole contactRole;
|
||||
|
||||
@OneToMany(mappedBy = "contact", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private Set<ContactPerson> contactPersons = new HashSet<>();
|
||||
|
||||
public void addPerson(Person person, boolean isPrimary) {
|
||||
ContactPerson contactPerson = new ContactPerson();
|
||||
contactPerson.setContact(this);
|
||||
contactPerson.setPerson(person);
|
||||
contactPerson.setPrimary(isPrimary);
|
||||
contactPersons.add(contactPerson);
|
||||
}
|
||||
|
||||
public void removePerson(Person person) {
|
||||
contactPersons.removeIf(cp -> cp.getPerson().equals(person));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.ldpv2.domain.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Junction entity for Contact-Person many-to-many relationship
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "contact_person")
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ContactPerson implements Serializable {
|
||||
|
||||
@EmbeddedId
|
||||
private ContactPersonId id = new ContactPersonId();
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@MapsId("contactId")
|
||||
@JoinColumn(name = "contact_id")
|
||||
private Contact contact;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@MapsId("personId")
|
||||
@JoinColumn(name = "person_id")
|
||||
private Person person;
|
||||
|
||||
@Column(name = "is_primary", nullable = false)
|
||||
private boolean isPrimary = false;
|
||||
|
||||
@Embeddable
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class ContactPersonId implements Serializable {
|
||||
@Column(name = "contact_id")
|
||||
private java.util.UUID contactId;
|
||||
|
||||
@Column(name = "person_id")
|
||||
private java.util.UUID personId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ldpv2.domain.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Contact Role entity representing functional roles
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "contact_role")
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ContactRole extends BaseEntity {
|
||||
|
||||
@Column(name = "role_name", nullable = false, unique = true, length = 100)
|
||||
private String roleName;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.ldpv2.domain.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Person entity representing individuals
|
||||
*/
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name = "person")
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Person extends BaseEntity {
|
||||
|
||||
@Column(name = "first_name", nullable = false, length = 100)
|
||||
private String firstName;
|
||||
|
||||
@Column(name = "last_name", nullable = false, length = 100)
|
||||
private String lastName;
|
||||
|
||||
@Column(nullable = false, unique = true, length = 255)
|
||||
private String email;
|
||||
|
||||
@Column(length = 50)
|
||||
private String phone;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ldpv2.dto.request;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CreateContactRequest {
|
||||
|
||||
@NotNull(message = "Contact role is required")
|
||||
private UUID contactRoleId;
|
||||
|
||||
@NotEmpty(message = "At least one person is required")
|
||||
private List<UUID> personIds;
|
||||
|
||||
@NotNull(message = "Primary person must be specified")
|
||||
private UUID primaryPersonId;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.ldpv2.dto.request;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CreateContactRoleRequest {
|
||||
|
||||
@NotBlank(message = "Role name is required")
|
||||
@Size(max = 100, message = "Role name must not exceed 100 characters")
|
||||
private String roleName;
|
||||
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.ldpv2.dto.request;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CreatePersonRequest {
|
||||
|
||||
@NotBlank(message = "First name is required")
|
||||
@Size(max = 100, message = "First name must not exceed 100 characters")
|
||||
private String firstName;
|
||||
|
||||
@NotBlank(message = "Last name is required")
|
||||
@Size(max = 100, message = "Last name must not exceed 100 characters")
|
||||
private String lastName;
|
||||
|
||||
@NotBlank(message = "Email is required")
|
||||
@Email(message = "Email must be valid")
|
||||
private String email;
|
||||
|
||||
@Size(max = 50, message = "Phone must not exceed 50 characters")
|
||||
private String phone;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ldpv2.dto.request;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UpdatePersonRequest {
|
||||
|
||||
@Size(max = 100, message = "First name must not exceed 100 characters")
|
||||
private String firstName;
|
||||
|
||||
@Size(max = 100, message = "Last name must not exceed 100 characters")
|
||||
private String lastName;
|
||||
|
||||
@Email(message = "Email must be valid")
|
||||
private String email;
|
||||
|
||||
@Size(max = 50, message = "Phone must not exceed 50 characters")
|
||||
private String phone;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ldpv2.dto.response;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ContactResponse {
|
||||
private UUID id;
|
||||
private ContactRoleResponse contactRole;
|
||||
private List<PersonInContactResponse> persons;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.ldpv2.dto.response;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ContactRoleResponse {
|
||||
private UUID id;
|
||||
private String roleName;
|
||||
private String description;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ldpv2.dto.response;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PersonInContactResponse {
|
||||
private PersonResponse person;
|
||||
private boolean isPrimary;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.ldpv2.dto.response;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PersonResponse {
|
||||
private UUID id;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String email;
|
||||
private String phone;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.ldpv2.repository;
|
||||
|
||||
import com.ldpv2.domain.entity.Contact;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface ContactRepository extends JpaRepository<Contact, UUID> {
|
||||
|
||||
@Query("SELECT c FROM Contact c JOIN FETCH c.contactRole LEFT JOIN FETCH c.contactPersons cp LEFT JOIN FETCH cp.person")
|
||||
List<Contact> findAllWithDetails();
|
||||
|
||||
@Query("SELECT c FROM Contact c JOIN FETCH c.contactRole LEFT JOIN FETCH c.contactPersons cp LEFT JOIN FETCH cp.person WHERE c.id = :id")
|
||||
Contact findByIdWithDetails(UUID id);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.ldpv2.repository;
|
||||
|
||||
import com.ldpv2.domain.entity.ContactRole;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface ContactRoleRepository extends JpaRepository<ContactRole, UUID> {
|
||||
Optional<ContactRole> findByRoleName(String roleName);
|
||||
boolean existsByRoleName(String roleName);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.ldpv2.repository;
|
||||
|
||||
import com.ldpv2.domain.entity.Person;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface PersonRepository extends JpaRepository<Person, UUID> {
|
||||
Optional<Person> findByEmail(String email);
|
||||
boolean existsByEmail(String email);
|
||||
|
||||
@Query("SELECT p FROM Person p WHERE " +
|
||||
"LOWER(p.firstName) LIKE LOWER(CONCAT('%', :name, '%')) OR " +
|
||||
"LOWER(p.lastName) LIKE LOWER(CONCAT('%', :name, '%'))")
|
||||
Page<Person> findByName(@Param("name") String name, Pageable pageable);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.ldpv2.service;
|
||||
|
||||
import com.ldpv2.domain.entity.ContactRole;
|
||||
import com.ldpv2.dto.request.CreateContactRoleRequest;
|
||||
import com.ldpv2.dto.response.ContactRoleResponse;
|
||||
import com.ldpv2.exception.BadRequestException;
|
||||
import com.ldpv2.repository.ContactRoleRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class ContactRoleService {
|
||||
|
||||
@Autowired
|
||||
private ContactRoleRepository contactRoleRepository;
|
||||
|
||||
@Transactional
|
||||
public ContactRoleResponse create(CreateContactRoleRequest request) {
|
||||
if (contactRoleRepository.existsByRoleName(request.getRoleName())) {
|
||||
throw new BadRequestException("Contact role with name '" + request.getRoleName() + "' already exists");
|
||||
}
|
||||
|
||||
ContactRole role = new ContactRole();
|
||||
role.setRoleName(request.getRoleName());
|
||||
role.setDescription(request.getDescription());
|
||||
|
||||
role = contactRoleRepository.save(role);
|
||||
return mapToResponse(role);
|
||||
}
|
||||
|
||||
public List<ContactRoleResponse> findAll() {
|
||||
return contactRoleRepository.findAll().stream()
|
||||
.map(this::mapToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private ContactRoleResponse mapToResponse(ContactRole role) {
|
||||
return new ContactRoleResponse(
|
||||
role.getId(),
|
||||
role.getRoleName(),
|
||||
role.getDescription(),
|
||||
role.getCreatedAt(),
|
||||
role.getUpdatedAt()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package com.ldpv2.service;
|
||||
|
||||
import com.ldpv2.domain.entity.Contact;
|
||||
import com.ldpv2.domain.entity.ContactPerson;
|
||||
import com.ldpv2.domain.entity.ContactRole;
|
||||
import com.ldpv2.domain.entity.Person;
|
||||
import com.ldpv2.dto.request.CreateContactRequest;
|
||||
import com.ldpv2.dto.response.ContactResponse;
|
||||
import com.ldpv2.dto.response.ContactRoleResponse;
|
||||
import com.ldpv2.dto.response.PersonInContactResponse;
|
||||
import com.ldpv2.dto.response.PersonResponse;
|
||||
import com.ldpv2.exception.BadRequestException;
|
||||
import com.ldpv2.exception.ResourceNotFoundException;
|
||||
import com.ldpv2.repository.ContactRepository;
|
||||
import com.ldpv2.repository.ContactRoleRepository;
|
||||
import com.ldpv2.repository.PersonRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class ContactService {
|
||||
|
||||
@Autowired
|
||||
private ContactRepository contactRepository;
|
||||
|
||||
@Autowired
|
||||
private ContactRoleRepository contactRoleRepository;
|
||||
|
||||
@Autowired
|
||||
private PersonRepository personRepository;
|
||||
|
||||
@Transactional
|
||||
public ContactResponse create(CreateContactRequest request) {
|
||||
ContactRole role = contactRoleRepository.findById(request.getContactRoleId())
|
||||
.orElseThrow(() -> new ResourceNotFoundException(
|
||||
"Contact role not found with id: " + request.getContactRoleId()));
|
||||
|
||||
if (!request.getPersonIds().contains(request.getPrimaryPersonId())) {
|
||||
throw new BadRequestException("Primary person must be in the list of persons");
|
||||
}
|
||||
|
||||
Contact contact = new Contact();
|
||||
contact.setContactRole(role);
|
||||
|
||||
for (UUID personId : request.getPersonIds()) {
|
||||
Person person = personRepository.findById(personId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Person not found with id: " + personId));
|
||||
boolean isPrimary = personId.equals(request.getPrimaryPersonId());
|
||||
contact.addPerson(person, isPrimary);
|
||||
}
|
||||
|
||||
contact = contactRepository.save(contact);
|
||||
return mapToResponse(contact);
|
||||
}
|
||||
|
||||
public ContactResponse findById(UUID id) {
|
||||
Contact contact = contactRepository.findByIdWithDetails(id);
|
||||
if (contact == null) {
|
||||
throw new ResourceNotFoundException("Contact not found with id: " + id);
|
||||
}
|
||||
return mapToResponse(contact);
|
||||
}
|
||||
|
||||
public List<ContactResponse> findAll() {
|
||||
return contactRepository.findAllWithDetails().stream()
|
||||
.map(this::mapToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ContactResponse addPerson(UUID contactId, UUID personId, boolean isPrimary) {
|
||||
Contact contact = contactRepository.findById(contactId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Contact not found with id: " + contactId));
|
||||
|
||||
Person person = personRepository.findById(personId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Person not found with id: " + personId));
|
||||
|
||||
contact.addPerson(person, isPrimary);
|
||||
contact = contactRepository.save(contact);
|
||||
return mapToResponse(contact);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ContactResponse removePerson(UUID contactId, UUID personId) {
|
||||
Contact contact = contactRepository.findById(contactId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Contact not found with id: " + contactId));
|
||||
|
||||
Person person = personRepository.findById(personId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Person not found with id: " + personId));
|
||||
|
||||
contact.removePerson(person);
|
||||
contact = contactRepository.save(contact);
|
||||
return mapToResponse(contact);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ContactResponse setPrimary(UUID contactId, UUID personId) {
|
||||
Contact contact = contactRepository.findByIdWithDetails(contactId);
|
||||
if (contact == null) {
|
||||
throw new ResourceNotFoundException("Contact not found with id: " + contactId);
|
||||
}
|
||||
|
||||
boolean personFound = false;
|
||||
for (ContactPerson cp : contact.getContactPersons()) {
|
||||
if (cp.getPerson().getId().equals(personId)) {
|
||||
cp.setPrimary(true);
|
||||
personFound = true;
|
||||
} else {
|
||||
cp.setPrimary(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!personFound) {
|
||||
throw new ResourceNotFoundException("Person not found in contact");
|
||||
}
|
||||
|
||||
contact = contactRepository.save(contact);
|
||||
return mapToResponse(contact);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void delete(UUID id) {
|
||||
if (!contactRepository.existsById(id)) {
|
||||
throw new ResourceNotFoundException("Contact not found with id: " + id);
|
||||
}
|
||||
contactRepository.deleteById(id);
|
||||
}
|
||||
|
||||
private ContactResponse mapToResponse(Contact contact) {
|
||||
ContactRoleResponse roleResponse = new ContactRoleResponse(
|
||||
contact.getContactRole().getId(),
|
||||
contact.getContactRole().getRoleName(),
|
||||
contact.getContactRole().getDescription(),
|
||||
contact.getContactRole().getCreatedAt(),
|
||||
contact.getContactRole().getUpdatedAt()
|
||||
);
|
||||
|
||||
List<PersonInContactResponse> personsResponse = contact.getContactPersons().stream()
|
||||
.map(cp -> {
|
||||
PersonResponse personResponse = new PersonResponse(
|
||||
cp.getPerson().getId(),
|
||||
cp.getPerson().getFirstName(),
|
||||
cp.getPerson().getLastName(),
|
||||
cp.getPerson().getEmail(),
|
||||
cp.getPerson().getPhone(),
|
||||
cp.getPerson().getCreatedAt(),
|
||||
cp.getPerson().getUpdatedAt()
|
||||
);
|
||||
return new PersonInContactResponse(personResponse, cp.isPrimary());
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new ContactResponse(
|
||||
contact.getId(),
|
||||
roleResponse,
|
||||
personsResponse,
|
||||
contact.getCreatedAt(),
|
||||
contact.getUpdatedAt()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.ldpv2.service;
|
||||
|
||||
import com.ldpv2.domain.entity.Person;
|
||||
import com.ldpv2.dto.request.CreatePersonRequest;
|
||||
import com.ldpv2.dto.request.UpdatePersonRequest;
|
||||
import com.ldpv2.dto.response.PersonResponse;
|
||||
import com.ldpv2.exception.BadRequestException;
|
||||
import com.ldpv2.exception.ResourceNotFoundException;
|
||||
import com.ldpv2.repository.PersonRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class PersonService {
|
||||
|
||||
@Autowired
|
||||
private PersonRepository personRepository;
|
||||
|
||||
@Transactional
|
||||
public PersonResponse create(CreatePersonRequest request) {
|
||||
if (personRepository.existsByEmail(request.getEmail())) {
|
||||
throw new BadRequestException("Person with email '" + request.getEmail() + "' already exists");
|
||||
}
|
||||
|
||||
Person person = new Person();
|
||||
person.setFirstName(request.getFirstName());
|
||||
person.setLastName(request.getLastName());
|
||||
person.setEmail(request.getEmail());
|
||||
person.setPhone(request.getPhone());
|
||||
|
||||
person = personRepository.save(person);
|
||||
return mapToResponse(person);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public PersonResponse update(UUID id, UpdatePersonRequest request) {
|
||||
Person person = personRepository.findById(id)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Person not found with id: " + id));
|
||||
|
||||
if (request.getFirstName() != null) {
|
||||
person.setFirstName(request.getFirstName());
|
||||
}
|
||||
|
||||
if (request.getLastName() != null) {
|
||||
person.setLastName(request.getLastName());
|
||||
}
|
||||
|
||||
if (request.getEmail() != null) {
|
||||
if (!request.getEmail().equals(person.getEmail()) &&
|
||||
personRepository.existsByEmail(request.getEmail())) {
|
||||
throw new BadRequestException("Person with email '" + request.getEmail() + "' already exists");
|
||||
}
|
||||
person.setEmail(request.getEmail());
|
||||
}
|
||||
|
||||
if (request.getPhone() != null) {
|
||||
person.setPhone(request.getPhone());
|
||||
}
|
||||
|
||||
person = personRepository.save(person);
|
||||
return mapToResponse(person);
|
||||
}
|
||||
|
||||
public PersonResponse findById(UUID id) {
|
||||
Person person = personRepository.findById(id)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Person not found with id: " + id));
|
||||
return mapToResponse(person);
|
||||
}
|
||||
|
||||
public Page<PersonResponse> findAll(Pageable pageable) {
|
||||
return personRepository.findAll(pageable).map(this::mapToResponse);
|
||||
}
|
||||
|
||||
public Page<PersonResponse> search(String name, Pageable pageable) {
|
||||
return personRepository.findByName(name, pageable).map(this::mapToResponse);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void delete(UUID id) {
|
||||
if (!personRepository.existsById(id)) {
|
||||
throw new ResourceNotFoundException("Person not found with id: " + id);
|
||||
}
|
||||
personRepository.deleteById(id);
|
||||
}
|
||||
|
||||
private PersonResponse mapToResponse(Person person) {
|
||||
return new PersonResponse(
|
||||
person.getId(),
|
||||
person.getFirstName(),
|
||||
person.getLastName(),
|
||||
person.getEmail(),
|
||||
person.getPhone(),
|
||||
person.getCreatedAt(),
|
||||
person.getUpdatedAt()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -16,4 +16,7 @@
|
||||
<!-- Story 2: Applications -->
|
||||
<include file="db/changelog/v1.0/004-create-application-table.xml"/>
|
||||
|
||||
<!-- Story 3: Contacts -->
|
||||
<include file="db/changelog/v1.0/005-create-contact-tables.xml"/>
|
||||
|
||||
</databaseChangeLog>
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog
|
||||
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
|
||||
|
||||
<changeSet id="005-create-contact-tables" author="ldpv2-team">
|
||||
|
||||
<!-- Contact Roles Table -->
|
||||
<createTable tableName="contact_role">
|
||||
<column name="id" type="UUID" defaultValueComputed="uuid_generate_v4()">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="role_name" type="VARCHAR(100)">
|
||||
<constraints nullable="false" unique="true"/>
|
||||
</column>
|
||||
<column name="description" type="TEXT"/>
|
||||
<column name="created_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="updated_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
|
||||
<!-- Person Table -->
|
||||
<createTable tableName="person">
|
||||
<column name="id" type="UUID" defaultValueComputed="uuid_generate_v4()">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="first_name" type="VARCHAR(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="last_name" type="VARCHAR(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="email" type="VARCHAR(255)">
|
||||
<constraints nullable="false" unique="true"/>
|
||||
</column>
|
||||
<column name="phone" type="VARCHAR(50)"/>
|
||||
<column name="created_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="updated_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
|
||||
<!-- Contact Table -->
|
||||
<createTable tableName="contact">
|
||||
<column name="id" type="UUID" defaultValueComputed="uuid_generate_v4()">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="contact_role_id" type="UUID">
|
||||
<constraints nullable="false"
|
||||
foreignKeyName="fk_contact_role"
|
||||
references="contact_role(id)"/>
|
||||
</column>
|
||||
<column name="created_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="updated_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
|
||||
<!-- Contact-Person Junction Table -->
|
||||
<createTable tableName="contact_person">
|
||||
<column name="contact_id" type="UUID">
|
||||
<constraints nullable="false"
|
||||
foreignKeyName="fk_contact_person_contact"
|
||||
references="contact(id)"
|
||||
deleteCascade="true"/>
|
||||
</column>
|
||||
<column name="person_id" type="UUID">
|
||||
<constraints nullable="false"
|
||||
foreignKeyName="fk_contact_person_person"
|
||||
references="person(id)"
|
||||
deleteCascade="true"/>
|
||||
</column>
|
||||
<column name="is_primary" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
|
||||
<!-- Indexes -->
|
||||
<createIndex tableName="person" indexName="idx_person_email">
|
||||
<column name="email"/>
|
||||
</createIndex>
|
||||
|
||||
<createIndex tableName="contact" indexName="idx_contact_role">
|
||||
<column name="contact_role_id"/>
|
||||
</createIndex>
|
||||
|
||||
<addPrimaryKey tableName="contact_person"
|
||||
columnNames="contact_id, person_id"
|
||||
constraintName="pk_contact_person"/>
|
||||
|
||||
<!-- Insert default contact roles -->
|
||||
<insert tableName="contact_role">
|
||||
<column name="role_name" value="Product Owner"/>
|
||||
<column name="description" value="Responsible for product vision and backlog"/>
|
||||
</insert>
|
||||
|
||||
<insert tableName="contact_role">
|
||||
<column name="role_name" value="Functional Authority"/>
|
||||
<column name="description" value="Business decision maker"/>
|
||||
</insert>
|
||||
|
||||
<insert tableName="contact_role">
|
||||
<column name="role_name" value="Developer"/>
|
||||
<column name="description" value="Software developer"/>
|
||||
</insert>
|
||||
|
||||
<insert tableName="contact_role">
|
||||
<column name="role_name" value="Maintainer"/>
|
||||
<column name="description" value="System maintainer"/>
|
||||
</insert>
|
||||
|
||||
<insert tableName="contact_role">
|
||||
<column name="role_name" value="Technical Lead"/>
|
||||
<column name="description" value="Technical team leader"/>
|
||||
</insert>
|
||||
|
||||
<insert tableName="contact_role">
|
||||
<column name="role_name" value="Business Analyst"/>
|
||||
<column name="description" value="Requirements analyst"/>
|
||||
</insert>
|
||||
|
||||
<insert tableName="contact_role">
|
||||
<column name="role_name" value="Director"/>
|
||||
<column name="description" value="Business unit director"/>
|
||||
</insert>
|
||||
|
||||
<insert tableName="contact_role">
|
||||
<column name="role_name" value="Program Manager"/>
|
||||
<column name="description" value="Program management"/>
|
||||
</insert>
|
||||
|
||||
<!-- Insert sample persons -->
|
||||
<insert tableName="person">
|
||||
<column name="first_name" value="John"/>
|
||||
<column name="last_name" value="Doe"/>
|
||||
<column name="email" value="john.doe@example.com"/>
|
||||
<column name="phone" value="+1-555-0101"/>
|
||||
</insert>
|
||||
|
||||
<insert tableName="person">
|
||||
<column name="first_name" value="Jane"/>
|
||||
<column name="last_name" value="Smith"/>
|
||||
<column name="email" value="jane.smith@example.com"/>
|
||||
<column name="phone" value="+1-555-0102"/>
|
||||
</insert>
|
||||
|
||||
<insert tableName="person">
|
||||
<column name="first_name" value="Bob"/>
|
||||
<column name="last_name" value="Johnson"/>
|
||||
<column name="email" value="bob.johnson@example.com"/>
|
||||
<column name="phone" value="+1-555-0103"/>
|
||||
</insert>
|
||||
|
||||
<insert tableName="person">
|
||||
<column name="first_name" value="Alice"/>
|
||||
<column name="last_name" value="Williams"/>
|
||||
<column name="email" value="alice.williams@example.com"/>
|
||||
<column name="phone" value="+1-555-0104"/>
|
||||
</insert>
|
||||
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user