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 -->
|
<!-- Story 2: Applications -->
|
||||||
<include file="db/changelog/v1.0/004-create-application-table.xml"/>
|
<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>
|
</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>
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { Contact, ContactRole, CreateContactRequest, CreateContactRoleRequest } from '../../shared/models/contact.model';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ContactService {
|
||||||
|
private readonly CONTACT_URL = '/api/contacts';
|
||||||
|
private readonly ROLE_URL = '/api/contact-roles';
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
// Contact Roles
|
||||||
|
getContactRoles(): Observable<ContactRole[]> {
|
||||||
|
return this.http.get<ContactRole[]>(this.ROLE_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
createContactRole(data: CreateContactRoleRequest): Observable<ContactRole> {
|
||||||
|
return this.http.post<ContactRole>(this.ROLE_URL, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contacts
|
||||||
|
getContacts(): Observable<Contact[]> {
|
||||||
|
return this.http.get<Contact[]>(this.CONTACT_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
getContact(id: string): Observable<Contact> {
|
||||||
|
return this.http.get<Contact>(`${this.CONTACT_URL}/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
createContact(data: CreateContactRequest): Observable<Contact> {
|
||||||
|
return this.http.post<Contact>(this.CONTACT_URL, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
addPersonToContact(contactId: string, personId: string, isPrimary: boolean = false): Observable<Contact> {
|
||||||
|
const params = new HttpParams().set('isPrimary', isPrimary.toString());
|
||||||
|
return this.http.post<Contact>(`${this.CONTACT_URL}/${contactId}/persons/${personId}`, null, { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
removePersonFromContact(contactId: string, personId: string): Observable<Contact> {
|
||||||
|
return this.http.delete<Contact>(`${this.CONTACT_URL}/${contactId}/persons/${personId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPrimaryPerson(contactId: string, personId: string): Observable<Contact> {
|
||||||
|
return this.http.patch<Contact>(`${this.CONTACT_URL}/${contactId}/persons/${personId}/primary`, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteContact(id: string): Observable<void> {
|
||||||
|
return this.http.delete<void>(`${this.CONTACT_URL}/${id}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { Person, CreatePersonRequest, UpdatePersonRequest } from '../../shared/models/contact.model';
|
||||||
|
import { Page } from '../../shared/models/environment.model';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class PersonService {
|
||||||
|
private readonly API_URL = '/api/persons';
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
getPersons(
|
||||||
|
name?: string,
|
||||||
|
page: number = 0,
|
||||||
|
size: number = 20,
|
||||||
|
sortBy: string = 'lastName',
|
||||||
|
sortDirection: string = 'asc'
|
||||||
|
): Observable<Page<Person>> {
|
||||||
|
let params = new HttpParams()
|
||||||
|
.set('page', page.toString())
|
||||||
|
.set('size', size.toString())
|
||||||
|
.set('sortBy', sortBy)
|
||||||
|
.set('sortDirection', sortDirection);
|
||||||
|
|
||||||
|
if (name && name.trim()) {
|
||||||
|
params = params.set('name', name.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http.get<Page<Person>>(this.API_URL, { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
getPerson(id: string): Observable<Person> {
|
||||||
|
return this.http.get<Person>(`${this.API_URL}/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
createPerson(data: CreatePersonRequest): Observable<Person> {
|
||||||
|
return this.http.post<Person>(this.API_URL, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePerson(id: string, data: UpdatePersonRequest): Observable<Person> {
|
||||||
|
return this.http.put<Person>(`${this.API_URL}/${id}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
deletePerson(id: string): Observable<void> {
|
||||||
|
return this.http.delete<void>(`${this.API_URL}/${id}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
export interface ContactRole {
|
||||||
|
id: string;
|
||||||
|
roleName: string;
|
||||||
|
description?: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Person {
|
||||||
|
id: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
email: string;
|
||||||
|
phone?: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PersonInContact {
|
||||||
|
person: Person;
|
||||||
|
isPrimary: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Contact {
|
||||||
|
id: string;
|
||||||
|
contactRole: ContactRole;
|
||||||
|
persons: PersonInContact[];
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreatePersonRequest {
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
email: string;
|
||||||
|
phone?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdatePersonRequest {
|
||||||
|
firstName?: string;
|
||||||
|
lastName?: string;
|
||||||
|
email?: string;
|
||||||
|
phone?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateContactRequest {
|
||||||
|
contactRoleId: string;
|
||||||
|
personIds: string[];
|
||||||
|
primaryPersonId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateContactRoleRequest {
|
||||||
|
roleName: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user