merge customer/employee into users

This commit is contained in:
2026-04-06 20:17:27 -06:00
parent 824ed7e5eb
commit 2360dc2419
51 changed files with 286 additions and 2546 deletions

View File

@@ -2,8 +2,6 @@ package com.petshop.backend.config;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.StoreAssignmentService;
import com.petshop.backend.service.UserBusinessLinkageService;
import org.springframework.boot.CommandLineRunner;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@@ -13,14 +11,10 @@ public class DataInitializer implements CommandLineRunner {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final UserBusinessLinkageService userBusinessLinkageService;
private final StoreAssignmentService storeAssignmentService;
public DataInitializer(UserRepository userRepository, PasswordEncoder passwordEncoder, UserBusinessLinkageService userBusinessLinkageService, StoreAssignmentService storeAssignmentService) {
public DataInitializer(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.userBusinessLinkageService = userBusinessLinkageService;
this.storeAssignmentService = storeAssignmentService;
}
@Override
@@ -34,16 +28,25 @@ public class DataInitializer implements CommandLineRunner {
admin.setUsername("admin");
admin.setPassword(passwordEncoder.encode("admin123"));
admin.setEmail("admin@petshop.com");
admin.setFirstName("Admin");
admin.setLastName("User");
admin.setFullName("Admin User");
admin.setPhone("000-000-1000");
admin.setRole(User.Role.ADMIN);
admin.setActive(true);
admin = userRepository.save(admin);
userRepository.save(admin);
System.out.println("Admin user created successfully");
} else {
System.out.println("Admin user already exists");
// Normalize missing fields if needed
boolean updated = false;
if (admin.getFirstName() == null || admin.getFirstName().isEmpty()) {
admin.setFirstName("Admin");
updated = true;
}
if (admin.getLastName() == null || admin.getLastName().isEmpty()) {
admin.setLastName("User");
updated = true;
}
if (admin.getFullName() == null || admin.getFullName().isEmpty()) {
admin.setFullName("Admin User");
updated = true;
@@ -65,12 +68,10 @@ public class DataInitializer implements CommandLineRunner {
updated = true;
}
if (updated) {
admin = userRepository.save(admin);
userRepository.save(admin);
System.out.println("Admin user normalized");
}
}
// Ensure linked employee
storeAssignmentService.assignStoreIfMissing(userBusinessLinkageService.ensureLinkedEmployee(admin), 1L);
User staff = userRepository.findByUsername("staff").orElse(null);
if (staff == null) {
@@ -79,16 +80,25 @@ public class DataInitializer implements CommandLineRunner {
staff.setUsername("staff");
staff.setPassword(passwordEncoder.encode("staff123"));
staff.setEmail("staff@petshop.com");
staff.setFirstName("Staff");
staff.setLastName("User");
staff.setFullName("Staff User");
staff.setPhone("000-000-1001");
staff.setRole(User.Role.STAFF);
staff.setActive(true);
staff = userRepository.save(staff);
userRepository.save(staff);
System.out.println("Staff user created successfully");
} else {
System.out.println("Staff user already exists");
// Normalize missing fields if needed
boolean updated = false;
if (staff.getFirstName() == null || staff.getFirstName().isEmpty()) {
staff.setFirstName("Staff");
updated = true;
}
if (staff.getLastName() == null || staff.getLastName().isEmpty()) {
staff.setLastName("User");
updated = true;
}
if (staff.getFullName() == null || staff.getFullName().isEmpty()) {
staff.setFullName("Staff User");
updated = true;
@@ -110,12 +120,10 @@ public class DataInitializer implements CommandLineRunner {
updated = true;
}
if (updated) {
staff = userRepository.save(staff);
userRepository.save(staff);
System.out.println("Staff user normalized");
}
}
// Ensure linked employee
storeAssignmentService.assignStoreIfMissing(userBusinessLinkageService.ensureLinkedEmployee(staff), 1L);
User customer = userRepository.findByUsername("customer").orElse(null);
if (customer == null) {
@@ -124,16 +132,25 @@ public class DataInitializer implements CommandLineRunner {
customer.setUsername("customer");
customer.setPassword(passwordEncoder.encode("customer123"));
customer.setEmail("customer@petshop.com");
customer.setFirstName("Test");
customer.setLastName("Customer");
customer.setFullName("Test Customer");
customer.setPhone("000-000-1002");
customer.setRole(User.Role.CUSTOMER);
customer.setActive(true);
customer = userRepository.save(customer);
userRepository.save(customer);
System.out.println("Customer user created successfully");
} else {
System.out.println("Customer user already exists");
// Normalize missing fields if needed
boolean updated = false;
if (customer.getFirstName() == null || customer.getFirstName().isEmpty()) {
customer.setFirstName("Test");
updated = true;
}
if (customer.getLastName() == null || customer.getLastName().isEmpty()) {
customer.setLastName("Customer");
updated = true;
}
if (customer.getFullName() == null || customer.getFullName().isEmpty()) {
customer.setFullName("Test Customer");
updated = true;
@@ -155,12 +172,10 @@ public class DataInitializer implements CommandLineRunner {
updated = true;
}
if (updated) {
customer = userRepository.save(customer);
userRepository.save(customer);
System.out.println("Customer user normalized");
}
}
// Ensure linked customer
userBusinessLinkageService.ensureLinkedCustomer(customer);
System.out.println("==== DataInitializer: Completed ====");
}

View File

@@ -1,34 +0,0 @@
package com.petshop.backend.config;
import com.petshop.backend.repository.CustomerPetRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
@Component
@Profile("local")
public class LocalAppointmentCustomerSeedInitializer implements CommandLineRunner {
private final DataSource dataSource;
private final CustomerPetRepository customerPetRepository;
public LocalAppointmentCustomerSeedInitializer(DataSource dataSource, CustomerPetRepository customerPetRepository) {
this.dataSource = dataSource;
this.customerPetRepository = customerPetRepository;
}
@Override
public void run(String... args) {
if (customerPetRepository.count() > 0) {
return;
}
ResourceDatabasePopulator populator = new ResourceDatabasePopulator(false, false, "UTF-8",
new ClassPathResource("dev/seed_demo_customer_pets.sql"));
populator.execute(dataSource);
}
}

View File

@@ -3,8 +3,7 @@ package com.petshop.backend.controller;
import com.petshop.backend.dto.adoption.AdoptionRequest;
import com.petshop.backend.dto.adoption.AdoptionResponse;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.AdoptionService;
import com.petshop.backend.util.AuthenticationHelper;
@@ -24,12 +23,10 @@ public class AdoptionController {
private final AdoptionService adoptionService;
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public AdoptionController(AdoptionService adoptionService, UserRepository userRepository, CustomerRepository customerRepository) {
public AdoptionController(AdoptionService adoptionService, UserRepository userRepository) {
this.adoptionService = adoptionService;
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
@GetMapping
@@ -45,8 +42,8 @@ public class AdoptionController {
Long customerId = null;
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
customerId = customer.getCustomerId();
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = user.getId();
}
return ResponseEntity.ok(adoptionService.getAllAdoptions(q, pageable, customerId));
@@ -63,8 +60,8 @@ public class AdoptionController {
Long customerId = null;
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
customerId = customer.getCustomerId();
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = user.getId();
}
return ResponseEntity.ok(adoptionService.getAdoptionById(id, customerId));

View File

@@ -3,8 +3,7 @@ package com.petshop.backend.controller;
import com.petshop.backend.dto.appointment.AppointmentRequest;
import com.petshop.backend.dto.appointment.AppointmentResponse;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.AppointmentService;
import com.petshop.backend.util.AuthenticationHelper;
@@ -27,12 +26,10 @@ public class AppointmentController {
private final AppointmentService appointmentService;
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public AppointmentController(AppointmentService appointmentService, UserRepository userRepository, CustomerRepository customerRepository) {
public AppointmentController(AppointmentService appointmentService, UserRepository userRepository) {
this.appointmentService = appointmentService;
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
@GetMapping
@@ -48,8 +45,8 @@ public class AppointmentController {
Long customerId = null;
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
customerId = customer.getCustomerId();
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = user.getId();
}
return ResponseEntity.ok(appointmentService.getAllAppointments(q, pageable, customerId));
@@ -66,8 +63,8 @@ public class AppointmentController {
Long customerId = null;
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
customerId = customer.getCustomerId();
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = user.getId();
}
return ResponseEntity.ok(appointmentService.getAppointmentById(id, customerId));
@@ -83,8 +80,8 @@ public class AppointmentController {
.orElse(null);
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
if (!request.getCustomerId().equals(customer.getCustomerId())) {
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
if (!request.getCustomerId().equals(user.getId())) {
throw new org.springframework.security.access.AccessDeniedException("You can only create appointments for yourself");
}
}

View File

@@ -7,15 +7,11 @@ import com.petshop.backend.dto.auth.ProfileUpdateRequest;
import com.petshop.backend.dto.auth.RegisterRequest;
import com.petshop.backend.dto.auth.RegisterResponse;
import com.petshop.backend.dto.auth.UserInfoResponse;
import com.petshop.backend.entity.EmployeeStore;
import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.EmployeeRepository;
import com.petshop.backend.repository.EmployeeStoreRepository;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.security.JwtUtil;
import com.petshop.backend.service.AvatarStorageService;
import com.petshop.backend.service.UserBusinessLinkageService;
import com.petshop.backend.util.AuthenticationHelper;
import jakarta.validation.Valid;
import org.springframework.core.io.Resource;
@@ -44,22 +40,14 @@ public class AuthController {
private final UserRepository userRepository;
private final JwtUtil jwtUtil;
private final PasswordEncoder passwordEncoder;
private final UserBusinessLinkageService userBusinessLinkageService;
private final EmployeeRepository employeeRepository;
private final EmployeeStoreRepository employeeStoreRepository;
private final AvatarStorageService avatarStorageService;
private final CustomerRepository customerRepository;
public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, JwtUtil jwtUtil, PasswordEncoder passwordEncoder, UserBusinessLinkageService userBusinessLinkageService, EmployeeRepository employeeRepository, EmployeeStoreRepository employeeStoreRepository, AvatarStorageService avatarStorageService, CustomerRepository customerRepository) {
public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, JwtUtil jwtUtil, PasswordEncoder passwordEncoder, AvatarStorageService avatarStorageService) {
this.authenticationManager = authenticationManager;
this.userRepository = userRepository;
this.jwtUtil = jwtUtil;
this.passwordEncoder = passwordEncoder;
this.userBusinessLinkageService = userBusinessLinkageService;
this.employeeRepository = employeeRepository;
this.employeeStoreRepository = employeeStoreRepository;
this.avatarStorageService = avatarStorageService;
this.customerRepository = customerRepository;
}
@PostMapping("/register")
@@ -94,9 +82,6 @@ public class AuthController {
User savedUser = userRepository.save(user);
// Create or link customer record
userBusinessLinkageService.ensureLinkedCustomer(savedUser);
String token = jwtUtil.generateToken(savedUser);
return ResponseEntity.status(HttpStatus.CREATED).body(new RegisterResponse(
@@ -148,22 +133,7 @@ public class AuthController {
@GetMapping("/me")
public ResponseEntity<UserInfoResponse> getCurrentUser() {
User user = getAuthenticatedUser();
EmployeeStore employeeStore = resolveEmployeeStore(user);
Long customerId = resolveCustomerId(user);
return ResponseEntity.ok(new UserInfoResponse(
user.getId(),
user.getUsername(),
user.getEmail(),
user.getFullName(),
user.getPhone(),
avatarStorageService.toOwnerAvatarUrl(user),
user.getRole().name(),
customerId,
employeeStore != null ? employeeStore.getStore().getStoreId() : null,
employeeStore != null ? employeeStore.getStore().getStoreName() : null
));
return ResponseEntity.ok(toUserInfoResponse(user));
}
@PutMapping("/me")
@@ -218,39 +188,24 @@ public class AuthController {
}
User updatedUser = userRepository.save(user);
userBusinessLinkageService.syncLinkedRecords(updatedUser);
return ResponseEntity.ok(toUserInfoResponse(updatedUser));
}
EmployeeStore employeeStore = resolveEmployeeStore(updatedUser);
Long customerId = resolveCustomerId(updatedUser);
return ResponseEntity.ok(new UserInfoResponse(
updatedUser.getId(),
updatedUser.getUsername(),
updatedUser.getEmail(),
updatedUser.getFullName(),
updatedUser.getPhone(),
avatarStorageService.toOwnerAvatarUrl(updatedUser),
updatedUser.getRole().name(),
private UserInfoResponse toUserInfoResponse(User user) {
StoreLocation primaryStore = user.getPrimaryStore();
Long customerId = user.getRole() == User.Role.CUSTOMER ? user.getId() : null;
return new UserInfoResponse(
user.getId(),
user.getUsername(),
user.getEmail(),
user.getFullName(),
user.getPhone(),
avatarStorageService.toOwnerAvatarUrl(user),
user.getRole().name(),
customerId,
employeeStore != null ? employeeStore.getStore().getStoreId() : null,
employeeStore != null ? employeeStore.getStore().getStoreName() : null
));
}
private EmployeeStore resolveEmployeeStore(User user) {
if (user.getRole() == User.Role.CUSTOMER) {
return null;
}
return employeeRepository.findByUserId(user.getId())
.flatMap(employee -> employeeStoreRepository.findByEmployeeEmployeeId(employee.getEmployeeId()))
.orElse(null);
}
private Long resolveCustomerId(User user) {
return customerRepository.findByUserId(user.getId())
.map(c -> c.getCustomerId())
.orElse(null);
primaryStore != null ? primaryStore.getStoreId() : null,
primaryStore != null ? primaryStore.getStoreName() : null
);
}
private String trimToNull(String value) {

View File

@@ -6,7 +6,6 @@ import com.petshop.backend.dto.chat.MessageRequest;
import com.petshop.backend.dto.chat.MessageResponse;
import com.petshop.backend.dto.chat.UpdateConversationRequest;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.ChatRealtimeService;
import com.petshop.backend.service.ChatService;
@@ -27,13 +26,11 @@ public class ChatController {
private final ChatService chatService;
private final ChatRealtimeService chatRealtimeService;
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public ChatController(ChatService chatService, ChatRealtimeService chatRealtimeService, UserRepository userRepository, CustomerRepository customerRepository) {
public ChatController(ChatService chatService, ChatRealtimeService chatRealtimeService, UserRepository userRepository) {
this.chatService = chatService;
this.chatRealtimeService = chatRealtimeService;
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
private User getCurrentUser() {

View File

@@ -1,9 +1,9 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.dto.customer.CustomerRequest;
import com.petshop.backend.dto.customer.CustomerResponse;
import com.petshop.backend.service.CustomerService;
import com.petshop.backend.dto.user.UserRequest;
import com.petshop.backend.dto.user.UserResponse;
import com.petshop.backend.service.UserService;
import jakarta.validation.Valid;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -17,45 +17,45 @@ import org.springframework.web.bind.annotation.*;
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public class CustomerController {
private final CustomerService customerService;
private final UserService userService;
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
public CustomerController(UserService userService) {
this.userService = userService;
}
@GetMapping
public ResponseEntity<Page<CustomerResponse>> getAllCustomers(
public ResponseEntity<Page<UserResponse>> getAllCustomers(
@RequestParam(required = false) String q,
Pageable pageable) {
return ResponseEntity.ok(customerService.getAllCustomers(q, pageable));
return ResponseEntity.ok(userService.getAllUsers(q, "CUSTOMER", pageable));
}
@GetMapping("/{id}")
public ResponseEntity<CustomerResponse> getCustomerById(@PathVariable Long id) {
return ResponseEntity.ok(customerService.getCustomerById(id));
public ResponseEntity<UserResponse> getCustomerById(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
@PostMapping
public ResponseEntity<CustomerResponse> createCustomer(@Valid @RequestBody CustomerRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(customerService.createCustomer(request));
public ResponseEntity<UserResponse> createCustomer(@Valid @RequestBody UserRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(userService.createUser(request));
}
@PutMapping("/{id}")
public ResponseEntity<CustomerResponse> updateCustomer(
public ResponseEntity<UserResponse> updateCustomer(
@PathVariable Long id,
@Valid @RequestBody CustomerRequest request) {
return ResponseEntity.ok(customerService.updateCustomer(id, request));
@Valid @RequestBody UserRequest request) {
return ResponseEntity.ok(userService.updateUser(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteCustomer(@PathVariable Long id) {
customerService.deleteCustomer(id);
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/bulk-delete")
public ResponseEntity<Void> bulkDeleteCustomers(@Valid @RequestBody BulkDeleteRequest request) {
customerService.bulkDeleteCustomers(request);
userService.bulkDeleteUsers(request);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,118 +0,0 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.customerpet.CustomerPetRequest;
import com.petshop.backend.dto.customerpet.CustomerPetResponse;
import com.petshop.backend.service.CatalogImageStorageService;
import com.petshop.backend.service.CustomerPetService;
import com.petshop.backend.entity.CustomerPet;
import com.petshop.backend.repository.CustomerPetRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.util.AuthenticationHelper;
import jakarta.validation.Valid;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/my-pets")
@PreAuthorize("hasRole('CUSTOMER')")
public class CustomerPetController {
private final CustomerPetService customerPetService;
private final CustomerPetRepository customerPetRepository;
private final CustomerRepository customerRepository;
private final UserRepository userRepository;
private final CatalogImageStorageService catalogImageStorageService;
public CustomerPetController(CustomerPetService customerPetService,
CustomerPetRepository customerPetRepository,
CustomerRepository customerRepository,
UserRepository userRepository,
CatalogImageStorageService catalogImageStorageService) {
this.customerPetService = customerPetService;
this.customerPetRepository = customerPetRepository;
this.customerRepository = customerRepository;
this.userRepository = userRepository;
this.catalogImageStorageService = catalogImageStorageService;
}
@GetMapping
public ResponseEntity<List<CustomerPetResponse>> getMyPets() {
return ResponseEntity.ok(customerPetService.getMyPets());
}
@PostMapping
public ResponseEntity<CustomerPetResponse> createPet(@Valid @RequestBody CustomerPetRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(customerPetService.createPet(request));
}
@PutMapping("/{id}")
public ResponseEntity<CustomerPetResponse> updatePet(@PathVariable Long id, @Valid @RequestBody CustomerPetRequest request) {
return ResponseEntity.ok(customerPetService.updatePet(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deletePet(@PathVariable Long id) {
customerPetService.deletePet(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/{id}/image")
public ResponseEntity<?> uploadImage(@PathVariable Long id, @RequestParam("image") MultipartFile image) {
try {
return ResponseEntity.ok(customerPetService.uploadImage(id, image));
}
catch (IllegalArgumentException ex) {
Map<String, String> error = new HashMap<>();
error.put("message", ex.getMessage());
return ResponseEntity.badRequest().body(error);
}
catch (IOException ex) {
Map<String, String> error = new HashMap<>();
error.put("message", "Failed to upload image: " + ex.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
@GetMapping("/{id}/image")
public ResponseEntity<Resource> getImage(@PathVariable Long id) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
CustomerPet pet = customerPetRepository.findByCustomerPetIdAndCustomerCustomerId(id, customer.getCustomerId()).orElse(null);
if (pet == null || pet.getImageUrl() == null || pet.getImageUrl().isBlank()) {
return ResponseEntity.notFound().build();
}
Resource resource = catalogImageStorageService.loadPetImage(pet.getImageUrl());
MediaType mediaType = catalogImageStorageService.resolveMediaType(resource);
return ResponseEntity.ok().contentType(mediaType).body(resource);
}
@DeleteMapping("/{id}/image")
public ResponseEntity<CustomerPetResponse> deleteImage(@PathVariable Long id) {
return ResponseEntity.ok(customerPetService.deleteImage(id));
}
}

View File

@@ -1,8 +1,6 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.common.DropdownOption;
import com.petshop.backend.entity.CustomerPet;
import com.petshop.backend.entity.EmployeeStore;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.*;
import org.springframework.http.ResponseEntity;
@@ -20,31 +18,24 @@ import java.util.stream.Collectors;
public class DropdownController {
private final PetRepository petRepository;
private final CustomerRepository customerRepository;
private final CustomerPetRepository customerPetRepository;
private final ServiceRepository serviceRepository;
private final ProductRepository productRepository;
private final CategoryRepository categoryRepository;
private final StoreRepository storeRepository;
private final SupplierRepository supplierRepository;
private final EmployeeStoreRepository employeeStoreRepository;
private final UserRepository userRepository;
public DropdownController(PetRepository petRepository, CustomerRepository customerRepository,
CustomerPetRepository customerPetRepository,
public DropdownController(PetRepository petRepository,
ServiceRepository serviceRepository, ProductRepository productRepository,
CategoryRepository categoryRepository, StoreRepository storeRepository,
SupplierRepository supplierRepository, EmployeeStoreRepository employeeStoreRepository,
SupplierRepository supplierRepository,
UserRepository userRepository) {
this.petRepository = petRepository;
this.customerRepository = customerRepository;
this.customerPetRepository = customerPetRepository;
this.serviceRepository = serviceRepository;
this.productRepository = productRepository;
this.categoryRepository = categoryRepository;
this.storeRepository = storeRepository;
this.supplierRepository = supplierRepository;
this.employeeStoreRepository = employeeStoreRepository;
this.userRepository = userRepository;
}
@@ -71,8 +62,8 @@ public class DropdownController {
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getCustomers() {
return ResponseEntity.ok(
customerRepository.findAll().stream()
.map(c -> new DropdownOption(c.getCustomerId(), c.getFirstName() + " " + c.getLastName()))
userRepository.findByRoleAndActiveTrue(User.Role.CUSTOMER).stream()
.map(u -> new DropdownOption(u.getId(), u.getFirstName() + " " + u.getLastName()))
.collect(Collectors.toList())
);
}
@@ -81,18 +72,8 @@ public class DropdownController {
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getAppointmentCustomers() {
return ResponseEntity.ok(
customerRepository.findAllWithPets().stream()
.map(c -> new DropdownOption(c.getCustomerId(), c.getFirstName() + " " + c.getLastName()))
.collect(Collectors.toList())
);
}
@GetMapping("/customers/{customerId}/pets")
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getCustomerPets(@PathVariable Long customerId) {
return ResponseEntity.ok(
customerPetRepository.findByCustomerCustomerIdOrderByPetNameAsc(customerId).stream()
.map(this::toCustomerPetOption)
userRepository.findByRoleAndActiveTrue(User.Role.CUSTOMER).stream()
.map(u -> new DropdownOption(u.getId(), u.getFirstName() + " " + u.getLastName()))
.collect(Collectors.toList())
);
}
@@ -159,17 +140,15 @@ public class DropdownController {
@GetMapping({"/stores/{storeId}/employees", "/employees"})
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getStoreEmployees(@PathVariable(required = false) Long storeId) {
List<EmployeeStore> employees;
List<User> employees;
if (storeId == null || storeId == 0) {
employees = employeeStoreRepository.findActiveAllOrderByEmployeeEmployeeIdAsc();
employees = userRepository.findByRoleAndActiveTrue(User.Role.STAFF);
} else {
employees = employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(storeId);
employees = userRepository.findByPrimaryStoreStoreIdAndRoleAndActiveTrue(storeId, User.Role.STAFF);
}
return ResponseEntity.ok(
employees.stream()
.filter(this::isAssignableEmployee)
.map(this::toEmployeeOption)
.distinct()
.map(u -> new DropdownOption(u.getId(), u.getFirstName() + " " + u.getLastName()))
.collect(Collectors.toList())
);
}
@@ -183,26 +162,4 @@ public class DropdownController {
.collect(Collectors.toList())
);
}
private DropdownOption toCustomerPetOption(CustomerPet pet) {
String species = pet.getSpecies() == null || pet.getSpecies().isBlank() ? "Pet" : pet.getSpecies();
String breed = pet.getBreed() == null || pet.getBreed().isBlank() ? "" : " · " + pet.getBreed();
return new DropdownOption(pet.getCustomerPetId(), pet.getPetName() + " (" + species + breed + ")");
}
private DropdownOption toEmployeeOption(EmployeeStore employeeStore) {
var employee = employeeStore.getEmployee();
return new DropdownOption(employee.getEmployeeId(), employee.getFirstName() + " " + employee.getLastName());
}
private boolean isAssignableEmployee(EmployeeStore employeeStore) {
Long userId = employeeStore.getEmployee().getUserId();
if (userId == null) {
return false;
}
return userRepository.findById(userId)
.filter(user -> user.getRole() == User.Role.STAFF)
.filter(user -> Boolean.TRUE.equals(user.getActive()))
.isPresent();
}
}

View File

@@ -1,8 +1,8 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.employee.EmployeeRequest;
import com.petshop.backend.dto.employee.EmployeeResponse;
import com.petshop.backend.service.EmployeeService;
import com.petshop.backend.dto.user.UserRequest;
import com.petshop.backend.dto.user.UserResponse;
import com.petshop.backend.service.UserService;
import jakarta.validation.Valid;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -15,35 +15,40 @@ import org.springframework.web.bind.annotation.*;
@RequestMapping("/api/v1/employees")
@PreAuthorize("hasRole('ADMIN')")
public class EmployeeController {
private final EmployeeService employeeService;
public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
private final UserService userService;
public EmployeeController(UserService userService) {
this.userService = userService;
}
@GetMapping
public ResponseEntity<Page<EmployeeResponse>> getAllEmployees(@RequestParam(required = false) String q, Pageable pageable) {
return ResponseEntity.ok(employeeService.getAllEmployees(q, pageable));
public ResponseEntity<Page<UserResponse>> getAllEmployees(
@RequestParam(required = false) String q,
Pageable pageable) {
return ResponseEntity.ok(userService.getAllUsers(q, "STAFF", pageable));
}
@GetMapping("/{id}")
public ResponseEntity<EmployeeResponse> getEmployeeById(@PathVariable Long id) {
return ResponseEntity.ok(employeeService.getEmployeeById(id));
public ResponseEntity<UserResponse> getEmployeeById(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
@PostMapping
public ResponseEntity<EmployeeResponse> createEmployee(@Valid @RequestBody EmployeeRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(employeeService.createEmployee(request));
public ResponseEntity<UserResponse> createEmployee(@Valid @RequestBody UserRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(userService.createUser(request));
}
@PutMapping("/{id}")
public ResponseEntity<EmployeeResponse> updateEmployee(@PathVariable Long id, @Valid @RequestBody EmployeeRequest request) {
return ResponseEntity.ok(employeeService.updateEmployee(id, request));
public ResponseEntity<UserResponse> updateEmployee(
@PathVariable Long id,
@Valid @RequestBody UserRequest request) {
return ResponseEntity.ok(userService.updateUser(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteEmployee(@PathVariable Long id) {
employeeService.deleteEmployee(id);
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
}

View File

@@ -3,8 +3,7 @@ package com.petshop.backend.controller;
import com.petshop.backend.dto.refund.RefundRequest;
import com.petshop.backend.dto.refund.RefundResponse;
import com.petshop.backend.dto.refund.RefundUpdateRequest;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.RefundService;
import com.petshop.backend.util.AuthenticationHelper;
@@ -26,12 +25,10 @@ public class RefundController {
private final RefundService refundService;
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public RefundController(RefundService refundService, UserRepository userRepository, CustomerRepository customerRepository) {
public RefundController(RefundService refundService, UserRepository userRepository) {
this.refundService = refundService;
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
@PostMapping
@@ -46,8 +43,8 @@ public class RefundController {
Long customerId = null;
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
customerId = customer.getCustomerId();
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = user.getId();
}
RefundResponse refund = refundService.createRefund(request, customerId);
@@ -70,8 +67,8 @@ public class RefundController {
Long customerId = null;
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
customerId = customer.getCustomerId();
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = user.getId();
}
List<RefundResponse> refunds = refundService.getAllRefunds(customerId);
@@ -90,8 +87,8 @@ public class RefundController {
Long customerId = null;
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
customerId = customer.getCustomerId();
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = user.getId();
}
RefundResponse refund = refundService.getRefundById(id, customerId);

View File

@@ -27,8 +27,6 @@ public class AppointmentRequest {
private List<Long> petIds;
private List<Long> customerPetIds;
private Long employeeId;
public Long getCustomerId() {
@@ -87,14 +85,6 @@ public class AppointmentRequest {
this.petIds = petIds;
}
public List<Long> getCustomerPetIds() {
return customerPetIds;
}
public void setCustomerPetIds(List<Long> customerPetIds) {
this.customerPetIds = customerPetIds;
}
public Long getEmployeeId() {
return employeeId;
}
@@ -115,13 +105,12 @@ public class AppointmentRequest {
Objects.equals(appointmentTime, that.appointmentTime) &&
Objects.equals(appointmentStatus, that.appointmentStatus) &&
Objects.equals(petIds, that.petIds) &&
Objects.equals(customerPetIds, that.customerPetIds) &&
Objects.equals(employeeId, that.employeeId);
}
@Override
public int hashCode() {
return Objects.hash(customerId, storeId, serviceId, appointmentDate, appointmentTime, appointmentStatus, petIds, customerPetIds, employeeId);
return Objects.hash(customerId, storeId, serviceId, appointmentDate, appointmentTime, appointmentStatus, petIds, employeeId);
}
@Override
@@ -134,7 +123,6 @@ public class AppointmentRequest {
", appointmentTime=" + appointmentTime +
", appointmentStatus='" + appointmentStatus + '\'' +
", petIds=" + petIds +
", customerPetIds=" + customerPetIds +
", employeeId=" + employeeId +
'}';
}

View File

@@ -21,8 +21,6 @@ public class AppointmentResponse {
private String employeeName;
private List<String> petNames;
private List<Long> petIds;
private List<String> customerPetNames;
private List<Long> customerPetIds;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@@ -158,24 +156,6 @@ public class AppointmentResponse {
this.petIds = petIds;
}
public List<String> getCustomerPetNames() {
return customerPetNames;
}
public void setCustomerPetNames(List<String> customerPetNames) {
this.customerPetNames = customerPetNames;
}
public List<Long> getCustomerPetIds() {
return customerPetIds;
}
public void setCustomerPetIds(List<Long> customerPetIds) {
this.customerPetIds = customerPetIds;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}

View File

@@ -1,64 +0,0 @@
package com.petshop.backend.dto.customer;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import java.util.Objects;
public class CustomerRequest {
@NotBlank(message = "First name is required")
private String firstName;
@NotBlank(message = "Last name is required")
private String lastName;
@Email(message = "Invalid email format")
private String email;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CustomerRequest that = (CustomerRequest) o;
return Objects.equals(firstName, that.firstName) &&
Objects.equals(lastName, that.lastName) &&
Objects.equals(email, that.email);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName, email);
}
@Override
public String toString() {
return "CustomerRequest{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
'}';
}
}

View File

@@ -1,98 +0,0 @@
package com.petshop.backend.dto.customer;
import java.time.LocalDateTime;
import java.util.Objects;
public class CustomerResponse {
private Long customerId;
private String firstName;
private String lastName;
private String email;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public CustomerResponse() {
}
public CustomerResponse(Long customerId, String firstName, String lastName, String email, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.customerId = customerId;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CustomerResponse that = (CustomerResponse) o;
return Objects.equals(customerId, that.customerId) && Objects.equals(firstName, that.firstName) && Objects.equals(lastName, that.lastName) && Objects.equals(email, that.email) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
}
@Override
public int hashCode() {
return Objects.hash(customerId, firstName, lastName, email, createdAt, updatedAt);
}
@Override
public String toString() {
return "CustomerResponse{" +
"customerId=" + customerId +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}

View File

@@ -1,66 +0,0 @@
package com.petshop.backend.dto.customerpet;
import jakarta.validation.constraints.NotBlank;
import java.util.Objects;
public class CustomerPetRequest {
@NotBlank(message = "Pet name is required")
private String petName;
@NotBlank(message = "Species is required")
private String species;
private String breed;
public String getPetName() {
return petName;
}
public void setPetName(String petName) {
this.petName = petName;
}
public String getSpecies() {
return species;
}
public void setSpecies(String species) {
this.species = species;
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CustomerPetRequest that = (CustomerPetRequest) o;
return Objects.equals(petName, that.petName) && Objects.equals(species, that.species) && Objects.equals(breed, that.breed);
}
@Override
public int hashCode() {
return Objects.hash(petName, species, breed);
}
}

View File

@@ -1,123 +0,0 @@
package com.petshop.backend.dto.customerpet;
import java.time.LocalDateTime;
import java.util.Objects;
public class CustomerPetResponse {
private Long customerPetId;
private Long customerId;
private String petName;
private String species;
private String breed;
private String imageUrl;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public CustomerPetResponse() {
}
public CustomerPetResponse(Long customerPetId, Long customerId, String petName, String species, String breed, String imageUrl, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.customerPetId = customerPetId;
this.customerId = customerId;
this.petName = petName;
this.species = species;
this.breed = breed;
this.imageUrl = imageUrl;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getCustomerPetId() {
return customerPetId;
}
public void setCustomerPetId(Long customerPetId) {
this.customerPetId = customerPetId;
}
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public String getPetName() {
return petName;
}
public void setPetName(String petName) {
this.petName = petName;
}
public String getSpecies() {
return species;
}
public void setSpecies(String species) {
this.species = species;
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CustomerPetResponse that = (CustomerPetResponse) o;
return Objects.equals(customerPetId, that.customerPetId);
}
@Override
public int hashCode() {
return Objects.hash(customerPetId);
}
}

View File

@@ -1,51 +0,0 @@
package com.petshop.backend.dto.employee;
import com.petshop.backend.entity.User;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
public class EmployeeRequest {
@NotBlank(message = "Username is required")
@Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
private String username;
@Size(min = 6, message = "Password must be at least 6 characters")
private String password;
@NotBlank(message = "First name is required")
private String firstName;
@NotBlank(message = "Last name is required")
private String lastName;
@Email(message = "Invalid email format")
private String email;
@NotBlank(message = "Phone is required")
@Size(max = 20, message = "Phone must not exceed 20 characters")
private String phone;
@NotNull(message = "Role is required")
private User.Role role;
private Boolean active = true;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public User.Role getRole() { return role; }
public void setRole(User.Role role) { this.role = role; }
public Boolean getActive() { return active; }
public void setActive(Boolean active) { this.active = active; }
}

View File

@@ -1,43 +0,0 @@
package com.petshop.backend.dto.employee;
import java.time.LocalDateTime;
public class EmployeeResponse {
private Long employeeId;
private Long userId;
private String username;
private String firstName;
private String lastName;
private String fullName;
private String email;
private String phone;
private String role;
private Boolean active;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public Long getEmployeeId() { return employeeId; }
public void setEmployeeId(Long employeeId) { this.employeeId = employeeId; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getFullName() { return fullName; }
public void setFullName(String fullName) { this.fullName = fullName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
public Boolean getActive() { return active; }
public void setActive(Boolean active) { this.active = active; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
}

View File

@@ -14,8 +14,8 @@ public class ActivityLog {
private Long logId;
@ManyToOne
@JoinColumn(name = "employeeId", nullable = false)
private Employee employee;
@JoinColumn(name = "userId", nullable = false)
private User user;
@Column(nullable = false, columnDefinition = "TEXT")
private String activity;
@@ -26,9 +26,9 @@ public class ActivityLog {
public ActivityLog() {
}
public ActivityLog(Long logId, Employee employee, String activity, LocalDateTime logTimestamp) {
public ActivityLog(Long logId, User user, String activity, LocalDateTime logTimestamp) {
this.logId = logId;
this.employee = employee;
this.user = user;
this.activity = activity;
this.logTimestamp = logTimestamp;
}
@@ -41,12 +41,12 @@ public class ActivityLog {
this.logId = logId;
}
public Employee getEmployee() {
return employee;
public User getUser() {
return user;
}
public void setEmployee(Employee employee) {
this.employee = employee;
public void setUser(User user) {
this.user = user;
}
public String getActivity() {
@@ -82,7 +82,7 @@ public class ActivityLog {
public String toString() {
return "ActivityLog{" +
"logId=" + logId +
", employee=" + employee +
", user=" + user +
", activity='" + activity + '\'' +
", logTimestamp=" + logTimestamp +
'}';

View File

@@ -4,7 +4,6 @@ import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Objects;
@@ -23,11 +22,11 @@ public class Adoption {
@ManyToOne
@JoinColumn(name = "customerId", nullable = false)
private Customer customer;
private User customer;
@ManyToOne
@JoinColumn(name = "employeeId", nullable = false)
private Employee employee;
private User employee;
@Column(nullable = false)
private LocalDate adoptionDate;
@@ -46,17 +45,6 @@ public class Adoption {
public Adoption() {
}
public Adoption(Long adoptionId, Pet pet, Customer customer, Employee employee, LocalDate adoptionDate, String adoptionStatus, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.adoptionId = adoptionId;
this.pet = pet;
this.customer = customer;
this.employee = employee;
this.adoptionDate = adoptionDate;
this.adoptionStatus = adoptionStatus;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getAdoptionId() {
return adoptionId;
}
@@ -73,19 +61,19 @@ public class Adoption {
this.pet = pet;
}
public Customer getCustomer() {
public User getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
public void setCustomer(User customer) {
this.customer = customer;
}
public Employee getEmployee() {
public User getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
public void setEmployee(User employee) {
this.employee = employee;
}
@@ -138,13 +126,8 @@ public class Adoption {
public String toString() {
return "Adoption{" +
"adoptionId=" + adoptionId +
", pet=" + pet +
", customer=" + customer +
", employee=" + employee +
", adoptionDate=" + adoptionDate +
", adoptionStatus='" + adoptionStatus + '\'' +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
", adoptionDate=" + adoptionDate +
'}';
}
}

View File

@@ -21,7 +21,7 @@ public class Appointment {
@ManyToOne
@JoinColumn(name = "customerId", nullable = false)
private Customer customer;
private User customer;
@ManyToOne
@JoinColumn(name = "storeId", nullable = false)
@@ -33,7 +33,7 @@ public class Appointment {
@ManyToOne
@JoinColumn(name = "employeeId", nullable = false)
private Employee employee;
private User employee;
@Column(nullable = false)
private LocalDate appointmentDate;
@@ -52,14 +52,6 @@ public class Appointment {
)
private Set<Pet> pets = new HashSet<>();
@ManyToMany
@JoinTable(
name = "appointment_customer_pet",
joinColumns = @JoinColumn(name = "appointment_id"),
inverseJoinColumns = @JoinColumn(name = "customer_pet_id")
)
private Set<CustomerPet> customerPets = new HashSet<>();
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@@ -71,20 +63,6 @@ public class Appointment {
public Appointment() {
}
public Appointment(Long appointmentId, Customer customer, StoreLocation store, Service service, Employee employee, LocalDate appointmentDate, LocalTime appointmentTime, String appointmentStatus, Set<Pet> pets, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.appointmentId = appointmentId;
this.customer = customer;
this.store = store;
this.service = service;
this.employee = employee;
this.appointmentDate = appointmentDate;
this.appointmentTime = appointmentTime;
this.appointmentStatus = appointmentStatus;
this.pets = pets;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getAppointmentId() {
return appointmentId;
}
@@ -93,11 +71,11 @@ public class Appointment {
this.appointmentId = appointmentId;
}
public Customer getCustomer() {
public User getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
public void setCustomer(User customer) {
this.customer = customer;
}
@@ -117,11 +95,11 @@ public class Appointment {
this.service = service;
}
public Employee getEmployee() {
public User getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
public void setEmployee(User employee) {
this.employee = employee;
}
@@ -157,15 +135,6 @@ public class Appointment {
this.pets = pets;
}
public Set<CustomerPet> getCustomerPets() {
return customerPets;
}
public void setCustomerPets(Set<CustomerPet> customerPets) {
this.customerPets = customerPets;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}

View File

@@ -1,132 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
import java.util.Objects;
@Entity
@Table(name = "customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long customerId;
@Column(name = "user_id")
private Long userId;
@Column(nullable = false, length = 50)
private String firstName;
@Column(nullable = false, length = 50)
private String lastName;
@Column(nullable = false, length = 100)
private String email;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public Customer() {
}
public Customer(Long customerId, Long userId, String firstName, String lastName, String email, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.customerId = customerId;
this.userId = userId;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Customer customer = (Customer) o;
return Objects.equals(customerId, customer.customerId);
}
@Override
public int hashCode() {
return Objects.hash(customerId);
}
@Override
public String toString() {
return "Customer{" +
"customerId=" + customerId +
", userId=" + userId +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}

View File

@@ -1,137 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
import java.util.Objects;
@Entity
@Table(name = "customer_pet")
public class CustomerPet {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "customer_pet_id")
private Long customerPetId;
@ManyToOne
@JoinColumn(name = "customer_id", nullable = false)
private Customer customer;
@Column(name = "pet_name", nullable = false, length = 50)
private String petName;
@Column(nullable = false, length = 50)
private String species;
@Column(length = 50)
private String breed;
@Column(name = "image_url", length = 255)
private String imageUrl;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public CustomerPet() {
}
public Long getCustomerPetId() {
return customerPetId;
}
public void setCustomerPetId(Long customerPetId) {
this.customerPetId = customerPetId;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public String getPetName() {
return petName;
}
public void setPetName(String petName) {
this.petName = petName;
}
public String getSpecies() {
return species;
}
public void setSpecies(String species) {
this.species = species;
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CustomerPet that = (CustomerPet) o;
return Objects.equals(customerPetId, that.customerPetId);
}
@Override
public int hashCode() {
return Objects.hash(customerPetId);
}
}

View File

@@ -1,158 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
import java.util.Objects;
@Entity
@Table(name = "employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long employeeId;
@Column(name = "user_id")
private Long userId;
@Column(nullable = false, length = 50)
private String firstName;
@Column(nullable = false, length = 50)
private String lastName;
@Column(nullable = false, length = 100)
private String email;
@Column(nullable = false, length = 50)
private String role;
@Column(nullable = false)
private Boolean isActive = true;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public Employee() {
}
public Employee(Long employeeId, Long userId, String firstName, String lastName, String email, String role, Boolean isActive, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.employeeId = employeeId;
this.userId = userId;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.role = role;
this.isActive = isActive;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Long employeeId) {
this.employeeId = employeeId;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public Boolean getIsActive() {
return isActive;
}
public void setIsActive(Boolean isActive) {
this.isActive = isActive;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(employeeId, employee.employeeId);
}
@Override
public int hashCode() {
return Objects.hash(employeeId);
}
@Override
public String toString() {
return "Employee{" +
"employeeId=" + employeeId +
", userId=" + userId +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", role='" + role + '\'' +
", isActive=" + isActive +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}

View File

@@ -1,117 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import java.io.Serializable;
import java.util.Objects;
@Entity
@Table(name = "employeeStore")
@IdClass(EmployeeStore.EmployeeStoreId.class)
public class EmployeeStore {
@Id
@ManyToOne
@JoinColumn(name = "employeeId", nullable = false)
private Employee employee;
@Id
@ManyToOne
@JoinColumn(name = "storeId", nullable = false)
private StoreLocation store;
public EmployeeStore() {
}
public EmployeeStore(Employee employee, StoreLocation store) {
this.employee = employee;
this.store = store;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public StoreLocation getStore() {
return store;
}
public void setStore(StoreLocation store) {
this.store = store;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EmployeeStore that = (EmployeeStore) o;
return Objects.equals(employee, that.employee) && Objects.equals(store, that.store);
}
@Override
public int hashCode() {
return Objects.hash(employee, store);
}
@Override
public String toString() {
return "EmployeeStore{" +
"employee=" + employee +
", store=" + store +
'}';
}
public static class EmployeeStoreId implements Serializable {
private Long employee;
private Long store;
public EmployeeStoreId() {
}
public EmployeeStoreId(Long employee, Long store) {
this.employee = employee;
this.store = store;
}
public Long getEmployee() {
return employee;
}
public void setEmployee(Long employee) {
this.employee = employee;
}
public Long getStore() {
return store;
}
public void setStore(Long store) {
this.store = store;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EmployeeStoreId that = (EmployeeStoreId) o;
return Objects.equals(employee, that.employee) && Objects.equals(store, that.store);
}
@Override
public int hashCode() {
return Objects.hash(employee, store);
}
@Override
public String toString() {
return "EmployeeStoreId{" +
"employee=" + employee +
", store=" + store +
'}';
}
}
}

View File

@@ -23,24 +23,24 @@ public class Pet {
@Column(nullable = false, length = 50)
private String petSpecies;
@Column(nullable = false, length = 50)
@Column(length = 50)
private String petBreed;
@Column(nullable = false)
@Column
private Integer petAge;
@Column(nullable = false, length = 20)
private String petStatus;
@Column(nullable = false, precision = 10, scale = 2)
@Column(precision = 10, scale = 2)
private BigDecimal petPrice;
@Column(length = 255)
private String imageUrl;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "customerId")
private Customer customer;
@JoinColumn(name = "ownerUserId")
private User owner;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "storeId")
@@ -57,19 +57,6 @@ public class Pet {
public Pet() {
}
public Pet(Long id, String petName, String petSpecies, String petBreed, Integer petAge, String petStatus, BigDecimal petPrice, String imageUrl, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.id = id;
this.petName = petName;
this.petSpecies = petSpecies;
this.petBreed = petBreed;
this.petAge = petAge;
this.petStatus = petStatus;
this.petPrice = petPrice;
this.imageUrl = imageUrl;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getPetId() {
return id;
}
@@ -134,6 +121,22 @@ public class Pet {
this.imageUrl = imageUrl;
}
public User getOwner() {
return owner;
}
public void setOwner(User owner) {
this.owner = owner;
}
public StoreLocation getStore() {
return store;
}
public void setStore(StoreLocation store) {
this.store = store;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
@@ -150,22 +153,6 @@ public class Pet {
this.updatedAt = updatedAt;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public StoreLocation getStore() {
return store;
}
public void setStore(StoreLocation store) {
this.store = store;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -185,13 +172,7 @@ public class Pet {
"id=" + id +
", petName='" + petName + '\'' +
", petSpecies='" + petSpecies + '\'' +
", petBreed='" + petBreed + '\'' +
", petAge=" + petAge +
", petStatus='" + petStatus + '\'' +
", petPrice=" + petPrice +
", imageUrl='" + imageUrl + '\'' +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}

View File

@@ -23,7 +23,7 @@ public class Sale {
@ManyToOne
@JoinColumn(name = "employeeId", nullable = false)
private Employee employee;
private User employee;
@ManyToOne
@JoinColumn(name = "storeId", nullable = false)
@@ -31,7 +31,7 @@ public class Sale {
@ManyToOne
@JoinColumn(name = "customerId")
private Customer customer;
private User customer;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal totalAmount;
@@ -60,21 +60,6 @@ public class Sale {
public Sale() {
}
public Sale(Long saleId, LocalDateTime saleDate, Employee employee, StoreLocation store, Customer customer, BigDecimal totalAmount, String paymentMethod, Boolean isRefund, Sale originalSale, List<SaleItem> items, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.saleId = saleId;
this.saleDate = saleDate;
this.employee = employee;
this.store = store;
this.customer = customer;
this.totalAmount = totalAmount;
this.paymentMethod = paymentMethod;
this.isRefund = isRefund;
this.originalSale = originalSale;
this.items = items;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getSaleId() {
return saleId;
}
@@ -91,11 +76,11 @@ public class Sale {
this.saleDate = saleDate;
}
public Employee getEmployee() {
public User getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
public void setEmployee(User employee) {
this.employee = employee;
}
@@ -107,11 +92,11 @@ public class Sale {
this.store = store;
}
public Customer getCustomer() {
public User getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
public void setCustomer(User customer) {
this.customer = customer;
}
@@ -189,16 +174,9 @@ public class Sale {
return "Sale{" +
"saleId=" + saleId +
", saleDate=" + saleDate +
", employee=" + employee +
", store=" + store +
", customer=" + customer +
", totalAmount=" + totalAmount +
", paymentMethod='" + paymentMethod + '\'' +
", isRefund=" + isRefund +
", originalSale=" + originalSale +
", items=" + items +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}

View File

@@ -19,9 +19,9 @@ public interface AdoptionRepository extends JpaRepository<Adoption, Long> {
"LOWER(a.pet.petName) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Adoption> searchAdoptions(@Param("q") String query, Pageable pageable);
Page<Adoption> findByCustomerCustomerId(Long customerId, Pageable pageable);
Page<Adoption> findByCustomerId(Long customerId, Pageable pageable);
@Query("SELECT a FROM Adoption a WHERE a.customer.customerId = :customerId AND (" +
@Query("SELECT a FROM Adoption a WHERE a.customer.id = :customerId AND (" +
"LOWER(a.customer.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.customer.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.pet.petName) LIKE LOWER(CONCAT('%', :q, '%')))")

View File

@@ -28,18 +28,18 @@ public interface AppointmentRepository extends JpaRepository<Appointment, Long>
"LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Appointment> searchAppointments(@Param("q") String query, Pageable pageable);
Page<Appointment> findByCustomerCustomerId(Long customerId, Pageable pageable);
Page<Appointment> findByCustomerId(Long customerId, Pageable pageable);
@Query("SELECT DISTINCT a FROM Appointment a LEFT JOIN a.pets p WHERE a.customer.customerId = :customerId AND (" +
@Query("SELECT DISTINCT a FROM Appointment a LEFT JOIN a.pets p WHERE a.customer.id = :customerId AND (" +
"LOWER(a.customer.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.customer.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.service.serviceName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')))")
Page<Appointment> searchAppointmentsByCustomer(@Param("customerId") Long customerId, @Param("q") String query, Pageable pageable);
@Query("SELECT a FROM Appointment a JOIN FETCH a.service WHERE a.employee.employeeId = :employeeId AND a.appointmentDate = :date AND LOWER(a.appointmentStatus) NOT IN ('cancelled', 'missed')")
List<Appointment> findByEmployeeEmployeeIdAndAppointmentDate(@Param("employeeId") Long employeeId, @Param("date") LocalDate date);
@Query("SELECT a FROM Appointment a JOIN FETCH a.service WHERE a.employee.id = :employeeId AND a.appointmentDate = :date AND LOWER(a.appointmentStatus) NOT IN ('cancelled', 'missed')")
List<Appointment> findByEmployeeIdAndAppointmentDate(@Param("employeeId") Long employeeId, @Param("date") LocalDate date);
@Query("SELECT a FROM Appointment a JOIN FETCH a.service WHERE a.employee.employeeId IN :employeeIds AND a.appointmentDate = :date AND LOWER(a.appointmentStatus) NOT IN ('cancelled', 'missed')")
List<Appointment> findByEmployeeEmployeeIdInAndAppointmentDate(@Param("employeeIds") List<Long> employeeIds, @Param("date") LocalDate date);
@Query("SELECT a FROM Appointment a JOIN FETCH a.service WHERE a.employee.id IN :employeeIds AND a.appointmentDate = :date AND LOWER(a.appointmentStatus) NOT IN ('cancelled', 'missed')")
List<Appointment> findByEmployeeIdInAndAppointmentDate(@Param("employeeIds") List<Long> employeeIds, @Param("date") LocalDate date);
}

View File

@@ -1,18 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.CustomerPet;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface CustomerPetRepository extends JpaRepository<CustomerPet, Long> {
List<CustomerPet> findByCustomerCustomerIdOrderByCreatedAtDesc(Long customerId);
List<CustomerPet> findByCustomerCustomerIdOrderByPetNameAsc(Long customerId);
Optional<CustomerPet> findByCustomerPetIdAndCustomerCustomerId(Long customerPetId, Long customerId);
}

View File

@@ -1,29 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Customer;
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.List;
import java.util.Optional;
@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
Optional<Customer> findByUserId(Long userId);
List<Customer> findAllByEmail(String email);
@Query("SELECT DISTINCT c FROM Customer c WHERE EXISTS (SELECT cp FROM CustomerPet cp WHERE cp.customer = c) ORDER BY c.firstName ASC, c.lastName ASC")
List<Customer> findAllWithPets();
@Query("SELECT c FROM Customer c WHERE " +
"LOWER(c.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(c.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(c.email) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"EXISTS (SELECT u FROM User u WHERE u.id = c.userId AND LOWER(COALESCE(u.phone, '')) LIKE LOWER(CONCAT('%', :q, '%')))")
Page<Customer> searchCustomers(@Param("q") String query, Pageable pageable);
}

View File

@@ -1,30 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Employee;
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.List;
import java.util.Optional;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
Optional<Employee> findByUserId(Long userId);
List<Employee> findAllByEmail(String email);
Optional<Employee> findFirstByIsActiveTrueOrderByEmployeeIdAsc();
List<Employee> findAllByIsActiveTrueOrderByEmployeeIdAsc();
@Query("SELECT e FROM Employee e WHERE " +
"LOWER(e.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(e.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(e.email) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(e.role) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"EXISTS (SELECT u FROM User u WHERE u.id = e.userId AND (" +
"LOWER(u.username) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.phone, '')) LIKE LOWER(CONCAT('%', :q, '%'))))")
Page<Employee> searchEmployees(@Param("q") String query, Pageable pageable);
}

View File

@@ -1,21 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.EmployeeStore;
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.List;
import java.util.Optional;
@Repository
public interface EmployeeStoreRepository extends JpaRepository<EmployeeStore, EmployeeStore.EmployeeStoreId> {
Optional<EmployeeStore> findByEmployeeEmployeeId(Long employeeId);
@Query("SELECT es FROM EmployeeStore es WHERE es.store.storeId = :storeId AND es.employee.isActive = true ORDER BY es.employee.employeeId ASC")
List<EmployeeStore> findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(@Param("storeId") Long storeId);
@Query("SELECT es FROM EmployeeStore es WHERE es.employee.isActive = true ORDER BY es.employee.employeeId ASC")
List<EmployeeStore> findActiveAllOrderByEmployeeEmployeeIdAsc();
}

View File

@@ -16,21 +16,21 @@ public interface PetRepository extends JpaRepository<Pet, Long> {
List<Pet> findAllByPetStatusIgnoreCaseOrderByPetNameAsc(String petStatus);
@Query("SELECT p FROM Pet p WHERE " +
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petBreed) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(p.petBreed, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
"(:species IS NULL OR LOWER(p.petSpecies) = LOWER(:species)) AND " +
"(:status IS NULL OR LOWER(p.petStatus) = LOWER(:status)) AND " +
"(:storeId IS NULL OR p.store.storeId = :storeId)")
Page<Pet> searchPets(@Param("q") String query, @Param("species") String species, @Param("status") String status, @Param("storeId") Long storeId, Pageable pageable);
@Query("SELECT p FROM Pet p WHERE LOWER(p.petStatus) = 'available' AND " +
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petBreed) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(p.petBreed, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
"(:species IS NULL OR LOWER(p.petSpecies) = LOWER(:species)) AND " +
"(:storeId IS NULL OR p.store.storeId = :storeId)")
Page<Pet> searchPublicPets(@Param("q") String query, @Param("species") String species, @Param("storeId") Long storeId, Pageable pageable);
@Query("SELECT DISTINCT p FROM Pet p LEFT JOIN Adoption a ON a.pet = p AND LOWER(a.adoptionStatus) = 'completed' WHERE " +
"(LOWER(p.petStatus) = 'available' OR a.customer.userId = :userId OR (LOWER(p.petStatus) = 'owned' AND p.customer.userId = :userId)) AND " +
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petBreed) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
"(LOWER(p.petStatus) = 'available' OR a.customer.id = :userId OR (LOWER(p.petStatus) = 'owned' AND p.owner.id = :userId)) AND " +
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(p.petBreed, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
"(:species IS NULL OR LOWER(p.petSpecies) = LOWER(:species)) AND " +
"(:status IS NULL OR LOWER(p.petStatus) = LOWER(:status))")
Page<Pet> searchCustomerVisiblePets(@Param("userId") Long userId, @Param("q") String query, @Param("species") String species, @Param("status") String status, Pageable pageable);

View File

@@ -8,6 +8,7 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
@@ -17,16 +18,24 @@ public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByPhone(String phone);
boolean existsByUsername(String username);
Page<User> findByRole(User.Role role, Pageable pageable);
List<User> findByRoleAndActiveTrue(User.Role role);
List<User> findByPrimaryStoreStoreIdAndRoleAndActiveTrue(Long storeId, User.Role role);
Optional<User> findFirstByPrimaryStoreStoreIdAndRoleAndActiveTrueOrderByIdAsc(Long storeId, User.Role role);
Optional<User> findFirstByRoleAndActiveTrueOrderByIdAsc(User.Role role);
@Query("SELECT u FROM User u WHERE " +
"LOWER(u.username) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.username, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.firstName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.lastName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.fullName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.email, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.phone, '')) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<User> searchUsers(@Param("q") String query, Pageable pageable);
@Query("SELECT u FROM User u WHERE u.role = :role AND (" +
"LOWER(u.username) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.username, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.firstName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.lastName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.fullName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.email, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.phone, '')) LIKE LOWER(CONCAT('%', :q, '%')))")

View File

@@ -4,14 +4,10 @@ import com.petshop.backend.dto.adoption.AdoptionRequest;
import com.petshop.backend.dto.adoption.AdoptionResponse;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.entity.Adoption;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.Employee;
import com.petshop.backend.entity.Pet;
import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.AdoptionRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.EmployeeRepository;
import com.petshop.backend.repository.PetRepository;
import com.petshop.backend.repository.UserRepository;
import org.springframework.data.domain.Page;
@@ -30,15 +26,11 @@ public class AdoptionService {
private final AdoptionRepository adoptionRepository;
private final PetRepository petRepository;
private final CustomerRepository customerRepository;
private final EmployeeRepository employeeRepository;
private final UserRepository userRepository;
public AdoptionService(AdoptionRepository adoptionRepository, PetRepository petRepository, CustomerRepository customerRepository, EmployeeRepository employeeRepository, UserRepository userRepository) {
public AdoptionService(AdoptionRepository adoptionRepository, PetRepository petRepository, UserRepository userRepository) {
this.adoptionRepository = adoptionRepository;
this.petRepository = petRepository;
this.customerRepository = customerRepository;
this.employeeRepository = employeeRepository;
this.userRepository = userRepository;
}
@@ -49,7 +41,7 @@ public class AdoptionService {
if (query != null && !query.trim().isEmpty()) {
adoptions = adoptionRepository.searchAdoptionsByCustomer(customerId, query, pageable);
} else {
adoptions = adoptionRepository.findByCustomerCustomerId(customerId, pageable);
adoptions = adoptionRepository.findByCustomerId(customerId, pageable);
}
} else {
if (query != null && !query.trim().isEmpty()) {
@@ -66,7 +58,7 @@ public class AdoptionService {
Adoption adoption = adoptionRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Adoption not found with id: " + id));
if (customerId != null && !adoption.getCustomer().getCustomerId().equals(customerId)) {
if (customerId != null && !adoption.getCustomer().getId().equals(customerId)) {
throw new ResourceNotFoundException("You can only view your own adoptions");
}
@@ -78,9 +70,9 @@ public class AdoptionService {
Pet pet = petRepository.findById(request.getPetId())
.orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + request.getPetId()));
Customer customer = customerRepository.findById(request.getCustomerId())
User customer = userRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
Employee employee = resolveAdoptionEmployee(request.getEmployeeId());
User employee = resolveAdoptionEmployee(request.getEmployeeId());
String adoptionStatus = normalizeAdoptionStatus(request.getAdoptionStatus());
validatePetAvailability(pet, null);
@@ -104,9 +96,9 @@ public class AdoptionService {
Pet pet = petRepository.findById(request.getPetId())
.orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + request.getPetId()));
Customer customer = customerRepository.findById(request.getCustomerId())
User customer = userRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
Employee employee = resolveAdoptionEmployee(request.getEmployeeId());
User employee = resolveAdoptionEmployee(request.getEmployeeId());
String adoptionStatus = normalizeAdoptionStatus(request.getAdoptionStatus());
validatePetAvailability(pet, adoption.getAdoptionId());
@@ -139,9 +131,9 @@ public class AdoptionService {
adoption.getAdoptionId(),
adoption.getPet().getPetId(),
adoption.getPet().getPetName(),
adoption.getCustomer().getCustomerId(),
adoption.getCustomer().getId(),
adoption.getCustomer().getFirstName() + " " + adoption.getCustomer().getLastName(),
adoption.getEmployee().getEmployeeId(),
adoption.getEmployee().getId(),
adoption.getEmployee().getFirstName() + " " + adoption.getEmployee().getLastName(),
adoption.getAdoptionDate(),
adoption.getAdoptionStatus(),
@@ -151,31 +143,22 @@ public class AdoptionService {
);
}
private Employee resolveAdoptionEmployee(Long requestedEmployeeId) {
private User resolveAdoptionEmployee(Long requestedEmployeeId) {
if (requestedEmployeeId != null) {
Employee employee = employeeRepository.findById(requestedEmployeeId)
User employee = userRepository.findById(requestedEmployeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + requestedEmployeeId));
if (!isAssignableEmployee(employee)) {
if (!isAssignableUser(employee)) {
throw new IllegalArgumentException("Selected employee is not assignable for adoption work");
}
return employee;
}
return employeeRepository.findAllByIsActiveTrueOrderByEmployeeIdAsc().stream()
.filter(this::isAssignableEmployee)
.findFirst()
return userRepository.findFirstByRoleAndActiveTrueOrderByIdAsc(User.Role.STAFF)
.orElseThrow(() -> new IllegalArgumentException("No assignable staff member is available for adoption assignment"));
}
private boolean isAssignableEmployee(Employee employee) {
Long userId = employee.getUserId();
if (userId == null || !Boolean.TRUE.equals(employee.getIsActive())) {
return false;
}
return userRepository.findById(userId)
.filter(user -> user.getRole() == User.Role.STAFF)
.filter(user -> Boolean.TRUE.equals(user.getActive()))
.isPresent();
private boolean isAssignableUser(User user) {
return user.getRole() == User.Role.STAFF && Boolean.TRUE.equals(user.getActive());
}
private String normalizeAdoptionStatus(String adoptionStatus) {

View File

@@ -1,12 +1,10 @@
package com.petshop.backend.service;
import com.petshop.backend.dto.analytics.DashboardResponse;
import com.petshop.backend.entity.Employee;
import com.petshop.backend.entity.Inventory;
import com.petshop.backend.entity.Product;
import com.petshop.backend.entity.Sale;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.EmployeeRepository;
import com.petshop.backend.repository.InventoryRepository;
import com.petshop.backend.repository.ProductRepository;
import com.petshop.backend.repository.SaleRepository;
@@ -26,14 +24,12 @@ public class AnalyticsService {
private final SaleRepository saleRepository;
private final InventoryRepository inventoryRepository;
private final ProductRepository productRepository;
private final EmployeeRepository employeeRepository;
public AnalyticsService(SaleRepository saleRepository,
InventoryRepository inventoryRepository, ProductRepository productRepository, EmployeeRepository employeeRepository) {
InventoryRepository inventoryRepository, ProductRepository productRepository) {
this.saleRepository = saleRepository;
this.inventoryRepository = inventoryRepository;
this.productRepository = productRepository;
this.employeeRepository = employeeRepository;
}
@Transactional(readOnly = true)
@@ -183,11 +179,8 @@ public class AnalyticsService {
}
if (user.getRole() == User.Role.STAFF && employeeRevenue.isEmpty()) {
Employee employee = employeeRepository.findByUserId(user.getId()).orElse(null);
if (employee != null) {
String employeeName = employee.getFirstName() + " " + employee.getLastName();
employeeRevenue.put(employeeName, BigDecimal.ZERO);
}
String employeeName = user.getFirstName() + " " + user.getLastName();
employeeRevenue.put(employeeName, BigDecimal.ZERO);
}
return employeeRevenue.entrySet().stream()
@@ -200,7 +193,7 @@ public class AnalyticsService {
return true;
}
if (user.getRole() == User.Role.STAFF) {
return sale.getEmployee() != null && sale.getEmployee().getUserId() != null && sale.getEmployee().getUserId().equals(user.getId());
return sale.getEmployee() != null && sale.getEmployee().getId() != null && sale.getEmployee().getId().equals(user.getId());
}
return false;
}

View File

@@ -4,19 +4,11 @@ import com.petshop.backend.dto.appointment.AppointmentRequest;
import com.petshop.backend.dto.appointment.AppointmentResponse;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.entity.Appointment;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.CustomerPet;
import com.petshop.backend.entity.Employee;
import com.petshop.backend.entity.EmployeeStore;
import com.petshop.backend.entity.Pet;
import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.AppointmentRepository;
import com.petshop.backend.repository.CustomerPetRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.EmployeeRepository;
import com.petshop.backend.repository.EmployeeStoreRepository;
import com.petshop.backend.repository.PetRepository;
import com.petshop.backend.repository.ServiceRepository;
import com.petshop.backend.repository.StoreRepository;
@@ -41,25 +33,17 @@ import java.util.stream.Collectors;
public class AppointmentService {
private final AppointmentRepository appointmentRepository;
private final CustomerRepository customerRepository;
private final CustomerPetRepository customerPetRepository;
private final ServiceRepository serviceRepository;
private final PetRepository petRepository;
private final StoreRepository storeRepository;
private final UserRepository userRepository;
private final EmployeeRepository employeeRepository;
private final EmployeeStoreRepository employeeStoreRepository;
public AppointmentService(AppointmentRepository appointmentRepository, CustomerRepository customerRepository, CustomerPetRepository customerPetRepository, ServiceRepository serviceRepository, PetRepository petRepository, StoreRepository storeRepository, UserRepository userRepository, EmployeeRepository employeeRepository, EmployeeStoreRepository employeeStoreRepository) {
public AppointmentService(AppointmentRepository appointmentRepository, ServiceRepository serviceRepository, PetRepository petRepository, StoreRepository storeRepository, UserRepository userRepository) {
this.appointmentRepository = appointmentRepository;
this.customerRepository = customerRepository;
this.customerPetRepository = customerPetRepository;
this.serviceRepository = serviceRepository;
this.petRepository = petRepository;
this.storeRepository = storeRepository;
this.userRepository = userRepository;
this.employeeRepository = employeeRepository;
this.employeeStoreRepository = employeeStoreRepository;
}
@Transactional(readOnly = true)
@@ -70,7 +54,7 @@ public class AppointmentService {
if (query != null && !query.trim().isEmpty()) {
appointments = appointmentRepository.searchAppointmentsByCustomer(customerId, query, pageable);
} else {
appointments = appointmentRepository.findByCustomerCustomerId(customerId, pageable);
appointments = appointmentRepository.findByCustomerId(customerId, pageable);
}
} else {
if (query != null && !query.trim().isEmpty()) {
@@ -88,7 +72,7 @@ public class AppointmentService {
Appointment appointment = appointmentRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Appointment not found with id: " + id));
if (customerId != null && !appointment.getCustomer().getCustomerId().equals(customerId)) {
if (customerId != null && !appointment.getCustomer().getId().equals(customerId)) {
throw new ResourceNotFoundException("You can only view your own appointments");
}
@@ -101,7 +85,7 @@ public class AppointmentService {
User authenticatedUser = AuthenticationHelper.getAuthenticatedUser(userRepository);
Customer customer = customerRepository.findById(request.getCustomerId())
User customer = userRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
StoreLocation store = storeRepository.findById(request.getStoreId())
@@ -111,15 +95,13 @@ public class AppointmentService {
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId()));
boolean hasPetIds = request.getPetIds() != null && !request.getPetIds().isEmpty();
boolean hasCustomerPetIds = request.getCustomerPetIds() != null && !request.getCustomerPetIds().isEmpty();
if (!hasPetIds && !hasCustomerPetIds) {
if (!hasPetIds) {
throw new IllegalArgumentException("Please specify at least one pet.");
}
Set<Pet> pets = hasPetIds ? fetchPets(request.getPetIds()) : new HashSet<>();
Set<CustomerPet> customerPets = hasCustomerPetIds ? fetchCustomerPets(request.getCustomerPetIds(), customer.getCustomerId()) : new HashSet<>();
Employee employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
Set<Pet> pets = fetchPets(request.getPetIds());
User employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
validateStoreAccess(store.getStoreId(), authenticatedUser);
validateAvailability(employee, service, request.getAppointmentDate(), request.getAppointmentTime(), null);
@@ -132,7 +114,6 @@ public class AppointmentService {
appointment.setAppointmentTime(request.getAppointmentTime());
appointment.setAppointmentStatus(request.getAppointmentStatus());
appointment.setPets(pets);
appointment.setCustomerPets(customerPets);
appointment.setEmployee(employee);
appointment = appointmentRepository.save(appointment);
@@ -148,7 +129,7 @@ public class AppointmentService {
Appointment appointment = appointmentRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Appointment not found with id: " + id));
Customer customer = customerRepository.findById(request.getCustomerId())
User customer = userRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
StoreLocation store = storeRepository.findById(request.getStoreId())
@@ -158,15 +139,13 @@ public class AppointmentService {
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId()));
boolean hasPetIds = request.getPetIds() != null && !request.getPetIds().isEmpty();
boolean hasCustomerPetIds = request.getCustomerPetIds() != null && !request.getCustomerPetIds().isEmpty();
if (!hasPetIds && !hasCustomerPetIds) {
if (!hasPetIds) {
throw new IllegalArgumentException("Please specify at least one pet.");
}
Set<Pet> pets = hasPetIds ? fetchPets(request.getPetIds()) : new HashSet<>();
Set<CustomerPet> customerPets = hasCustomerPetIds ? fetchCustomerPets(request.getCustomerPetIds(), customer.getCustomerId()) : new HashSet<>();
Employee employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
Set<Pet> pets = fetchPets(request.getPetIds());
User employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
validateStoreAccess(store.getStoreId(), authenticatedUser);
validateAvailability(employee, service, request.getAppointmentDate(), request.getAppointmentTime(), id);
@@ -178,7 +157,6 @@ public class AppointmentService {
appointment.setAppointmentTime(request.getAppointmentTime());
appointment.setAppointmentStatus(request.getAppointmentStatus());
appointment.setPets(pets);
appointment.setCustomerPets(customerPets);
appointment.setEmployee(employee);
appointment = appointmentRepository.save(appointment);
@@ -206,20 +184,17 @@ public class AppointmentService {
com.petshop.backend.entity.Service service = serviceRepository.findById(serviceId)
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + serviceId));
List<Employee> assignableEmployees = employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(storeId).stream()
.filter(es -> isAssignableEmployee(es.getEmployee()))
.map(EmployeeStore::getEmployee)
.collect(Collectors.toList());
List<User> assignableUsers = userRepository.findByPrimaryStoreStoreIdAndRoleAndActiveTrue(storeId, User.Role.STAFF);
if (assignableEmployees.isEmpty()) {
if (assignableUsers.isEmpty()) {
return List.of();
}
List<Long> employeeIds = assignableEmployees.stream().map(Employee::getEmployeeId).collect(Collectors.toList());
List<Appointment> allAppointments = appointmentRepository.findByEmployeeEmployeeIdInAndAppointmentDate(employeeIds, date);
List<Long> employeeIds = assignableUsers.stream().map(User::getId).collect(Collectors.toList());
List<Appointment> allAppointments = appointmentRepository.findByEmployeeIdInAndAppointmentDate(employeeIds, date);
java.util.Map<Long, List<Appointment>> appointmentsByEmployee = allAppointments.stream()
.collect(Collectors.groupingBy(a -> a.getEmployee().getEmployeeId()));
.collect(Collectors.groupingBy(a -> a.getEmployee().getId()));
List<String> availableSlots = new ArrayList<>();
LocalTime startTime = LocalTime.of(9, 0);
@@ -229,8 +204,8 @@ public class AppointmentService {
LocalTime currentTime = startTime;
while (!currentTime.isAfter(latestStart)) {
final LocalTime slotTime = currentTime;
boolean anyEmployeeAvailable = assignableEmployees.stream().anyMatch(emp -> {
List<Appointment> empAppointments = appointmentsByEmployee.getOrDefault(emp.getEmployeeId(), List.of());
boolean anyEmployeeAvailable = assignableUsers.stream().anyMatch(emp -> {
List<Appointment> empAppointments = appointmentsByEmployee.getOrDefault(emp.getId(), List.of());
return isSlotAvailable(empAppointments, service, slotTime, null);
});
@@ -262,20 +237,6 @@ public class AppointmentService {
return pets;
}
private Set<CustomerPet> fetchCustomerPets(List<Long> customerPetIds, Long customerId) {
Set<CustomerPet> customerPets = new HashSet<>();
for (Long customerPetId : customerPetIds) {
CustomerPet customerPet = customerPetRepository.findById(customerPetId)
.orElseThrow(() -> new ResourceNotFoundException("Customer pet not found with id: " + customerPetId));
if (!customerPet.getCustomer().getCustomerId().equals(customerId)) {
throw new IllegalArgumentException("Selected pet does not belong to the selected customer");
}
customerPets.add(customerPet);
}
return customerPets;
}
private AppointmentResponse mapToResponse(Appointment appointment) {
List<String> petNames = appointment.getPets().stream()
.map(Pet::getPetName)
@@ -285,17 +246,9 @@ public class AppointmentService {
.map(Pet::getPetId)
.collect(Collectors.toList());
List<String> customerPetNames = appointment.getCustomerPets().stream()
.map(CustomerPet::getPetName)
.collect(Collectors.toList());
List<Long> customerPetIds = appointment.getCustomerPets().stream()
.map(CustomerPet::getCustomerPetId)
.collect(Collectors.toList());
AppointmentResponse response = new AppointmentResponse();
response.setAppointmentId(appointment.getAppointmentId());
response.setCustomerId(appointment.getCustomer().getCustomerId());
response.setCustomerId(appointment.getCustomer().getId());
response.setCustomerName(appointment.getCustomer().getFirstName() + " " + appointment.getCustomer().getLastName());
response.setStoreId(appointment.getStore().getStoreId());
response.setStoreName(appointment.getStore().getStoreName());
@@ -304,54 +257,38 @@ public class AppointmentService {
response.setAppointmentDate(appointment.getAppointmentDate());
response.setAppointmentTime(appointment.getAppointmentTime());
response.setAppointmentStatus(appointment.getAppointmentStatus());
response.setEmployeeId(appointment.getEmployee().getEmployeeId());
response.setEmployeeId(appointment.getEmployee().getId());
response.setEmployeeName(appointment.getEmployee().getFirstName() + " " + appointment.getEmployee().getLastName());
response.setPetNames(petNames);
response.setPetIds(petIds);
response.setCustomerPetNames(customerPetNames);
response.setCustomerPetIds(customerPetIds);
response.setCreatedAt(appointment.getCreatedAt());
response.setUpdatedAt(appointment.getUpdatedAt());
return response;
}
private Employee resolveAppointmentEmployee(Long requestedEmployeeId, Long storeId) {
List<EmployeeStore> assignableEmployees = employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(storeId).stream()
.filter(es -> isAssignableEmployee(es.getEmployee()))
.collect(Collectors.toList());
private User resolveAppointmentEmployee(Long requestedEmployeeId, Long storeId) {
List<User> assignableUsers = userRepository.findByPrimaryStoreStoreIdAndRoleAndActiveTrue(storeId, User.Role.STAFF);
if (requestedEmployeeId != null) {
Employee employee = employeeRepository.findById(requestedEmployeeId)
User employee = userRepository.findById(requestedEmployeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + requestedEmployeeId));
boolean assignedToStore = assignableEmployees.stream()
.anyMatch(es -> es.getEmployee().getEmployeeId().equals(requestedEmployeeId));
boolean assignedToStore = assignableUsers.stream()
.anyMatch(u -> u.getId().equals(requestedEmployeeId));
if (!assignedToStore) {
throw new IllegalArgumentException("Selected employee is not assignable for the selected store");
}
return employee;
}
return assignableEmployees.stream()
.map(EmployeeStore::getEmployee)
return assignableUsers.stream()
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No assignable staff member is assigned to the selected store"));
}
private boolean isAssignableEmployee(Employee employee) {
Long userId = employee.getUserId();
if (userId == null || !Boolean.TRUE.equals(employee.getIsActive())) {
return false;
}
return userRepository.findById(userId)
.filter(user -> user.getRole() == User.Role.STAFF)
.filter(user -> Boolean.TRUE.equals(user.getActive()))
.isPresent();
}
private void validateAvailability(Employee employee, com.petshop.backend.entity.Service service, LocalDate date, LocalTime time, Long appointmentIdToIgnore) {
private void validateAvailability(User employee, com.petshop.backend.entity.Service service, LocalDate date, LocalTime time, Long appointmentIdToIgnore) {
List<Appointment> existingAppointments = appointmentRepository
.findByEmployeeEmployeeIdAndAppointmentDate(employee.getEmployeeId(), date);
.findByEmployeeIdAndAppointmentDate(employee.getId(), date);
if (!isSlotAvailable(existingAppointments, service, time, appointmentIdToIgnore)) {
throw new IllegalArgumentException("The selected employee is already booked for this time slot");
}
@@ -377,11 +314,7 @@ public class AppointmentService {
return;
}
Employee employee = AuthenticationHelper.getAuthenticatedEmployee(userRepository, employeeRepository);
EmployeeStore employeeStore = employeeStoreRepository.findByEmployeeEmployeeId(employee.getEmployeeId())
.orElseThrow(() -> new AccessDeniedException("Authenticated staff member is not assigned to a store"));
if (!employeeStore.getStore().getStoreId().equals(requestedStoreId)) {
if (user.getPrimaryStore() == null || !user.getPrimaryStore().getStoreId().equals(requestedStoreId)) {
throw new AccessDeniedException("Staff can only manage appointments for their assigned store");
}
}

View File

@@ -3,11 +3,9 @@ package com.petshop.backend.service;
import com.petshop.backend.dto.chat.ConversationResponse;
import com.petshop.backend.dto.chat.MessageResponse;
import com.petshop.backend.entity.Conversation;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.ConversationRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.MessageRepository;
import com.petshop.backend.repository.UserRepository;
import org.springframework.messaging.simp.SimpMessagingTemplate;
@@ -21,14 +19,12 @@ public class ChatRealtimeService {
private final SimpMessagingTemplate messagingTemplate;
private final ConversationRepository conversationRepository;
private final MessageRepository messageRepository;
private final CustomerRepository customerRepository;
private final UserRepository userRepository;
public ChatRealtimeService(SimpMessagingTemplate messagingTemplate, ConversationRepository conversationRepository, MessageRepository messageRepository, CustomerRepository customerRepository, UserRepository userRepository) {
public ChatRealtimeService(SimpMessagingTemplate messagingTemplate, ConversationRepository conversationRepository, MessageRepository messageRepository, UserRepository userRepository) {
this.messagingTemplate = messagingTemplate;
this.conversationRepository = conversationRepository;
this.messageRepository = messageRepository;
this.customerRepository = customerRepository;
this.userRepository = userRepository;
}
@@ -54,13 +50,11 @@ public class ChatRealtimeService {
}
private void sendConversationToCustomerQueue(ConversationResponse conversation) {
Customer customer = customerRepository.findById(conversation.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found"));
if (customer.getUserId() == null) {
User customerUser = userRepository.findById(conversation.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
if (customerUser.getUsername() == null) {
return;
}
User customerUser = userRepository.findById(customer.getUserId())
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
messagingTemplate.convertAndSendToUser(customerUser.getUsername(), "/queue/chat/conversations", conversation);
}

View File

@@ -6,12 +6,10 @@ import com.petshop.backend.dto.chat.MessageRequest;
import com.petshop.backend.dto.chat.MessageResponse;
import com.petshop.backend.dto.chat.UpdateConversationRequest;
import com.petshop.backend.entity.Conversation;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.Message;
import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.ConversationRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.MessageRepository;
import com.petshop.backend.repository.UserRepository;
import org.springframework.security.access.AccessDeniedException;
@@ -28,16 +26,13 @@ public class ChatService {
private final ConversationRepository conversationRepository;
private final MessageRepository messageRepository;
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public ChatService(ConversationRepository conversationRepository,
MessageRepository messageRepository,
UserRepository userRepository,
CustomerRepository customerRepository) {
UserRepository userRepository) {
this.conversationRepository = conversationRepository;
this.messageRepository = messageRepository;
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
@Transactional
@@ -49,11 +44,8 @@ public class ChatService {
throw new AccessDeniedException("Only customers can start new conversations");
}
Customer customer = customerRepository.findByUserId(userId)
.orElseThrow(() -> new ResourceNotFoundException("Customer record not found for user"));
Conversation conversation = new Conversation();
conversation.setCustomerId(customer.getCustomerId());
conversation.setCustomerId(userId);
conversation.setStatus(Conversation.ConversationStatus.OPEN);
conversation.setMode(Conversation.ConversationMode.AUTOMATED);
conversation = conversationRepository.save(conversation);
@@ -72,9 +64,7 @@ public class ChatService {
List<Conversation> conversations;
if (role == User.Role.CUSTOMER) {
Customer customer = customerRepository.findByUserId(userId)
.orElseThrow(() -> new ResourceNotFoundException("Customer record not found for user"));
conversations = conversationRepository.findByCustomerId(customer.getCustomerId());
conversations = conversationRepository.findByCustomerId(userId);
} else if (role == User.Role.STAFF) {
List<Conversation> assignedToMe = conversationRepository.findByStaffId(userId);
List<Conversation> unassigned = conversationRepository.findByStaffIdIsNull();
@@ -225,9 +215,7 @@ public class ChatService {
}
if (role == User.Role.CUSTOMER) {
Customer customer = customerRepository.findByUserId(userId)
.orElseThrow(() -> new ResourceNotFoundException("Customer record not found for user"));
return conversation.getCustomerId().equals(customer.getCustomerId());
return conversation.getCustomerId().equals(userId);
}
if (role == User.Role.STAFF) {

View File

@@ -1,163 +0,0 @@
package com.petshop.backend.service;
import com.petshop.backend.dto.customerpet.CustomerPetRequest;
import com.petshop.backend.dto.customerpet.CustomerPetResponse;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.CustomerPet;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.CustomerPetRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.util.AuthenticationHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
@Service
public class CustomerPetService {
private final CustomerPetRepository customerPetRepository;
private final CustomerRepository customerRepository;
private final UserRepository userRepository;
private final CatalogImageStorageService catalogImageStorageService;
public CustomerPetService(CustomerPetRepository customerPetRepository,
CustomerRepository customerRepository,
UserRepository userRepository,
CatalogImageStorageService catalogImageStorageService) {
this.customerPetRepository = customerPetRepository;
this.customerRepository = customerRepository;
this.userRepository = userRepository;
this.catalogImageStorageService = catalogImageStorageService;
}
@Transactional(readOnly = true)
public List<CustomerPetResponse> getMyPets() {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
return customerPetRepository.findByCustomerCustomerIdOrderByCreatedAtDesc(customer.getCustomerId())
.stream()
.map(this::mapToResponse)
.collect(Collectors.toList());
}
@Transactional
public CustomerPetResponse createPet(CustomerPetRequest request) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
CustomerPet pet = new CustomerPet();
pet.setCustomer(customer);
pet.setPetName(request.getPetName());
pet.setSpecies(request.getSpecies());
pet.setBreed(request.getBreed());
pet = customerPetRepository.save(pet);
return mapToResponse(pet);
}
@Transactional
public CustomerPetResponse updatePet(Long id, CustomerPetRequest request) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
CustomerPet pet = customerPetRepository.findByCustomerPetIdAndCustomerCustomerId(id, customer.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + id));
pet.setPetName(request.getPetName());
pet.setSpecies(request.getSpecies());
pet.setBreed(request.getBreed());
pet = customerPetRepository.save(pet);
return mapToResponse(pet);
}
@Transactional
public void deletePet(Long id) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
CustomerPet pet = customerPetRepository.findByCustomerPetIdAndCustomerCustomerId(id, customer.getCustomerId()).orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + id));
deleteStoredImageIfPresent(pet.getImageUrl());
customerPetRepository.delete(pet);
}
@Transactional
public CustomerPetResponse uploadImage(Long id, MultipartFile file) throws IOException {
validateImageFile(file);
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
CustomerPet pet = customerPetRepository.findByCustomerPetIdAndCustomerCustomerId(id, customer.getCustomerId()).orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + id));
deleteStoredImageIfPresent(pet.getImageUrl());
pet.setImageUrl(catalogImageStorageService.storePetImage(file));
return mapToResponse(customerPetRepository.save(pet));
}
@Transactional
public CustomerPetResponse deleteImage(Long id) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
CustomerPet pet = customerPetRepository.findByCustomerPetIdAndCustomerCustomerId(id, customer.getCustomerId()).orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + id));
deleteStoredImageIfPresent(pet.getImageUrl());
pet.setImageUrl(null);
return mapToResponse(customerPetRepository.save(pet));
}
private CustomerPetResponse mapToResponse(CustomerPet pet) {
return new CustomerPetResponse(
pet.getCustomerPetId(),
pet.getCustomer().getCustomerId(),
pet.getPetName(),
pet.getSpecies(),
pet.getBreed(),
pet.getImageUrl() != null && !pet.getImageUrl().isBlank()
? "/api/v1/my-pets/" + pet.getCustomerPetId() + "/image"
: null,
pet.getCreatedAt(),
pet.getUpdatedAt()
);
}
private void validateImageFile(MultipartFile file) {
if (file == null || file.isEmpty()) {
throw new IllegalArgumentException("Please select an image to upload");
}
if (file.getSize() > 5 * 1024 * 1024) {
throw new IllegalArgumentException("Image file size must be less than 5MB");
}
String contentType = file.getContentType();
if (contentType == null) {
throw new IllegalArgumentException("Only JPG, PNG, and GIF images are allowed");
}
String normalized = contentType.toLowerCase(Locale.ROOT);
if (!normalized.equals("image/jpeg") && !normalized.equals("image/png") && !normalized.equals("image/gif")) {
throw new IllegalArgumentException("Only JPG, PNG, and GIF images are allowed");
}
}
private void deleteStoredImageIfPresent(String storedImagePath) {
if (storedImagePath == null || storedImagePath.isBlank()) {
return;
}
try {
catalogImageStorageService.deletePetImage(storedImagePath);
}
catch (IOException ignored) {
}
}
}

View File

@@ -1,158 +0,0 @@
package com.petshop.backend.service;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.dto.customer.CustomerRequest;
import com.petshop.backend.dto.customer.CustomerResponse;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.UserRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
import static org.springframework.http.HttpStatus.CONFLICT;
@Service
public class CustomerService {
private static final String TEMP_PASSWORD = "TempPass123!";
private final CustomerRepository customerRepository;
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final UserBusinessLinkageService userBusinessLinkageService;
public CustomerService(CustomerRepository customerRepository, UserRepository userRepository, PasswordEncoder passwordEncoder, UserBusinessLinkageService userBusinessLinkageService) {
this.customerRepository = customerRepository;
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.userBusinessLinkageService = userBusinessLinkageService;
}
public Page<CustomerResponse> getAllCustomers(String query, Pageable pageable) {
Page<Customer> customers;
if (query != null && !query.trim().isEmpty()) {
customers = customerRepository.searchCustomers(query, pageable);
} else {
customers = customerRepository.findAll(pageable);
}
return customers.map(this::mapToResponse);
}
public CustomerResponse getCustomerById(Long id) {
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + id));
return mapToResponse(customer);
}
@Transactional
public CustomerResponse createCustomer(CustomerRequest request) {
ensureEmailAvailable(request.getEmail(), null);
Customer customer = new Customer();
customer.setFirstName(request.getFirstName());
customer.setLastName(request.getLastName());
customer.setEmail(request.getEmail());
customer = customerRepository.save(customer);
User user = createLinkedUser(customer);
Customer linkedCustomer = userBusinessLinkageService.ensureLinkedCustomer(user);
syncLinkedUser(linkedCustomer);
return mapToResponse(linkedCustomer);
}
@Transactional
public CustomerResponse updateCustomer(Long id, CustomerRequest request) {
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + id));
ensureEmailAvailable(request.getEmail(), customer.getUserId());
customer.setFirstName(request.getFirstName());
customer.setLastName(request.getLastName());
customer.setEmail(request.getEmail());
customer = customerRepository.save(customer);
syncLinkedUser(customer);
return mapToResponse(customer);
}
@Transactional
public void deleteCustomer(Long id) {
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + id));
if (customer.getUserId() != null && userRepository.existsById(customer.getUserId())) {
userRepository.deleteById(customer.getUserId());
return;
}
customerRepository.deleteById(id);
}
@Transactional
public void bulkDeleteCustomers(BulkDeleteRequest request) {
customerRepository.deleteAllById(request.getIds());
}
private CustomerResponse mapToResponse(Customer customer) {
return new CustomerResponse(
customer.getCustomerId(),
customer.getFirstName(),
customer.getLastName(),
customer.getEmail(),
customer.getCreatedAt(),
customer.getUpdatedAt()
);
}
private void syncLinkedUser(Customer customer) {
if (customer.getUserId() == null) {
return;
}
userRepository.findById(customer.getUserId()).ifPresent(user -> {
user.setEmail(customer.getEmail());
user.setFullName((customer.getFirstName() + " " + customer.getLastName()).trim());
userRepository.save(user);
});
}
private User createLinkedUser(Customer customer) {
User user = new User();
user.setUsername(generateUsername(customer));
user.setPassword(passwordEncoder.encode(TEMP_PASSWORD));
user.setEmail(customer.getEmail());
user.setFullName((customer.getFirstName() + " " + customer.getLastName()).trim());
user.setPhone(generatePhone(customer));
user.setRole(User.Role.CUSTOMER);
user.setActive(false);
user.setTokenVersion(0);
return userRepository.save(user);
}
private String generateUsername(Customer customer) {
return "customer_" + customer.getCustomerId();
}
private String generatePhone(Customer customer) {
return String.format("200-000-%04d", customer.getCustomerId());
}
private void ensureEmailAvailable(String email, Long currentUserId) {
if (email == null || email.isBlank()) {
return;
}
userRepository.findByEmail(email).ifPresent(existing -> {
if (currentUserId == null || !existing.getId().equals(currentUserId)) {
throw new ResponseStatusException(CONFLICT, "Email already exists");
}
});
}
}

View File

@@ -1,183 +0,0 @@
package com.petshop.backend.service;
import com.petshop.backend.dto.employee.EmployeeRequest;
import com.petshop.backend.dto.employee.EmployeeResponse;
import com.petshop.backend.entity.Employee;
import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.EmployeeRepository;
import com.petshop.backend.repository.UserRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
import static org.springframework.http.HttpStatus.CONFLICT;
@Service
public class EmployeeService {
private final EmployeeRepository employeeRepository;
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final UserBusinessLinkageService userBusinessLinkageService;
public EmployeeService(EmployeeRepository employeeRepository, UserRepository userRepository, PasswordEncoder passwordEncoder, UserBusinessLinkageService userBusinessLinkageService) {
this.employeeRepository = employeeRepository;
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.userBusinessLinkageService = userBusinessLinkageService;
}
public Page<EmployeeResponse> getAllEmployees(String query, Pageable pageable) {
Page<Employee> employees;
if (query != null && !query.trim().isEmpty()) {
employees = employeeRepository.searchEmployees(query, pageable);
} else {
employees = employeeRepository.findAll(pageable);
}
return employees.map(this::mapToResponse);
}
public EmployeeResponse getEmployeeById(Long id) {
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + id));
return mapToResponse(employee);
}
@Transactional
public EmployeeResponse createEmployee(EmployeeRequest request) {
validateRole(request.getRole());
if (request.getPassword() == null || request.getPassword().trim().length() < 6) {
throw new IllegalArgumentException("Password must be at least 6 characters");
}
if (userRepository.findByUsername(request.getUsername()).isPresent()) {
throw new ResponseStatusException(CONFLICT, "Username already exists");
}
if (request.getEmail() != null && userRepository.findByEmail(request.getEmail()).isPresent()) {
throw new ResponseStatusException(CONFLICT, "Email already exists");
}
String phone = trimToNull(request.getPhone());
if (phone != null && userRepository.findByPhone(phone).isPresent()) {
throw new ResponseStatusException(CONFLICT, "Phone already exists");
}
User user = new User();
user.setUsername(request.getUsername());
user.setPassword(passwordEncoder.encode(request.getPassword()));
user.setFullName(fullName(request));
user.setEmail(request.getEmail());
user.setPhone(phone);
user.setRole(request.getRole());
user.setActive(request.getActive() != null ? request.getActive() : true);
user = userRepository.save(user);
Employee employee = userBusinessLinkageService.ensureLinkedEmployee(user);
return mapToResponse(employee, user);
}
@Transactional
public EmployeeResponse updateEmployee(Long id, EmployeeRequest request) {
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + id));
User user = requireLinkedUser(employee);
validateRole(request.getRole());
if (!user.getUsername().equals(request.getUsername()) && userRepository.findByUsername(request.getUsername()).isPresent()) {
throw new ResponseStatusException(CONFLICT, "Username already exists");
}
if (!java.util.Objects.equals(user.getEmail(), request.getEmail()) && request.getEmail() != null && userRepository.findByEmail(request.getEmail()).isPresent()) {
throw new ResponseStatusException(CONFLICT, "Email already exists");
}
String phone = trimToNull(request.getPhone());
Long currentUserId = user.getId();
if (!java.util.Objects.equals(user.getPhone(), phone)) {
userRepository.findByPhone(phone)
.filter(existing -> !existing.getId().equals(currentUserId))
.ifPresent(existing -> { throw new ResponseStatusException(CONFLICT, "Phone already exists"); });
}
user.setUsername(request.getUsername());
if (request.getPassword() != null && !request.getPassword().trim().isEmpty()) {
user.setPassword(passwordEncoder.encode(request.getPassword()));
user.setTokenVersion(user.getTokenVersion() + 1);
}
user.setEmail(request.getEmail());
user.setPhone(phone);
user.setFullName(fullName(request));
user.setRole(request.getRole());
user.setActive(request.getActive() != null ? request.getActive() : true);
user = userRepository.save(user);
employee = userBusinessLinkageService.ensureLinkedEmployee(user);
return mapToResponse(employee, user);
}
@Transactional
public void deleteEmployee(Long id) {
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + id));
if (employee.getUserId() != null && userRepository.existsById(employee.getUserId())) {
userRepository.deleteById(employee.getUserId());
return;
}
employeeRepository.deleteById(id);
}
private EmployeeResponse mapToResponse(Employee employee) {
User user = employee.getUserId() == null ? null : userRepository.findById(employee.getUserId()).orElse(null);
return mapToResponse(employee, user);
}
private EmployeeResponse mapToResponse(Employee employee, User user) {
EmployeeResponse response = new EmployeeResponse();
response.setEmployeeId(employee.getEmployeeId());
response.setUserId(user != null ? user.getId() : employee.getUserId());
response.setUsername(user != null ? user.getUsername() : null);
response.setFirstName(employee.getFirstName());
response.setLastName(employee.getLastName());
response.setFullName(user != null ? user.getFullName() : fullName(employee));
response.setEmail(user != null ? user.getEmail() : employee.getEmail());
response.setPhone(user != null ? user.getPhone() : null);
response.setRole(user != null ? user.getRole().name() : normalizeRole(employee.getRole()));
response.setActive(user != null ? user.getActive() : employee.getIsActive());
response.setCreatedAt(employee.getCreatedAt());
response.setUpdatedAt(employee.getUpdatedAt());
return response;
}
private User requireLinkedUser(Employee employee) {
if (employee.getUserId() == null) {
throw new ResourceNotFoundException("Employee user account not found");
}
return userRepository.findById(employee.getUserId())
.orElseThrow(() -> new ResourceNotFoundException("Employee user account not found"));
}
private void validateRole(User.Role role) {
if (role != User.Role.STAFF && role != User.Role.ADMIN) {
throw new IllegalArgumentException("Employee role must be STAFF or ADMIN");
}
}
private String fullName(EmployeeRequest request) {
return (request.getFirstName().trim() + " " + request.getLastName().trim()).trim();
}
private String fullName(Employee employee) {
return (employee.getFirstName().trim() + " " + employee.getLastName().trim()).trim();
}
private String normalizeRole(String role) {
return role == null ? null : role.trim().toUpperCase(java.util.Locale.ROOT);
}
private String trimToNull(String value) {
if (value == null) {
return null;
}
String trimmed = value.trim();
return trimmed.isEmpty() ? null : trimmed;
}
}

View File

@@ -4,16 +4,15 @@ import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.dto.pet.PetRequest;
import com.petshop.backend.dto.pet.PetResponse;
import com.petshop.backend.entity.Adoption;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.Pet;
import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.security.AppPrincipal;
import com.petshop.backend.repository.AdoptionRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.PetRepository;
import com.petshop.backend.repository.StoreRepository;
import com.petshop.backend.repository.UserRepository;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.data.domain.Page;
@@ -33,14 +32,14 @@ public class PetService {
private final PetRepository petRepository;
private final AdoptionRepository adoptionRepository;
private final CustomerRepository customerRepository;
private final UserRepository userRepository;
private final StoreRepository storeRepository;
private final CatalogImageStorageService catalogImageStorageService;
public PetService(PetRepository petRepository, AdoptionRepository adoptionRepository, CustomerRepository customerRepository, StoreRepository storeRepository, CatalogImageStorageService catalogImageStorageService) {
public PetService(PetRepository petRepository, AdoptionRepository adoptionRepository, UserRepository userRepository, StoreRepository storeRepository, CatalogImageStorageService catalogImageStorageService) {
this.petRepository = petRepository;
this.adoptionRepository = adoptionRepository;
this.customerRepository = customerRepository;
this.userRepository = userRepository;
this.storeRepository = storeRepository;
this.catalogImageStorageService = catalogImageStorageService;
}
@@ -188,7 +187,7 @@ public class PetService {
}
return adoptionRepository.findFirstByPet_IdAndAdoptionStatusOrderByAdoptionDateDesc(pet.getPetId(), "Completed")
.map(Adoption::getCustomer)
.map(customer -> userId.equals(customer.getUserId()))
.map(customer -> userId.equals(customer.getId()))
.orElse(false);
}
@@ -257,7 +256,7 @@ public class PetService {
}
private PetResponse mapToResponse(Pet pet) {
Customer customer = pet.getCustomer();
User owner = pet.getOwner();
StoreLocation store = pet.getStore();
return new PetResponse(
pet.getPetId(),
@@ -270,8 +269,8 @@ public class PetService {
pet.getImageUrl() != null && !pet.getImageUrl().isBlank() ? "/api/v1/pets/" + pet.getPetId() + "/image" : null,
pet.getCreatedAt(),
pet.getUpdatedAt(),
customer != null ? customer.getCustomerId() : null,
customer != null ? customer.getFirstName() + " " + customer.getLastName() : null,
owner != null ? owner.getId() : null,
owner != null ? owner.getFirstName() + " " + owner.getLastName() : null,
store != null ? store.getStoreId() : null,
store != null ? store.getStoreName() : null
);
@@ -280,11 +279,11 @@ public class PetService {
private void applyOwnerAndStore(Pet pet, PetRequest request) {
if ("owned".equalsIgnoreCase(request.getPetStatus())) {
if (request.getCustomerId() != null) {
Customer customer = customerRepository.findById(request.getCustomerId())
User owner = userRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
pet.setCustomer(customer);
pet.setOwner(owner);
} else {
pet.setCustomer(null);
pet.setOwner(null);
}
pet.setStore(null);
} else if ("available".equalsIgnoreCase(request.getPetStatus()) || "unadopted".equalsIgnoreCase(request.getPetStatus())) {
@@ -295,14 +294,14 @@ public class PetService {
} else {
pet.setStore(null);
}
pet.setCustomer(null);
pet.setOwner(null);
} else {
if (request.getCustomerId() != null) {
Customer customer = customerRepository.findById(request.getCustomerId())
User owner = userRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
pet.setCustomer(customer);
pet.setOwner(owner);
} else {
pet.setCustomer(null);
pet.setOwner(null);
}
if (request.getStoreId() != null) {
StoreLocation store = storeRepository.findById(request.getStoreId())
@@ -318,8 +317,8 @@ public class PetService {
if (!"owned".equalsIgnoreCase(normalizeStatus(pet.getPetStatus()))) {
return false;
}
Customer customer = pet.getCustomer();
return customer != null && userId.equals(customer.getUserId());
User owner = pet.getOwner();
return owner != null && userId.equals(owner.getId());
}
public record ImagePayload(Resource resource, MediaType mediaType) {

View File

@@ -46,13 +46,13 @@ public class RefundService {
throw new RuntimeException("Sale has no associated customer");
}
if (customerId != null && !sale.getCustomer().getCustomerId().equals(customerId)) {
if (customerId != null && !sale.getCustomer().getId().equals(customerId)) {
throw new RuntimeException("You can only create refunds for your own purchases");
}
Refund refund = new Refund();
refund.setSaleId(sale.getSaleId());
refund.setCustomerId(sale.getCustomer().getCustomerId());
refund.setCustomerId(sale.getCustomer().getId());
refund.setReason(request.getReason());
refund.setStatus(Refund.RefundStatus.PENDING);

View File

@@ -24,20 +24,14 @@ public class SaleService {
private final ProductRepository productRepository;
private final StoreRepository storeRepository;
private final InventoryRepository inventoryRepository;
private final EmployeeRepository employeeRepository;
private final EmployeeStoreRepository employeeStoreRepository;
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public SaleService(SaleRepository saleRepository, ProductRepository productRepository, StoreRepository storeRepository, InventoryRepository inventoryRepository, EmployeeRepository employeeRepository, EmployeeStoreRepository employeeStoreRepository, UserRepository userRepository, CustomerRepository customerRepository) {
public SaleService(SaleRepository saleRepository, ProductRepository productRepository, StoreRepository storeRepository, InventoryRepository inventoryRepository, UserRepository userRepository) {
this.saleRepository = saleRepository;
this.productRepository = productRepository;
this.storeRepository = storeRepository;
this.inventoryRepository = inventoryRepository;
this.employeeRepository = employeeRepository;
this.employeeStoreRepository = employeeStoreRepository;
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
@Transactional(readOnly = true)
@@ -60,18 +54,16 @@ public class SaleService {
@Transactional
public SaleResponse createSale(SaleRequest request) {
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
Employee employee = AuthenticationHelper.getAuthenticatedEmployee(userRepository, employeeRepository);
Long employeeStoreId = employeeStoreRepository.findByEmployeeEmployeeId(employee.getEmployeeId())
.orElseThrow(() -> new BusinessException("Authenticated staff member is not assigned to a store"))
.getStore()
.getStoreId();
User employee = AuthenticationHelper.getAuthenticatedUser(userRepository);
StoreLocation store = storeRepository.findById(request.getStoreId())
.orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + request.getStoreId()));
if (user.getRole() == User.Role.STAFF && !employeeStoreId.equals(store.getStoreId())) {
throw new BusinessException("Staff can only create sales for their assigned store");
if (employee.getRole() == User.Role.STAFF) {
Long assignedStoreId = employee.getPrimaryStore() != null ? employee.getPrimaryStore().getStoreId() : null;
if (!store.getStoreId().equals(assignedStoreId)) {
throw new BusinessException("Staff can only create sales for their assigned store");
}
}
Sale sale = new Sale();
@@ -82,7 +74,7 @@ public class SaleService {
sale.setIsRefund(request.getIsRefund() != null ? request.getIsRefund() : false);
if (request.getCustomerId() != null) {
Customer customer = customerRepository.findById(request.getCustomerId())
User customer = userRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
sale.setCustomer(customer);
}
@@ -185,7 +177,7 @@ public class SaleService {
SaleResponse response = new SaleResponse();
response.setSaleId(sale.getSaleId());
response.setSaleDate(sale.getSaleDate());
response.setEmployeeId(sale.getEmployee().getEmployeeId());
response.setEmployeeId(sale.getEmployee().getId());
response.setEmployeeName(sale.getEmployee().getFirstName() + " " + sale.getEmployee().getLastName());
if (sale.getStore() != null) {

View File

@@ -1,34 +0,0 @@
package com.petshop.backend.service;
import com.petshop.backend.entity.Employee;
import com.petshop.backend.entity.EmployeeStore;
import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.EmployeeStoreRepository;
import com.petshop.backend.repository.StoreRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class StoreAssignmentService {
private final EmployeeStoreRepository employeeStoreRepository;
private final StoreRepository storeRepository;
public StoreAssignmentService(EmployeeStoreRepository employeeStoreRepository, StoreRepository storeRepository) {
this.employeeStoreRepository = employeeStoreRepository;
this.storeRepository = storeRepository;
}
@Transactional
public void assignStoreIfMissing(Employee employee, Long storeId) {
if (employeeStoreRepository.findByEmployeeEmployeeId(employee.getEmployeeId()).isPresent()) {
return;
}
StoreLocation store = storeRepository.findById(storeId)
.orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + storeId));
employeeStoreRepository.save(new EmployeeStore(employee, store));
}
}

View File

@@ -1,152 +0,0 @@
package com.petshop.backend.service;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.Employee;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class UserBusinessLinkageService {
private final EmployeeRepository employeeRepository;
private final CustomerRepository customerRepository;
@Autowired
public UserBusinessLinkageService(EmployeeRepository employeeRepository, CustomerRepository customerRepository) {
this.employeeRepository = employeeRepository;
this.customerRepository = customerRepository;
}
@Transactional
public Employee ensureLinkedEmployee(User user) {
if (user.getId() != null) {
var existing = employeeRepository.findByUserId(user.getId());
if (existing.isPresent()) {
return syncEmployee(existing.get(), user);
}
}
List<Employee> emailMatches = employeeRepository.findAllByEmail(user.getEmail());
if (emailMatches.size() == 1) {
Employee employee = emailMatches.get(0);
if (employee.getUserId() == null) {
employee.setUserId(user.getId());
return syncEmployee(employee, user);
}
}
Employee newEmployee = new Employee();
newEmployee.setUserId(user.getId());
newEmployee.setEmail(user.getEmail());
String[] nameParts = splitFullName(user.getFullName());
newEmployee.setFirstName(nameParts[0]);
newEmployee.setLastName(nameParts[1]);
newEmployee.setIsActive(true);
if (user.getRole() == User.Role.ADMIN) {
newEmployee.setRole("Manager");
} else if (user.getRole() == User.Role.STAFF) {
newEmployee.setRole("Staff");
} else {
newEmployee.setRole("Staff");
}
return syncEmployee(newEmployee, user);
}
@Transactional
public Customer ensureLinkedCustomer(User user) {
if (user.getId() != null) {
var existing = customerRepository.findByUserId(user.getId());
if (existing.isPresent()) {
return syncCustomer(existing.get(), user);
}
}
List<Customer> emailMatches = customerRepository.findAllByEmail(user.getEmail());
if (emailMatches.size() == 1) {
Customer customer = emailMatches.get(0);
if (customer.getUserId() == null) {
customer.setUserId(user.getId());
return syncCustomer(customer, user);
}
}
Customer newCustomer = new Customer();
newCustomer.setUserId(user.getId());
newCustomer.setEmail(user.getEmail());
String[] nameParts = splitFullName(user.getFullName());
newCustomer.setFirstName(nameParts[0]);
newCustomer.setLastName(nameParts[1]);
return syncCustomer(newCustomer, user);
}
@Transactional
public void syncLinkedRecords(User user) {
if (user.getRole() == User.Role.CUSTOMER) {
ensureLinkedCustomer(user);
return;
}
ensureLinkedEmployee(user);
}
private Employee syncEmployee(Employee employee, User user) {
employee.setUserId(user.getId());
employee.setEmail(user.getEmail());
String[] nameParts = splitFullName(user.getFullName());
employee.setFirstName(nameParts[0]);
employee.setLastName(nameParts[1]);
if (user.getRole() == User.Role.ADMIN) {
employee.setRole("Manager");
} else {
employee.setRole("Staff");
}
return employeeRepository.save(employee);
}
private Customer syncCustomer(Customer customer, User user) {
customer.setUserId(user.getId());
customer.setEmail(user.getEmail());
String[] nameParts = splitFullName(user.getFullName());
customer.setFirstName(nameParts[0]);
customer.setLastName(nameParts[1]);
return customerRepository.save(customer);
}
private String[] splitFullName(String fullName) {
if (fullName == null || fullName.trim().isEmpty()) {
return new String[]{"System", "User"};
}
String trimmed = fullName.trim();
int spaceIndex = trimmed.indexOf(' ');
if (spaceIndex == -1) {
return new String[]{trimmed, "User"};
}
String firstName = trimmed.substring(0, spaceIndex).trim();
String lastName = trimmed.substring(spaceIndex + 1).trim();
if (firstName.isEmpty()) {
firstName = "System";
}
if (lastName.isEmpty()) {
lastName = "User";
}
return new String[]{firstName, lastName};
}
}

View File

@@ -26,13 +26,11 @@ public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final UserBusinessLinkageService userBusinessLinkageService;
private final StoreRepository storeRepository;
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, UserBusinessLinkageService userBusinessLinkageService, StoreRepository storeRepository) {
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, StoreRepository storeRepository) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.userBusinessLinkageService = userBusinessLinkageService;
this.storeRepository = storeRepository;
}
@@ -79,8 +77,6 @@ public class UserService {
user = userRepository.save(user);
userBusinessLinkageService.syncLinkedRecords(user);
return mapToResponse(user);
}
@@ -117,7 +113,6 @@ public class UserService {
}
user = userRepository.save(user);
userBusinessLinkageService.syncLinkedRecords(user);
return mapToResponse(user);
}

View File

@@ -1,10 +1,6 @@
package com.petshop.backend.util;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.Employee;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.EmployeeRepository;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.security.AppPrincipal;
import org.springframework.security.core.Authentication;
@@ -47,16 +43,4 @@ public class AuthenticationHelper {
return userRepository.findByUsername(username)
.orElseThrow(() -> new RuntimeException("User not found: " + username));
}
public static Employee getAuthenticatedEmployee(UserRepository userRepository, EmployeeRepository employeeRepository) {
User user = getAuthenticatedUser(userRepository);
return employeeRepository.findByUserId(user.getId())
.orElseThrow(() -> new RuntimeException("Employee record not found for user: " + user.getUsername()));
}
public static Customer getAuthenticatedCustomer(UserRepository userRepository, CustomerRepository customerRepository) {
User user = getAuthenticatedUser(userRepository);
return customerRepository.findByUserId(user.getId())
.orElseThrow(() -> new RuntimeException("Customer record not found for user: " + user.getUsername()));
}
}