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.entity.User;
import com.petshop.backend.repository.UserRepository; 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.boot.CommandLineRunner;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -13,14 +11,10 @@ public class DataInitializer implements CommandLineRunner {
private final UserRepository userRepository; private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder; 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.userRepository = userRepository;
this.passwordEncoder = passwordEncoder; this.passwordEncoder = passwordEncoder;
this.userBusinessLinkageService = userBusinessLinkageService;
this.storeAssignmentService = storeAssignmentService;
} }
@Override @Override
@@ -34,16 +28,25 @@ public class DataInitializer implements CommandLineRunner {
admin.setUsername("admin"); admin.setUsername("admin");
admin.setPassword(passwordEncoder.encode("admin123")); admin.setPassword(passwordEncoder.encode("admin123"));
admin.setEmail("admin@petshop.com"); admin.setEmail("admin@petshop.com");
admin.setFirstName("Admin");
admin.setLastName("User");
admin.setFullName("Admin User"); admin.setFullName("Admin User");
admin.setPhone("000-000-1000"); admin.setPhone("000-000-1000");
admin.setRole(User.Role.ADMIN); admin.setRole(User.Role.ADMIN);
admin.setActive(true); admin.setActive(true);
admin = userRepository.save(admin); userRepository.save(admin);
System.out.println("Admin user created successfully"); System.out.println("Admin user created successfully");
} else { } else {
System.out.println("Admin user already exists"); System.out.println("Admin user already exists");
// Normalize missing fields if needed
boolean updated = false; 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()) { if (admin.getFullName() == null || admin.getFullName().isEmpty()) {
admin.setFullName("Admin User"); admin.setFullName("Admin User");
updated = true; updated = true;
@@ -65,12 +68,10 @@ public class DataInitializer implements CommandLineRunner {
updated = true; updated = true;
} }
if (updated) { if (updated) {
admin = userRepository.save(admin); userRepository.save(admin);
System.out.println("Admin user normalized"); System.out.println("Admin user normalized");
} }
} }
// Ensure linked employee
storeAssignmentService.assignStoreIfMissing(userBusinessLinkageService.ensureLinkedEmployee(admin), 1L);
User staff = userRepository.findByUsername("staff").orElse(null); User staff = userRepository.findByUsername("staff").orElse(null);
if (staff == null) { if (staff == null) {
@@ -79,16 +80,25 @@ public class DataInitializer implements CommandLineRunner {
staff.setUsername("staff"); staff.setUsername("staff");
staff.setPassword(passwordEncoder.encode("staff123")); staff.setPassword(passwordEncoder.encode("staff123"));
staff.setEmail("staff@petshop.com"); staff.setEmail("staff@petshop.com");
staff.setFirstName("Staff");
staff.setLastName("User");
staff.setFullName("Staff User"); staff.setFullName("Staff User");
staff.setPhone("000-000-1001"); staff.setPhone("000-000-1001");
staff.setRole(User.Role.STAFF); staff.setRole(User.Role.STAFF);
staff.setActive(true); staff.setActive(true);
staff = userRepository.save(staff); userRepository.save(staff);
System.out.println("Staff user created successfully"); System.out.println("Staff user created successfully");
} else { } else {
System.out.println("Staff user already exists"); System.out.println("Staff user already exists");
// Normalize missing fields if needed
boolean updated = false; 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()) { if (staff.getFullName() == null || staff.getFullName().isEmpty()) {
staff.setFullName("Staff User"); staff.setFullName("Staff User");
updated = true; updated = true;
@@ -110,12 +120,10 @@ public class DataInitializer implements CommandLineRunner {
updated = true; updated = true;
} }
if (updated) { if (updated) {
staff = userRepository.save(staff); userRepository.save(staff);
System.out.println("Staff user normalized"); System.out.println("Staff user normalized");
} }
} }
// Ensure linked employee
storeAssignmentService.assignStoreIfMissing(userBusinessLinkageService.ensureLinkedEmployee(staff), 1L);
User customer = userRepository.findByUsername("customer").orElse(null); User customer = userRepository.findByUsername("customer").orElse(null);
if (customer == null) { if (customer == null) {
@@ -124,16 +132,25 @@ public class DataInitializer implements CommandLineRunner {
customer.setUsername("customer"); customer.setUsername("customer");
customer.setPassword(passwordEncoder.encode("customer123")); customer.setPassword(passwordEncoder.encode("customer123"));
customer.setEmail("customer@petshop.com"); customer.setEmail("customer@petshop.com");
customer.setFirstName("Test");
customer.setLastName("Customer");
customer.setFullName("Test Customer"); customer.setFullName("Test Customer");
customer.setPhone("000-000-1002"); customer.setPhone("000-000-1002");
customer.setRole(User.Role.CUSTOMER); customer.setRole(User.Role.CUSTOMER);
customer.setActive(true); customer.setActive(true);
customer = userRepository.save(customer); userRepository.save(customer);
System.out.println("Customer user created successfully"); System.out.println("Customer user created successfully");
} else { } else {
System.out.println("Customer user already exists"); System.out.println("Customer user already exists");
// Normalize missing fields if needed
boolean updated = false; 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()) { if (customer.getFullName() == null || customer.getFullName().isEmpty()) {
customer.setFullName("Test Customer"); customer.setFullName("Test Customer");
updated = true; updated = true;
@@ -155,12 +172,10 @@ public class DataInitializer implements CommandLineRunner {
updated = true; updated = true;
} }
if (updated) { if (updated) {
customer = userRepository.save(customer); userRepository.save(customer);
System.out.println("Customer user normalized"); System.out.println("Customer user normalized");
} }
} }
// Ensure linked customer
userBusinessLinkageService.ensureLinkedCustomer(customer);
System.out.println("==== DataInitializer: Completed ===="); 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.AdoptionRequest;
import com.petshop.backend.dto.adoption.AdoptionResponse; import com.petshop.backend.dto.adoption.AdoptionResponse;
import com.petshop.backend.dto.common.BulkDeleteRequest; import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.entity.Customer; import com.petshop.backend.entity.User;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.UserRepository; import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.AdoptionService; import com.petshop.backend.service.AdoptionService;
import com.petshop.backend.util.AuthenticationHelper; import com.petshop.backend.util.AuthenticationHelper;
@@ -24,12 +23,10 @@ public class AdoptionController {
private final AdoptionService adoptionService; private final AdoptionService adoptionService;
private final UserRepository userRepository; 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.adoptionService = adoptionService;
this.userRepository = userRepository; this.userRepository = userRepository;
this.customerRepository = customerRepository;
} }
@GetMapping @GetMapping
@@ -45,8 +42,8 @@ public class AdoptionController {
Long customerId = null; Long customerId = null;
if (role != null && role.equals("CUSTOMER")) { if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository); User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = customer.getCustomerId(); customerId = user.getId();
} }
return ResponseEntity.ok(adoptionService.getAllAdoptions(q, pageable, customerId)); return ResponseEntity.ok(adoptionService.getAllAdoptions(q, pageable, customerId));
@@ -63,8 +60,8 @@ public class AdoptionController {
Long customerId = null; Long customerId = null;
if (role != null && role.equals("CUSTOMER")) { if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository); User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = customer.getCustomerId(); customerId = user.getId();
} }
return ResponseEntity.ok(adoptionService.getAdoptionById(id, customerId)); 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.AppointmentRequest;
import com.petshop.backend.dto.appointment.AppointmentResponse; import com.petshop.backend.dto.appointment.AppointmentResponse;
import com.petshop.backend.dto.common.BulkDeleteRequest; import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.entity.Customer; import com.petshop.backend.entity.User;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.UserRepository; import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.AppointmentService; import com.petshop.backend.service.AppointmentService;
import com.petshop.backend.util.AuthenticationHelper; import com.petshop.backend.util.AuthenticationHelper;
@@ -27,12 +26,10 @@ public class AppointmentController {
private final AppointmentService appointmentService; private final AppointmentService appointmentService;
private final UserRepository userRepository; 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.appointmentService = appointmentService;
this.userRepository = userRepository; this.userRepository = userRepository;
this.customerRepository = customerRepository;
} }
@GetMapping @GetMapping
@@ -48,8 +45,8 @@ public class AppointmentController {
Long customerId = null; Long customerId = null;
if (role != null && role.equals("CUSTOMER")) { if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository); User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = customer.getCustomerId(); customerId = user.getId();
} }
return ResponseEntity.ok(appointmentService.getAllAppointments(q, pageable, customerId)); return ResponseEntity.ok(appointmentService.getAllAppointments(q, pageable, customerId));
@@ -66,8 +63,8 @@ public class AppointmentController {
Long customerId = null; Long customerId = null;
if (role != null && role.equals("CUSTOMER")) { if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository); User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = customer.getCustomerId(); customerId = user.getId();
} }
return ResponseEntity.ok(appointmentService.getAppointmentById(id, customerId)); return ResponseEntity.ok(appointmentService.getAppointmentById(id, customerId));
@@ -83,8 +80,8 @@ public class AppointmentController {
.orElse(null); .orElse(null);
if (role != null && role.equals("CUSTOMER")) { if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository); User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
if (!request.getCustomerId().equals(customer.getCustomerId())) { if (!request.getCustomerId().equals(user.getId())) {
throw new org.springframework.security.access.AccessDeniedException("You can only create appointments for yourself"); 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.RegisterRequest;
import com.petshop.backend.dto.auth.RegisterResponse; import com.petshop.backend.dto.auth.RegisterResponse;
import com.petshop.backend.dto.auth.UserInfoResponse; 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.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.repository.UserRepository;
import com.petshop.backend.security.JwtUtil; import com.petshop.backend.security.JwtUtil;
import com.petshop.backend.service.AvatarStorageService; import com.petshop.backend.service.AvatarStorageService;
import com.petshop.backend.service.UserBusinessLinkageService;
import com.petshop.backend.util.AuthenticationHelper; import com.petshop.backend.util.AuthenticationHelper;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
@@ -44,22 +40,14 @@ public class AuthController {
private final UserRepository userRepository; private final UserRepository userRepository;
private final JwtUtil jwtUtil; private final JwtUtil jwtUtil;
private final PasswordEncoder passwordEncoder; private final PasswordEncoder passwordEncoder;
private final UserBusinessLinkageService userBusinessLinkageService;
private final EmployeeRepository employeeRepository;
private final EmployeeStoreRepository employeeStoreRepository;
private final AvatarStorageService avatarStorageService; 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.authenticationManager = authenticationManager;
this.userRepository = userRepository; this.userRepository = userRepository;
this.jwtUtil = jwtUtil; this.jwtUtil = jwtUtil;
this.passwordEncoder = passwordEncoder; this.passwordEncoder = passwordEncoder;
this.userBusinessLinkageService = userBusinessLinkageService;
this.employeeRepository = employeeRepository;
this.employeeStoreRepository = employeeStoreRepository;
this.avatarStorageService = avatarStorageService; this.avatarStorageService = avatarStorageService;
this.customerRepository = customerRepository;
} }
@PostMapping("/register") @PostMapping("/register")
@@ -94,9 +82,6 @@ public class AuthController {
User savedUser = userRepository.save(user); User savedUser = userRepository.save(user);
// Create or link customer record
userBusinessLinkageService.ensureLinkedCustomer(savedUser);
String token = jwtUtil.generateToken(savedUser); String token = jwtUtil.generateToken(savedUser);
return ResponseEntity.status(HttpStatus.CREATED).body(new RegisterResponse( return ResponseEntity.status(HttpStatus.CREATED).body(new RegisterResponse(
@@ -148,22 +133,7 @@ public class AuthController {
@GetMapping("/me") @GetMapping("/me")
public ResponseEntity<UserInfoResponse> getCurrentUser() { public ResponseEntity<UserInfoResponse> getCurrentUser() {
User user = getAuthenticatedUser(); User user = getAuthenticatedUser();
return ResponseEntity.ok(toUserInfoResponse(user));
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
));
} }
@PutMapping("/me") @PutMapping("/me")
@@ -218,39 +188,24 @@ public class AuthController {
} }
User updatedUser = userRepository.save(user); User updatedUser = userRepository.save(user);
userBusinessLinkageService.syncLinkedRecords(updatedUser); return ResponseEntity.ok(toUserInfoResponse(updatedUser));
}
EmployeeStore employeeStore = resolveEmployeeStore(updatedUser); private UserInfoResponse toUserInfoResponse(User user) {
Long customerId = resolveCustomerId(updatedUser); StoreLocation primaryStore = user.getPrimaryStore();
Long customerId = user.getRole() == User.Role.CUSTOMER ? user.getId() : null;
return ResponseEntity.ok(new UserInfoResponse( return new UserInfoResponse(
updatedUser.getId(), user.getId(),
updatedUser.getUsername(), user.getUsername(),
updatedUser.getEmail(), user.getEmail(),
updatedUser.getFullName(), user.getFullName(),
updatedUser.getPhone(), user.getPhone(),
avatarStorageService.toOwnerAvatarUrl(updatedUser), avatarStorageService.toOwnerAvatarUrl(user),
updatedUser.getRole().name(), user.getRole().name(),
customerId, customerId,
employeeStore != null ? employeeStore.getStore().getStoreId() : null, primaryStore != null ? primaryStore.getStoreId() : null,
employeeStore != null ? employeeStore.getStore().getStoreName() : null primaryStore != null ? primaryStore.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);
} }
private String trimToNull(String value) { 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.MessageResponse;
import com.petshop.backend.dto.chat.UpdateConversationRequest; import com.petshop.backend.dto.chat.UpdateConversationRequest;
import com.petshop.backend.entity.User; import com.petshop.backend.entity.User;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.UserRepository; import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.ChatRealtimeService; import com.petshop.backend.service.ChatRealtimeService;
import com.petshop.backend.service.ChatService; import com.petshop.backend.service.ChatService;
@@ -27,13 +26,11 @@ public class ChatController {
private final ChatService chatService; private final ChatService chatService;
private final ChatRealtimeService chatRealtimeService; private final ChatRealtimeService chatRealtimeService;
private final UserRepository userRepository; 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.chatService = chatService;
this.chatRealtimeService = chatRealtimeService; this.chatRealtimeService = chatRealtimeService;
this.userRepository = userRepository; this.userRepository = userRepository;
this.customerRepository = customerRepository;
} }
private User getCurrentUser() { private User getCurrentUser() {

View File

@@ -1,9 +1,9 @@
package com.petshop.backend.controller; package com.petshop.backend.controller;
import com.petshop.backend.dto.common.BulkDeleteRequest; import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.dto.customer.CustomerRequest; import com.petshop.backend.dto.user.UserRequest;
import com.petshop.backend.dto.customer.CustomerResponse; import com.petshop.backend.dto.user.UserResponse;
import com.petshop.backend.service.CustomerService; import com.petshop.backend.service.UserService;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@@ -17,45 +17,45 @@ import org.springframework.web.bind.annotation.*;
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')") @PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public class CustomerController { public class CustomerController {
private final CustomerService customerService; private final UserService userService;
public CustomerController(CustomerService customerService) { public CustomerController(UserService userService) {
this.customerService = customerService; this.userService = userService;
} }
@GetMapping @GetMapping
public ResponseEntity<Page<CustomerResponse>> getAllCustomers( public ResponseEntity<Page<UserResponse>> getAllCustomers(
@RequestParam(required = false) String q, @RequestParam(required = false) String q,
Pageable pageable) { Pageable pageable) {
return ResponseEntity.ok(customerService.getAllCustomers(q, pageable)); return ResponseEntity.ok(userService.getAllUsers(q, "CUSTOMER", pageable));
} }
@GetMapping("/{id}") @GetMapping("/{id}")
public ResponseEntity<CustomerResponse> getCustomerById(@PathVariable Long id) { public ResponseEntity<UserResponse> getCustomerById(@PathVariable Long id) {
return ResponseEntity.ok(customerService.getCustomerById(id)); return ResponseEntity.ok(userService.getUserById(id));
} }
@PostMapping @PostMapping
public ResponseEntity<CustomerResponse> createCustomer(@Valid @RequestBody CustomerRequest request) { public ResponseEntity<UserResponse> createCustomer(@Valid @RequestBody UserRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(customerService.createCustomer(request)); return ResponseEntity.status(HttpStatus.CREATED).body(userService.createUser(request));
} }
@PutMapping("/{id}") @PutMapping("/{id}")
public ResponseEntity<CustomerResponse> updateCustomer( public ResponseEntity<UserResponse> updateCustomer(
@PathVariable Long id, @PathVariable Long id,
@Valid @RequestBody CustomerRequest request) { @Valid @RequestBody UserRequest request) {
return ResponseEntity.ok(customerService.updateCustomer(id, request)); return ResponseEntity.ok(userService.updateUser(id, request));
} }
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public ResponseEntity<Void> deleteCustomer(@PathVariable Long id) { public ResponseEntity<Void> deleteCustomer(@PathVariable Long id) {
customerService.deleteCustomer(id); userService.deleteUser(id);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@PostMapping("/bulk-delete") @PostMapping("/bulk-delete")
public ResponseEntity<Void> bulkDeleteCustomers(@Valid @RequestBody BulkDeleteRequest request) { public ResponseEntity<Void> bulkDeleteCustomers(@Valid @RequestBody BulkDeleteRequest request) {
customerService.bulkDeleteCustomers(request); userService.bulkDeleteUsers(request);
return ResponseEntity.noContent().build(); 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; package com.petshop.backend.controller;
import com.petshop.backend.dto.common.DropdownOption; 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.entity.User;
import com.petshop.backend.repository.*; import com.petshop.backend.repository.*;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -20,31 +18,24 @@ import java.util.stream.Collectors;
public class DropdownController { public class DropdownController {
private final PetRepository petRepository; private final PetRepository petRepository;
private final CustomerRepository customerRepository;
private final CustomerPetRepository customerPetRepository;
private final ServiceRepository serviceRepository; private final ServiceRepository serviceRepository;
private final ProductRepository productRepository; private final ProductRepository productRepository;
private final CategoryRepository categoryRepository; private final CategoryRepository categoryRepository;
private final StoreRepository storeRepository; private final StoreRepository storeRepository;
private final SupplierRepository supplierRepository; private final SupplierRepository supplierRepository;
private final EmployeeStoreRepository employeeStoreRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
public DropdownController(PetRepository petRepository, CustomerRepository customerRepository, public DropdownController(PetRepository petRepository,
CustomerPetRepository customerPetRepository,
ServiceRepository serviceRepository, ProductRepository productRepository, ServiceRepository serviceRepository, ProductRepository productRepository,
CategoryRepository categoryRepository, StoreRepository storeRepository, CategoryRepository categoryRepository, StoreRepository storeRepository,
SupplierRepository supplierRepository, EmployeeStoreRepository employeeStoreRepository, SupplierRepository supplierRepository,
UserRepository userRepository) { UserRepository userRepository) {
this.petRepository = petRepository; this.petRepository = petRepository;
this.customerRepository = customerRepository;
this.customerPetRepository = customerPetRepository;
this.serviceRepository = serviceRepository; this.serviceRepository = serviceRepository;
this.productRepository = productRepository; this.productRepository = productRepository;
this.categoryRepository = categoryRepository; this.categoryRepository = categoryRepository;
this.storeRepository = storeRepository; this.storeRepository = storeRepository;
this.supplierRepository = supplierRepository; this.supplierRepository = supplierRepository;
this.employeeStoreRepository = employeeStoreRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
} }
@@ -71,8 +62,8 @@ public class DropdownController {
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')") @PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getCustomers() { public ResponseEntity<List<DropdownOption>> getCustomers() {
return ResponseEntity.ok( return ResponseEntity.ok(
customerRepository.findAll().stream() userRepository.findByRoleAndActiveTrue(User.Role.CUSTOMER).stream()
.map(c -> new DropdownOption(c.getCustomerId(), c.getFirstName() + " " + c.getLastName())) .map(u -> new DropdownOption(u.getId(), u.getFirstName() + " " + u.getLastName()))
.collect(Collectors.toList()) .collect(Collectors.toList())
); );
} }
@@ -81,18 +72,8 @@ public class DropdownController {
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')") @PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getAppointmentCustomers() { public ResponseEntity<List<DropdownOption>> getAppointmentCustomers() {
return ResponseEntity.ok( return ResponseEntity.ok(
customerRepository.findAllWithPets().stream() userRepository.findByRoleAndActiveTrue(User.Role.CUSTOMER).stream()
.map(c -> new DropdownOption(c.getCustomerId(), c.getFirstName() + " " + c.getLastName())) .map(u -> new DropdownOption(u.getId(), u.getFirstName() + " " + u.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)
.collect(Collectors.toList()) .collect(Collectors.toList())
); );
} }
@@ -159,17 +140,15 @@ public class DropdownController {
@GetMapping({"/stores/{storeId}/employees", "/employees"}) @GetMapping({"/stores/{storeId}/employees", "/employees"})
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')") @PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getStoreEmployees(@PathVariable(required = false) Long storeId) { public ResponseEntity<List<DropdownOption>> getStoreEmployees(@PathVariable(required = false) Long storeId) {
List<EmployeeStore> employees; List<User> employees;
if (storeId == null || storeId == 0) { if (storeId == null || storeId == 0) {
employees = employeeStoreRepository.findActiveAllOrderByEmployeeEmployeeIdAsc(); employees = userRepository.findByRoleAndActiveTrue(User.Role.STAFF);
} else { } else {
employees = employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(storeId); employees = userRepository.findByPrimaryStoreStoreIdAndRoleAndActiveTrue(storeId, User.Role.STAFF);
} }
return ResponseEntity.ok( return ResponseEntity.ok(
employees.stream() employees.stream()
.filter(this::isAssignableEmployee) .map(u -> new DropdownOption(u.getId(), u.getFirstName() + " " + u.getLastName()))
.map(this::toEmployeeOption)
.distinct()
.collect(Collectors.toList()) .collect(Collectors.toList())
); );
} }
@@ -183,26 +162,4 @@ public class DropdownController {
.collect(Collectors.toList()) .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; package com.petshop.backend.controller;
import com.petshop.backend.dto.employee.EmployeeRequest; import com.petshop.backend.dto.user.UserRequest;
import com.petshop.backend.dto.employee.EmployeeResponse; import com.petshop.backend.dto.user.UserResponse;
import com.petshop.backend.service.EmployeeService; import com.petshop.backend.service.UserService;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@@ -15,35 +15,40 @@ import org.springframework.web.bind.annotation.*;
@RequestMapping("/api/v1/employees") @RequestMapping("/api/v1/employees")
@PreAuthorize("hasRole('ADMIN')") @PreAuthorize("hasRole('ADMIN')")
public class EmployeeController { public class EmployeeController {
private final EmployeeService employeeService;
public EmployeeController(EmployeeService employeeService) { private final UserService userService;
this.employeeService = employeeService;
public EmployeeController(UserService userService) {
this.userService = userService;
} }
@GetMapping @GetMapping
public ResponseEntity<Page<EmployeeResponse>> getAllEmployees(@RequestParam(required = false) String q, Pageable pageable) { public ResponseEntity<Page<UserResponse>> getAllEmployees(
return ResponseEntity.ok(employeeService.getAllEmployees(q, pageable)); @RequestParam(required = false) String q,
Pageable pageable) {
return ResponseEntity.ok(userService.getAllUsers(q, "STAFF", pageable));
} }
@GetMapping("/{id}") @GetMapping("/{id}")
public ResponseEntity<EmployeeResponse> getEmployeeById(@PathVariable Long id) { public ResponseEntity<UserResponse> getEmployeeById(@PathVariable Long id) {
return ResponseEntity.ok(employeeService.getEmployeeById(id)); return ResponseEntity.ok(userService.getUserById(id));
} }
@PostMapping @PostMapping
public ResponseEntity<EmployeeResponse> createEmployee(@Valid @RequestBody EmployeeRequest request) { public ResponseEntity<UserResponse> createEmployee(@Valid @RequestBody UserRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(employeeService.createEmployee(request)); return ResponseEntity.status(HttpStatus.CREATED).body(userService.createUser(request));
} }
@PutMapping("/{id}") @PutMapping("/{id}")
public ResponseEntity<EmployeeResponse> updateEmployee(@PathVariable Long id, @Valid @RequestBody EmployeeRequest request) { public ResponseEntity<UserResponse> updateEmployee(
return ResponseEntity.ok(employeeService.updateEmployee(id, request)); @PathVariable Long id,
@Valid @RequestBody UserRequest request) {
return ResponseEntity.ok(userService.updateUser(id, request));
} }
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public ResponseEntity<Void> deleteEmployee(@PathVariable Long id) { public ResponseEntity<Void> deleteEmployee(@PathVariable Long id) {
employeeService.deleteEmployee(id); userService.deleteUser(id);
return ResponseEntity.noContent().build(); 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.RefundRequest;
import com.petshop.backend.dto.refund.RefundResponse; import com.petshop.backend.dto.refund.RefundResponse;
import com.petshop.backend.dto.refund.RefundUpdateRequest; import com.petshop.backend.dto.refund.RefundUpdateRequest;
import com.petshop.backend.entity.Customer; import com.petshop.backend.entity.User;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.UserRepository; import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.RefundService; import com.petshop.backend.service.RefundService;
import com.petshop.backend.util.AuthenticationHelper; import com.petshop.backend.util.AuthenticationHelper;
@@ -26,12 +25,10 @@ public class RefundController {
private final RefundService refundService; private final RefundService refundService;
private final UserRepository userRepository; 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.refundService = refundService;
this.userRepository = userRepository; this.userRepository = userRepository;
this.customerRepository = customerRepository;
} }
@PostMapping @PostMapping
@@ -46,8 +43,8 @@ public class RefundController {
Long customerId = null; Long customerId = null;
if (role != null && role.equals("CUSTOMER")) { if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository); User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = customer.getCustomerId(); customerId = user.getId();
} }
RefundResponse refund = refundService.createRefund(request, customerId); RefundResponse refund = refundService.createRefund(request, customerId);
@@ -70,8 +67,8 @@ public class RefundController {
Long customerId = null; Long customerId = null;
if (role != null && role.equals("CUSTOMER")) { if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository); User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = customer.getCustomerId(); customerId = user.getId();
} }
List<RefundResponse> refunds = refundService.getAllRefunds(customerId); List<RefundResponse> refunds = refundService.getAllRefunds(customerId);
@@ -90,8 +87,8 @@ public class RefundController {
Long customerId = null; Long customerId = null;
if (role != null && role.equals("CUSTOMER")) { if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository); User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = customer.getCustomerId(); customerId = user.getId();
} }
RefundResponse refund = refundService.getRefundById(id, customerId); RefundResponse refund = refundService.getRefundById(id, customerId);

View File

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

View File

@@ -21,8 +21,6 @@ public class AppointmentResponse {
private String employeeName; private String employeeName;
private List<String> petNames; private List<String> petNames;
private List<Long> petIds; private List<Long> petIds;
private List<String> customerPetNames;
private List<Long> customerPetIds;
private LocalDateTime createdAt; private LocalDateTime createdAt;
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
@@ -158,24 +156,6 @@ public class AppointmentResponse {
this.petIds = petIds; 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() { public LocalDateTime getCreatedAt() {
return createdAt; 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; private Long logId;
@ManyToOne @ManyToOne
@JoinColumn(name = "employeeId", nullable = false) @JoinColumn(name = "userId", nullable = false)
private Employee employee; private User user;
@Column(nullable = false, columnDefinition = "TEXT") @Column(nullable = false, columnDefinition = "TEXT")
private String activity; private String activity;
@@ -26,9 +26,9 @@ public class ActivityLog {
public 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.logId = logId;
this.employee = employee; this.user = user;
this.activity = activity; this.activity = activity;
this.logTimestamp = logTimestamp; this.logTimestamp = logTimestamp;
} }
@@ -41,12 +41,12 @@ public class ActivityLog {
this.logId = logId; this.logId = logId;
} }
public Employee getEmployee() { public User getUser() {
return employee; return user;
} }
public void setEmployee(Employee employee) { public void setUser(User user) {
this.employee = employee; this.user = user;
} }
public String getActivity() { public String getActivity() {
@@ -82,7 +82,7 @@ public class ActivityLog {
public String toString() { public String toString() {
return "ActivityLog{" + return "ActivityLog{" +
"logId=" + logId + "logId=" + logId +
", employee=" + employee + ", user=" + user +
", activity='" + activity + '\'' + ", activity='" + activity + '\'' +
", logTimestamp=" + logTimestamp + ", logTimestamp=" + logTimestamp +
'}'; '}';

View File

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

View File

@@ -21,7 +21,7 @@ public class Appointment {
@ManyToOne @ManyToOne
@JoinColumn(name = "customerId", nullable = false) @JoinColumn(name = "customerId", nullable = false)
private Customer customer; private User customer;
@ManyToOne @ManyToOne
@JoinColumn(name = "storeId", nullable = false) @JoinColumn(name = "storeId", nullable = false)
@@ -33,7 +33,7 @@ public class Appointment {
@ManyToOne @ManyToOne
@JoinColumn(name = "employeeId", nullable = false) @JoinColumn(name = "employeeId", nullable = false)
private Employee employee; private User employee;
@Column(nullable = false) @Column(nullable = false)
private LocalDate appointmentDate; private LocalDate appointmentDate;
@@ -52,14 +52,6 @@ public class Appointment {
) )
private Set<Pet> pets = new HashSet<>(); 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 @CreationTimestamp
@Column(name = "created_at", updatable = false) @Column(name = "created_at", updatable = false)
private LocalDateTime createdAt; private LocalDateTime createdAt;
@@ -71,20 +63,6 @@ public class Appointment {
public 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() { public Long getAppointmentId() {
return appointmentId; return appointmentId;
} }
@@ -93,11 +71,11 @@ public class Appointment {
this.appointmentId = appointmentId; this.appointmentId = appointmentId;
} }
public Customer getCustomer() { public User getCustomer() {
return customer; return customer;
} }
public void setCustomer(Customer customer) { public void setCustomer(User customer) {
this.customer = customer; this.customer = customer;
} }
@@ -117,11 +95,11 @@ public class Appointment {
this.service = service; this.service = service;
} }
public Employee getEmployee() { public User getEmployee() {
return employee; return employee;
} }
public void setEmployee(Employee employee) { public void setEmployee(User employee) {
this.employee = employee; this.employee = employee;
} }
@@ -157,15 +135,6 @@ public class Appointment {
this.pets = pets; this.pets = pets;
} }
public Set<CustomerPet> getCustomerPets() {
return customerPets;
}
public void setCustomerPets(Set<CustomerPet> customerPets) {
this.customerPets = customerPets;
}
public LocalDateTime getCreatedAt() { public LocalDateTime getCreatedAt() {
return createdAt; 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) @Column(nullable = false, length = 50)
private String petSpecies; private String petSpecies;
@Column(nullable = false, length = 50) @Column(length = 50)
private String petBreed; private String petBreed;
@Column(nullable = false) @Column
private Integer petAge; private Integer petAge;
@Column(nullable = false, length = 20) @Column(nullable = false, length = 20)
private String petStatus; private String petStatus;
@Column(nullable = false, precision = 10, scale = 2) @Column(precision = 10, scale = 2)
private BigDecimal petPrice; private BigDecimal petPrice;
@Column(length = 255) @Column(length = 255)
private String imageUrl; private String imageUrl;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "customerId") @JoinColumn(name = "ownerUserId")
private Customer customer; private User owner;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "storeId") @JoinColumn(name = "storeId")
@@ -57,19 +57,6 @@ public class Pet {
public 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() { public Long getPetId() {
return id; return id;
} }
@@ -134,6 +121,22 @@ public class Pet {
this.imageUrl = imageUrl; 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() { public LocalDateTime getCreatedAt() {
return createdAt; return createdAt;
} }
@@ -150,22 +153,6 @@ public class Pet {
this.updatedAt = updatedAt; 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 @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
@@ -185,13 +172,7 @@ public class Pet {
"id=" + id + "id=" + id +
", petName='" + petName + '\'' + ", petName='" + petName + '\'' +
", petSpecies='" + petSpecies + '\'' + ", petSpecies='" + petSpecies + '\'' +
", petBreed='" + petBreed + '\'' +
", petAge=" + petAge +
", petStatus='" + petStatus + '\'' + ", petStatus='" + petStatus + '\'' +
", petPrice=" + petPrice +
", imageUrl='" + imageUrl + '\'' +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}'; '}';
} }
} }

View File

@@ -23,7 +23,7 @@ public class Sale {
@ManyToOne @ManyToOne
@JoinColumn(name = "employeeId", nullable = false) @JoinColumn(name = "employeeId", nullable = false)
private Employee employee; private User employee;
@ManyToOne @ManyToOne
@JoinColumn(name = "storeId", nullable = false) @JoinColumn(name = "storeId", nullable = false)
@@ -31,7 +31,7 @@ public class Sale {
@ManyToOne @ManyToOne
@JoinColumn(name = "customerId") @JoinColumn(name = "customerId")
private Customer customer; private User customer;
@Column(nullable = false, precision = 10, scale = 2) @Column(nullable = false, precision = 10, scale = 2)
private BigDecimal totalAmount; private BigDecimal totalAmount;
@@ -60,21 +60,6 @@ public class Sale {
public 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() { public Long getSaleId() {
return saleId; return saleId;
} }
@@ -91,11 +76,11 @@ public class Sale {
this.saleDate = saleDate; this.saleDate = saleDate;
} }
public Employee getEmployee() { public User getEmployee() {
return employee; return employee;
} }
public void setEmployee(Employee employee) { public void setEmployee(User employee) {
this.employee = employee; this.employee = employee;
} }
@@ -107,11 +92,11 @@ public class Sale {
this.store = store; this.store = store;
} }
public Customer getCustomer() { public User getCustomer() {
return customer; return customer;
} }
public void setCustomer(Customer customer) { public void setCustomer(User customer) {
this.customer = customer; this.customer = customer;
} }
@@ -189,16 +174,9 @@ public class Sale {
return "Sale{" + return "Sale{" +
"saleId=" + saleId + "saleId=" + saleId +
", saleDate=" + saleDate + ", saleDate=" + saleDate +
", employee=" + employee +
", store=" + store +
", customer=" + customer +
", totalAmount=" + totalAmount + ", totalAmount=" + totalAmount +
", paymentMethod='" + paymentMethod + '\'' + ", paymentMethod='" + paymentMethod + '\'' +
", isRefund=" + isRefund + ", 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, '%'))") "LOWER(a.pet.petName) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Adoption> searchAdoptions(@Param("q") String query, Pageable pageable); 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.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.customer.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + "LOWER(a.customer.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.pet.petName) LIKE LOWER(CONCAT('%', :q, '%')))") "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, '%'))") "LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Appointment> searchAppointments(@Param("q") String query, Pageable pageable); 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.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.customer.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + "LOWER(a.customer.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.service.serviceName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + "LOWER(a.service.serviceName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')))") "LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')))")
Page<Appointment> searchAppointmentsByCustomer(@Param("customerId") Long customerId, @Param("q") String query, Pageable pageable); 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')") @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> findByEmployeeEmployeeIdAndAppointmentDate(@Param("employeeId") Long employeeId, @Param("date") LocalDate date); 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')") @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> findByEmployeeEmployeeIdInAndAppointmentDate(@Param("employeeIds") List<Long> employeeIds, @Param("date") LocalDate date); 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); List<Pet> findAllByPetStatusIgnoreCaseOrderByPetNameAsc(String petStatus);
@Query("SELECT p FROM Pet p WHERE " + @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 " + "(:species IS NULL OR LOWER(p.petSpecies) = LOWER(:species)) AND " +
"(:status IS NULL OR LOWER(p.petStatus) = LOWER(:status)) AND " + "(:status IS NULL OR LOWER(p.petStatus) = LOWER(:status)) AND " +
"(:storeId IS NULL OR p.store.storeId = :storeId)") "(: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); 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 " + @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 " + "(:species IS NULL OR LOWER(p.petSpecies) = LOWER(:species)) AND " +
"(:storeId IS NULL OR p.store.storeId = :storeId)") "(: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); 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 " + @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 " + "(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(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 " + "(:species IS NULL OR LOWER(p.petSpecies) = LOWER(:species)) AND " +
"(:status IS NULL OR LOWER(p.petStatus) = LOWER(:status))") "(: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); 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.data.repository.query.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional; import java.util.Optional;
@Repository @Repository
@@ -17,16 +18,24 @@ public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByPhone(String phone); Optional<User> findByPhone(String phone);
boolean existsByUsername(String username); boolean existsByUsername(String username);
Page<User> findByRole(User.Role role, Pageable pageable); 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 " + @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.fullName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.email, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " + "LOWER(COALESCE(u.email, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.phone, '')) LIKE LOWER(CONCAT('%', :q, '%'))") "LOWER(COALESCE(u.phone, '')) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<User> searchUsers(@Param("q") String query, Pageable pageable); Page<User> searchUsers(@Param("q") String query, Pageable pageable);
@Query("SELECT u FROM User u WHERE u.role = :role AND (" + @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.fullName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.email, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " + "LOWER(COALESCE(u.email, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.phone, '')) LIKE LOWER(CONCAT('%', :q, '%')))") "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.adoption.AdoptionResponse;
import com.petshop.backend.dto.common.BulkDeleteRequest; import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.entity.Adoption; 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.Pet;
import com.petshop.backend.entity.User; import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException; import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.AdoptionRepository; 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.PetRepository;
import com.petshop.backend.repository.UserRepository; import com.petshop.backend.repository.UserRepository;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -30,15 +26,11 @@ public class AdoptionService {
private final AdoptionRepository adoptionRepository; private final AdoptionRepository adoptionRepository;
private final PetRepository petRepository; private final PetRepository petRepository;
private final CustomerRepository customerRepository;
private final EmployeeRepository employeeRepository;
private final UserRepository userRepository; 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.adoptionRepository = adoptionRepository;
this.petRepository = petRepository; this.petRepository = petRepository;
this.customerRepository = customerRepository;
this.employeeRepository = employeeRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
} }
@@ -49,7 +41,7 @@ public class AdoptionService {
if (query != null && !query.trim().isEmpty()) { if (query != null && !query.trim().isEmpty()) {
adoptions = adoptionRepository.searchAdoptionsByCustomer(customerId, query, pageable); adoptions = adoptionRepository.searchAdoptionsByCustomer(customerId, query, pageable);
} else { } else {
adoptions = adoptionRepository.findByCustomerCustomerId(customerId, pageable); adoptions = adoptionRepository.findByCustomerId(customerId, pageable);
} }
} else { } else {
if (query != null && !query.trim().isEmpty()) { if (query != null && !query.trim().isEmpty()) {
@@ -66,7 +58,7 @@ public class AdoptionService {
Adoption adoption = adoptionRepository.findById(id) Adoption adoption = adoptionRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Adoption not found with id: " + 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"); throw new ResourceNotFoundException("You can only view your own adoptions");
} }
@@ -78,9 +70,9 @@ public class AdoptionService {
Pet pet = petRepository.findById(request.getPetId()) Pet pet = petRepository.findById(request.getPetId())
.orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + 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())); .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()); String adoptionStatus = normalizeAdoptionStatus(request.getAdoptionStatus());
validatePetAvailability(pet, null); validatePetAvailability(pet, null);
@@ -104,9 +96,9 @@ public class AdoptionService {
Pet pet = petRepository.findById(request.getPetId()) Pet pet = petRepository.findById(request.getPetId())
.orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + 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())); .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()); String adoptionStatus = normalizeAdoptionStatus(request.getAdoptionStatus());
validatePetAvailability(pet, adoption.getAdoptionId()); validatePetAvailability(pet, adoption.getAdoptionId());
@@ -139,9 +131,9 @@ public class AdoptionService {
adoption.getAdoptionId(), adoption.getAdoptionId(),
adoption.getPet().getPetId(), adoption.getPet().getPetId(),
adoption.getPet().getPetName(), adoption.getPet().getPetName(),
adoption.getCustomer().getCustomerId(), adoption.getCustomer().getId(),
adoption.getCustomer().getFirstName() + " " + adoption.getCustomer().getLastName(), adoption.getCustomer().getFirstName() + " " + adoption.getCustomer().getLastName(),
adoption.getEmployee().getEmployeeId(), adoption.getEmployee().getId(),
adoption.getEmployee().getFirstName() + " " + adoption.getEmployee().getLastName(), adoption.getEmployee().getFirstName() + " " + adoption.getEmployee().getLastName(),
adoption.getAdoptionDate(), adoption.getAdoptionDate(),
adoption.getAdoptionStatus(), adoption.getAdoptionStatus(),
@@ -151,31 +143,22 @@ public class AdoptionService {
); );
} }
private Employee resolveAdoptionEmployee(Long requestedEmployeeId) { private User resolveAdoptionEmployee(Long requestedEmployeeId) {
if (requestedEmployeeId != null) { if (requestedEmployeeId != null) {
Employee employee = employeeRepository.findById(requestedEmployeeId) User employee = userRepository.findById(requestedEmployeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + 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"); throw new IllegalArgumentException("Selected employee is not assignable for adoption work");
} }
return employee; return employee;
} }
return employeeRepository.findAllByIsActiveTrueOrderByEmployeeIdAsc().stream() return userRepository.findFirstByRoleAndActiveTrueOrderByIdAsc(User.Role.STAFF)
.filter(this::isAssignableEmployee)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No assignable staff member is available for adoption assignment")); .orElseThrow(() -> new IllegalArgumentException("No assignable staff member is available for adoption assignment"));
} }
private boolean isAssignableEmployee(Employee employee) { private boolean isAssignableUser(User user) {
Long userId = employee.getUserId(); return user.getRole() == User.Role.STAFF && Boolean.TRUE.equals(user.getActive());
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 String normalizeAdoptionStatus(String adoptionStatus) { private String normalizeAdoptionStatus(String adoptionStatus) {

View File

@@ -1,12 +1,10 @@
package com.petshop.backend.service; package com.petshop.backend.service;
import com.petshop.backend.dto.analytics.DashboardResponse; import com.petshop.backend.dto.analytics.DashboardResponse;
import com.petshop.backend.entity.Employee;
import com.petshop.backend.entity.Inventory; import com.petshop.backend.entity.Inventory;
import com.petshop.backend.entity.Product; import com.petshop.backend.entity.Product;
import com.petshop.backend.entity.Sale; import com.petshop.backend.entity.Sale;
import com.petshop.backend.entity.User; import com.petshop.backend.entity.User;
import com.petshop.backend.repository.EmployeeRepository;
import com.petshop.backend.repository.InventoryRepository; import com.petshop.backend.repository.InventoryRepository;
import com.petshop.backend.repository.ProductRepository; import com.petshop.backend.repository.ProductRepository;
import com.petshop.backend.repository.SaleRepository; import com.petshop.backend.repository.SaleRepository;
@@ -26,14 +24,12 @@ public class AnalyticsService {
private final SaleRepository saleRepository; private final SaleRepository saleRepository;
private final InventoryRepository inventoryRepository; private final InventoryRepository inventoryRepository;
private final ProductRepository productRepository; private final ProductRepository productRepository;
private final EmployeeRepository employeeRepository;
public AnalyticsService(SaleRepository saleRepository, public AnalyticsService(SaleRepository saleRepository,
InventoryRepository inventoryRepository, ProductRepository productRepository, EmployeeRepository employeeRepository) { InventoryRepository inventoryRepository, ProductRepository productRepository) {
this.saleRepository = saleRepository; this.saleRepository = saleRepository;
this.inventoryRepository = inventoryRepository; this.inventoryRepository = inventoryRepository;
this.productRepository = productRepository; this.productRepository = productRepository;
this.employeeRepository = employeeRepository;
} }
@Transactional(readOnly = true) @Transactional(readOnly = true)
@@ -183,11 +179,8 @@ public class AnalyticsService {
} }
if (user.getRole() == User.Role.STAFF && employeeRevenue.isEmpty()) { if (user.getRole() == User.Role.STAFF && employeeRevenue.isEmpty()) {
Employee employee = employeeRepository.findByUserId(user.getId()).orElse(null); String employeeName = user.getFirstName() + " " + user.getLastName();
if (employee != null) { employeeRevenue.put(employeeName, BigDecimal.ZERO);
String employeeName = employee.getFirstName() + " " + employee.getLastName();
employeeRevenue.put(employeeName, BigDecimal.ZERO);
}
} }
return employeeRevenue.entrySet().stream() return employeeRevenue.entrySet().stream()
@@ -200,7 +193,7 @@ public class AnalyticsService {
return true; return true;
} }
if (user.getRole() == User.Role.STAFF) { 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; 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.appointment.AppointmentResponse;
import com.petshop.backend.dto.common.BulkDeleteRequest; import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.entity.Appointment; 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.Pet;
import com.petshop.backend.entity.StoreLocation; import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.entity.User; import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException; import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.AppointmentRepository; 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.PetRepository;
import com.petshop.backend.repository.ServiceRepository; import com.petshop.backend.repository.ServiceRepository;
import com.petshop.backend.repository.StoreRepository; import com.petshop.backend.repository.StoreRepository;
@@ -41,25 +33,17 @@ import java.util.stream.Collectors;
public class AppointmentService { public class AppointmentService {
private final AppointmentRepository appointmentRepository; private final AppointmentRepository appointmentRepository;
private final CustomerRepository customerRepository;
private final CustomerPetRepository customerPetRepository;
private final ServiceRepository serviceRepository; private final ServiceRepository serviceRepository;
private final PetRepository petRepository; private final PetRepository petRepository;
private final StoreRepository storeRepository; private final StoreRepository storeRepository;
private final UserRepository userRepository; 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.appointmentRepository = appointmentRepository;
this.customerRepository = customerRepository;
this.customerPetRepository = customerPetRepository;
this.serviceRepository = serviceRepository; this.serviceRepository = serviceRepository;
this.petRepository = petRepository; this.petRepository = petRepository;
this.storeRepository = storeRepository; this.storeRepository = storeRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.employeeRepository = employeeRepository;
this.employeeStoreRepository = employeeStoreRepository;
} }
@Transactional(readOnly = true) @Transactional(readOnly = true)
@@ -70,7 +54,7 @@ public class AppointmentService {
if (query != null && !query.trim().isEmpty()) { if (query != null && !query.trim().isEmpty()) {
appointments = appointmentRepository.searchAppointmentsByCustomer(customerId, query, pageable); appointments = appointmentRepository.searchAppointmentsByCustomer(customerId, query, pageable);
} else { } else {
appointments = appointmentRepository.findByCustomerCustomerId(customerId, pageable); appointments = appointmentRepository.findByCustomerId(customerId, pageable);
} }
} else { } else {
if (query != null && !query.trim().isEmpty()) { if (query != null && !query.trim().isEmpty()) {
@@ -88,7 +72,7 @@ public class AppointmentService {
Appointment appointment = appointmentRepository.findById(id) Appointment appointment = appointmentRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Appointment not found with id: " + 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"); throw new ResourceNotFoundException("You can only view your own appointments");
} }
@@ -101,7 +85,7 @@ public class AppointmentService {
User authenticatedUser = AuthenticationHelper.getAuthenticatedUser(userRepository); 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())); .orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
StoreLocation store = storeRepository.findById(request.getStoreId()) StoreLocation store = storeRepository.findById(request.getStoreId())
@@ -111,15 +95,13 @@ public class AppointmentService {
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId())); .orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId()));
boolean hasPetIds = request.getPetIds() != null && !request.getPetIds().isEmpty(); 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."); throw new IllegalArgumentException("Please specify at least one pet.");
} }
Set<Pet> pets = hasPetIds ? fetchPets(request.getPetIds()) : new HashSet<>(); Set<Pet> pets = fetchPets(request.getPetIds());
Set<CustomerPet> customerPets = hasCustomerPetIds ? fetchCustomerPets(request.getCustomerPetIds(), customer.getCustomerId()) : new HashSet<>(); User employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
Employee employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
validateStoreAccess(store.getStoreId(), authenticatedUser); validateStoreAccess(store.getStoreId(), authenticatedUser);
validateAvailability(employee, service, request.getAppointmentDate(), request.getAppointmentTime(), null); validateAvailability(employee, service, request.getAppointmentDate(), request.getAppointmentTime(), null);
@@ -132,7 +114,6 @@ public class AppointmentService {
appointment.setAppointmentTime(request.getAppointmentTime()); appointment.setAppointmentTime(request.getAppointmentTime());
appointment.setAppointmentStatus(request.getAppointmentStatus()); appointment.setAppointmentStatus(request.getAppointmentStatus());
appointment.setPets(pets); appointment.setPets(pets);
appointment.setCustomerPets(customerPets);
appointment.setEmployee(employee); appointment.setEmployee(employee);
appointment = appointmentRepository.save(appointment); appointment = appointmentRepository.save(appointment);
@@ -148,7 +129,7 @@ public class AppointmentService {
Appointment appointment = appointmentRepository.findById(id) Appointment appointment = appointmentRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Appointment not found with id: " + 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())); .orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
StoreLocation store = storeRepository.findById(request.getStoreId()) StoreLocation store = storeRepository.findById(request.getStoreId())
@@ -158,15 +139,13 @@ public class AppointmentService {
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId())); .orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId()));
boolean hasPetIds = request.getPetIds() != null && !request.getPetIds().isEmpty(); 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."); throw new IllegalArgumentException("Please specify at least one pet.");
} }
Set<Pet> pets = hasPetIds ? fetchPets(request.getPetIds()) : new HashSet<>(); Set<Pet> pets = fetchPets(request.getPetIds());
Set<CustomerPet> customerPets = hasCustomerPetIds ? fetchCustomerPets(request.getCustomerPetIds(), customer.getCustomerId()) : new HashSet<>(); User employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
Employee employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
validateStoreAccess(store.getStoreId(), authenticatedUser); validateStoreAccess(store.getStoreId(), authenticatedUser);
validateAvailability(employee, service, request.getAppointmentDate(), request.getAppointmentTime(), id); validateAvailability(employee, service, request.getAppointmentDate(), request.getAppointmentTime(), id);
@@ -178,7 +157,6 @@ public class AppointmentService {
appointment.setAppointmentTime(request.getAppointmentTime()); appointment.setAppointmentTime(request.getAppointmentTime());
appointment.setAppointmentStatus(request.getAppointmentStatus()); appointment.setAppointmentStatus(request.getAppointmentStatus());
appointment.setPets(pets); appointment.setPets(pets);
appointment.setCustomerPets(customerPets);
appointment.setEmployee(employee); appointment.setEmployee(employee);
appointment = appointmentRepository.save(appointment); appointment = appointmentRepository.save(appointment);
@@ -206,20 +184,17 @@ public class AppointmentService {
com.petshop.backend.entity.Service service = serviceRepository.findById(serviceId) com.petshop.backend.entity.Service service = serviceRepository.findById(serviceId)
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + serviceId)); .orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + serviceId));
List<Employee> assignableEmployees = employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(storeId).stream() List<User> assignableUsers = userRepository.findByPrimaryStoreStoreIdAndRoleAndActiveTrue(storeId, User.Role.STAFF);
.filter(es -> isAssignableEmployee(es.getEmployee()))
.map(EmployeeStore::getEmployee)
.collect(Collectors.toList());
if (assignableEmployees.isEmpty()) { if (assignableUsers.isEmpty()) {
return List.of(); return List.of();
} }
List<Long> employeeIds = assignableEmployees.stream().map(Employee::getEmployeeId).collect(Collectors.toList()); List<Long> employeeIds = assignableUsers.stream().map(User::getId).collect(Collectors.toList());
List<Appointment> allAppointments = appointmentRepository.findByEmployeeEmployeeIdInAndAppointmentDate(employeeIds, date); List<Appointment> allAppointments = appointmentRepository.findByEmployeeIdInAndAppointmentDate(employeeIds, date);
java.util.Map<Long, List<Appointment>> appointmentsByEmployee = allAppointments.stream() 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<>(); List<String> availableSlots = new ArrayList<>();
LocalTime startTime = LocalTime.of(9, 0); LocalTime startTime = LocalTime.of(9, 0);
@@ -229,8 +204,8 @@ public class AppointmentService {
LocalTime currentTime = startTime; LocalTime currentTime = startTime;
while (!currentTime.isAfter(latestStart)) { while (!currentTime.isAfter(latestStart)) {
final LocalTime slotTime = currentTime; final LocalTime slotTime = currentTime;
boolean anyEmployeeAvailable = assignableEmployees.stream().anyMatch(emp -> { boolean anyEmployeeAvailable = assignableUsers.stream().anyMatch(emp -> {
List<Appointment> empAppointments = appointmentsByEmployee.getOrDefault(emp.getEmployeeId(), List.of()); List<Appointment> empAppointments = appointmentsByEmployee.getOrDefault(emp.getId(), List.of());
return isSlotAvailable(empAppointments, service, slotTime, null); return isSlotAvailable(empAppointments, service, slotTime, null);
}); });
@@ -262,20 +237,6 @@ public class AppointmentService {
return pets; 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) { private AppointmentResponse mapToResponse(Appointment appointment) {
List<String> petNames = appointment.getPets().stream() List<String> petNames = appointment.getPets().stream()
.map(Pet::getPetName) .map(Pet::getPetName)
@@ -285,17 +246,9 @@ public class AppointmentService {
.map(Pet::getPetId) .map(Pet::getPetId)
.collect(Collectors.toList()); .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(); AppointmentResponse response = new AppointmentResponse();
response.setAppointmentId(appointment.getAppointmentId()); response.setAppointmentId(appointment.getAppointmentId());
response.setCustomerId(appointment.getCustomer().getCustomerId()); response.setCustomerId(appointment.getCustomer().getId());
response.setCustomerName(appointment.getCustomer().getFirstName() + " " + appointment.getCustomer().getLastName()); response.setCustomerName(appointment.getCustomer().getFirstName() + " " + appointment.getCustomer().getLastName());
response.setStoreId(appointment.getStore().getStoreId()); response.setStoreId(appointment.getStore().getStoreId());
response.setStoreName(appointment.getStore().getStoreName()); response.setStoreName(appointment.getStore().getStoreName());
@@ -304,54 +257,38 @@ public class AppointmentService {
response.setAppointmentDate(appointment.getAppointmentDate()); response.setAppointmentDate(appointment.getAppointmentDate());
response.setAppointmentTime(appointment.getAppointmentTime()); response.setAppointmentTime(appointment.getAppointmentTime());
response.setAppointmentStatus(appointment.getAppointmentStatus()); response.setAppointmentStatus(appointment.getAppointmentStatus());
response.setEmployeeId(appointment.getEmployee().getEmployeeId()); response.setEmployeeId(appointment.getEmployee().getId());
response.setEmployeeName(appointment.getEmployee().getFirstName() + " " + appointment.getEmployee().getLastName()); response.setEmployeeName(appointment.getEmployee().getFirstName() + " " + appointment.getEmployee().getLastName());
response.setPetNames(petNames); response.setPetNames(petNames);
response.setPetIds(petIds); response.setPetIds(petIds);
response.setCustomerPetNames(customerPetNames);
response.setCustomerPetIds(customerPetIds);
response.setCreatedAt(appointment.getCreatedAt()); response.setCreatedAt(appointment.getCreatedAt());
response.setUpdatedAt(appointment.getUpdatedAt()); response.setUpdatedAt(appointment.getUpdatedAt());
return response; return response;
} }
private Employee resolveAppointmentEmployee(Long requestedEmployeeId, Long storeId) { private User resolveAppointmentEmployee(Long requestedEmployeeId, Long storeId) {
List<EmployeeStore> assignableEmployees = employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(storeId).stream() List<User> assignableUsers = userRepository.findByPrimaryStoreStoreIdAndRoleAndActiveTrue(storeId, User.Role.STAFF);
.filter(es -> isAssignableEmployee(es.getEmployee()))
.collect(Collectors.toList());
if (requestedEmployeeId != null) { if (requestedEmployeeId != null) {
Employee employee = employeeRepository.findById(requestedEmployeeId) User employee = userRepository.findById(requestedEmployeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + requestedEmployeeId)); .orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + requestedEmployeeId));
boolean assignedToStore = assignableEmployees.stream() boolean assignedToStore = assignableUsers.stream()
.anyMatch(es -> es.getEmployee().getEmployeeId().equals(requestedEmployeeId)); .anyMatch(u -> u.getId().equals(requestedEmployeeId));
if (!assignedToStore) { if (!assignedToStore) {
throw new IllegalArgumentException("Selected employee is not assignable for the selected store"); throw new IllegalArgumentException("Selected employee is not assignable for the selected store");
} }
return employee; return employee;
} }
return assignableEmployees.stream() return assignableUsers.stream()
.map(EmployeeStore::getEmployee)
.findFirst() .findFirst()
.orElseThrow(() -> new IllegalArgumentException("No assignable staff member is assigned to the selected store")); .orElseThrow(() -> new IllegalArgumentException("No assignable staff member is assigned to the selected store"));
} }
private boolean isAssignableEmployee(Employee employee) { private void validateAvailability(User employee, com.petshop.backend.entity.Service service, LocalDate date, LocalTime time, Long appointmentIdToIgnore) {
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) {
List<Appointment> existingAppointments = appointmentRepository List<Appointment> existingAppointments = appointmentRepository
.findByEmployeeEmployeeIdAndAppointmentDate(employee.getEmployeeId(), date); .findByEmployeeIdAndAppointmentDate(employee.getId(), date);
if (!isSlotAvailable(existingAppointments, service, time, appointmentIdToIgnore)) { if (!isSlotAvailable(existingAppointments, service, time, appointmentIdToIgnore)) {
throw new IllegalArgumentException("The selected employee is already booked for this time slot"); throw new IllegalArgumentException("The selected employee is already booked for this time slot");
} }
@@ -377,11 +314,7 @@ public class AppointmentService {
return; return;
} }
Employee employee = AuthenticationHelper.getAuthenticatedEmployee(userRepository, employeeRepository); if (user.getPrimaryStore() == null || !user.getPrimaryStore().getStoreId().equals(requestedStoreId)) {
EmployeeStore employeeStore = employeeStoreRepository.findByEmployeeEmployeeId(employee.getEmployeeId())
.orElseThrow(() -> new AccessDeniedException("Authenticated staff member is not assigned to a store"));
if (!employeeStore.getStore().getStoreId().equals(requestedStoreId)) {
throw new AccessDeniedException("Staff can only manage appointments for their assigned store"); 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.ConversationResponse;
import com.petshop.backend.dto.chat.MessageResponse; import com.petshop.backend.dto.chat.MessageResponse;
import com.petshop.backend.entity.Conversation; import com.petshop.backend.entity.Conversation;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.User; import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException; import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.ConversationRepository; import com.petshop.backend.repository.ConversationRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.MessageRepository; import com.petshop.backend.repository.MessageRepository;
import com.petshop.backend.repository.UserRepository; import com.petshop.backend.repository.UserRepository;
import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.messaging.simp.SimpMessagingTemplate;
@@ -21,14 +19,12 @@ public class ChatRealtimeService {
private final SimpMessagingTemplate messagingTemplate; private final SimpMessagingTemplate messagingTemplate;
private final ConversationRepository conversationRepository; private final ConversationRepository conversationRepository;
private final MessageRepository messageRepository; private final MessageRepository messageRepository;
private final CustomerRepository customerRepository;
private final UserRepository userRepository; 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.messagingTemplate = messagingTemplate;
this.conversationRepository = conversationRepository; this.conversationRepository = conversationRepository;
this.messageRepository = messageRepository; this.messageRepository = messageRepository;
this.customerRepository = customerRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
} }
@@ -54,13 +50,11 @@ public class ChatRealtimeService {
} }
private void sendConversationToCustomerQueue(ConversationResponse conversation) { private void sendConversationToCustomerQueue(ConversationResponse conversation) {
Customer customer = customerRepository.findById(conversation.getCustomerId()) User customerUser = userRepository.findById(conversation.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found")); .orElseThrow(() -> new ResourceNotFoundException("User not found"));
if (customer.getUserId() == null) { if (customerUser.getUsername() == null) {
return; return;
} }
User customerUser = userRepository.findById(customer.getUserId())
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
messagingTemplate.convertAndSendToUser(customerUser.getUsername(), "/queue/chat/conversations", conversation); 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.MessageResponse;
import com.petshop.backend.dto.chat.UpdateConversationRequest; import com.petshop.backend.dto.chat.UpdateConversationRequest;
import com.petshop.backend.entity.Conversation; import com.petshop.backend.entity.Conversation;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.Message; import com.petshop.backend.entity.Message;
import com.petshop.backend.entity.User; import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException; import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.ConversationRepository; import com.petshop.backend.repository.ConversationRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.MessageRepository; import com.petshop.backend.repository.MessageRepository;
import com.petshop.backend.repository.UserRepository; import com.petshop.backend.repository.UserRepository;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;
@@ -28,16 +26,13 @@ public class ChatService {
private final ConversationRepository conversationRepository; private final ConversationRepository conversationRepository;
private final MessageRepository messageRepository; private final MessageRepository messageRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public ChatService(ConversationRepository conversationRepository, public ChatService(ConversationRepository conversationRepository,
MessageRepository messageRepository, MessageRepository messageRepository,
UserRepository userRepository, UserRepository userRepository) {
CustomerRepository customerRepository) {
this.conversationRepository = conversationRepository; this.conversationRepository = conversationRepository;
this.messageRepository = messageRepository; this.messageRepository = messageRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.customerRepository = customerRepository;
} }
@Transactional @Transactional
@@ -49,11 +44,8 @@ public class ChatService {
throw new AccessDeniedException("Only customers can start new conversations"); 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 conversation = new Conversation();
conversation.setCustomerId(customer.getCustomerId()); conversation.setCustomerId(userId);
conversation.setStatus(Conversation.ConversationStatus.OPEN); conversation.setStatus(Conversation.ConversationStatus.OPEN);
conversation.setMode(Conversation.ConversationMode.AUTOMATED); conversation.setMode(Conversation.ConversationMode.AUTOMATED);
conversation = conversationRepository.save(conversation); conversation = conversationRepository.save(conversation);
@@ -72,9 +64,7 @@ public class ChatService {
List<Conversation> conversations; List<Conversation> conversations;
if (role == User.Role.CUSTOMER) { if (role == User.Role.CUSTOMER) {
Customer customer = customerRepository.findByUserId(userId) conversations = conversationRepository.findByCustomerId(userId);
.orElseThrow(() -> new ResourceNotFoundException("Customer record not found for user"));
conversations = conversationRepository.findByCustomerId(customer.getCustomerId());
} else if (role == User.Role.STAFF) { } else if (role == User.Role.STAFF) {
List<Conversation> assignedToMe = conversationRepository.findByStaffId(userId); List<Conversation> assignedToMe = conversationRepository.findByStaffId(userId);
List<Conversation> unassigned = conversationRepository.findByStaffIdIsNull(); List<Conversation> unassigned = conversationRepository.findByStaffIdIsNull();
@@ -225,9 +215,7 @@ public class ChatService {
} }
if (role == User.Role.CUSTOMER) { if (role == User.Role.CUSTOMER) {
Customer customer = customerRepository.findByUserId(userId) return conversation.getCustomerId().equals(userId);
.orElseThrow(() -> new ResourceNotFoundException("Customer record not found for user"));
return conversation.getCustomerId().equals(customer.getCustomerId());
} }
if (role == User.Role.STAFF) { 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.PetRequest;
import com.petshop.backend.dto.pet.PetResponse; import com.petshop.backend.dto.pet.PetResponse;
import com.petshop.backend.entity.Adoption; import com.petshop.backend.entity.Adoption;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.Pet; import com.petshop.backend.entity.Pet;
import com.petshop.backend.entity.StoreLocation; import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.entity.User; import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException; import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.security.AppPrincipal; import com.petshop.backend.security.AppPrincipal;
import com.petshop.backend.repository.AdoptionRepository; import com.petshop.backend.repository.AdoptionRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.PetRepository; import com.petshop.backend.repository.PetRepository;
import com.petshop.backend.repository.StoreRepository; import com.petshop.backend.repository.StoreRepository;
import com.petshop.backend.repository.UserRepository;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -33,14 +32,14 @@ public class PetService {
private final PetRepository petRepository; private final PetRepository petRepository;
private final AdoptionRepository adoptionRepository; private final AdoptionRepository adoptionRepository;
private final CustomerRepository customerRepository; private final UserRepository userRepository;
private final StoreRepository storeRepository; private final StoreRepository storeRepository;
private final CatalogImageStorageService catalogImageStorageService; 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.petRepository = petRepository;
this.adoptionRepository = adoptionRepository; this.adoptionRepository = adoptionRepository;
this.customerRepository = customerRepository; this.userRepository = userRepository;
this.storeRepository = storeRepository; this.storeRepository = storeRepository;
this.catalogImageStorageService = catalogImageStorageService; this.catalogImageStorageService = catalogImageStorageService;
} }
@@ -188,7 +187,7 @@ public class PetService {
} }
return adoptionRepository.findFirstByPet_IdAndAdoptionStatusOrderByAdoptionDateDesc(pet.getPetId(), "Completed") return adoptionRepository.findFirstByPet_IdAndAdoptionStatusOrderByAdoptionDateDesc(pet.getPetId(), "Completed")
.map(Adoption::getCustomer) .map(Adoption::getCustomer)
.map(customer -> userId.equals(customer.getUserId())) .map(customer -> userId.equals(customer.getId()))
.orElse(false); .orElse(false);
} }
@@ -257,7 +256,7 @@ public class PetService {
} }
private PetResponse mapToResponse(Pet pet) { private PetResponse mapToResponse(Pet pet) {
Customer customer = pet.getCustomer(); User owner = pet.getOwner();
StoreLocation store = pet.getStore(); StoreLocation store = pet.getStore();
return new PetResponse( return new PetResponse(
pet.getPetId(), pet.getPetId(),
@@ -270,8 +269,8 @@ public class PetService {
pet.getImageUrl() != null && !pet.getImageUrl().isBlank() ? "/api/v1/pets/" + pet.getPetId() + "/image" : null, pet.getImageUrl() != null && !pet.getImageUrl().isBlank() ? "/api/v1/pets/" + pet.getPetId() + "/image" : null,
pet.getCreatedAt(), pet.getCreatedAt(),
pet.getUpdatedAt(), pet.getUpdatedAt(),
customer != null ? customer.getCustomerId() : null, owner != null ? owner.getId() : null,
customer != null ? customer.getFirstName() + " " + customer.getLastName() : null, owner != null ? owner.getFirstName() + " " + owner.getLastName() : null,
store != null ? store.getStoreId() : null, store != null ? store.getStoreId() : null,
store != null ? store.getStoreName() : null store != null ? store.getStoreName() : null
); );
@@ -280,11 +279,11 @@ public class PetService {
private void applyOwnerAndStore(Pet pet, PetRequest request) { private void applyOwnerAndStore(Pet pet, PetRequest request) {
if ("owned".equalsIgnoreCase(request.getPetStatus())) { if ("owned".equalsIgnoreCase(request.getPetStatus())) {
if (request.getCustomerId() != null) { 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())); .orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
pet.setCustomer(customer); pet.setOwner(owner);
} else { } else {
pet.setCustomer(null); pet.setOwner(null);
} }
pet.setStore(null); pet.setStore(null);
} else if ("available".equalsIgnoreCase(request.getPetStatus()) || "unadopted".equalsIgnoreCase(request.getPetStatus())) { } else if ("available".equalsIgnoreCase(request.getPetStatus()) || "unadopted".equalsIgnoreCase(request.getPetStatus())) {
@@ -295,14 +294,14 @@ public class PetService {
} else { } else {
pet.setStore(null); pet.setStore(null);
} }
pet.setCustomer(null); pet.setOwner(null);
} else { } else {
if (request.getCustomerId() != null) { 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())); .orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
pet.setCustomer(customer); pet.setOwner(owner);
} else { } else {
pet.setCustomer(null); pet.setOwner(null);
} }
if (request.getStoreId() != null) { if (request.getStoreId() != null) {
StoreLocation store = storeRepository.findById(request.getStoreId()) StoreLocation store = storeRepository.findById(request.getStoreId())
@@ -318,8 +317,8 @@ public class PetService {
if (!"owned".equalsIgnoreCase(normalizeStatus(pet.getPetStatus()))) { if (!"owned".equalsIgnoreCase(normalizeStatus(pet.getPetStatus()))) {
return false; return false;
} }
Customer customer = pet.getCustomer(); User owner = pet.getOwner();
return customer != null && userId.equals(customer.getUserId()); return owner != null && userId.equals(owner.getId());
} }
public record ImagePayload(Resource resource, MediaType mediaType) { public record ImagePayload(Resource resource, MediaType mediaType) {

View File

@@ -46,13 +46,13 @@ public class RefundService {
throw new RuntimeException("Sale has no associated customer"); 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"); throw new RuntimeException("You can only create refunds for your own purchases");
} }
Refund refund = new Refund(); Refund refund = new Refund();
refund.setSaleId(sale.getSaleId()); refund.setSaleId(sale.getSaleId());
refund.setCustomerId(sale.getCustomer().getCustomerId()); refund.setCustomerId(sale.getCustomer().getId());
refund.setReason(request.getReason()); refund.setReason(request.getReason());
refund.setStatus(Refund.RefundStatus.PENDING); refund.setStatus(Refund.RefundStatus.PENDING);

View File

@@ -24,20 +24,14 @@ public class SaleService {
private final ProductRepository productRepository; private final ProductRepository productRepository;
private final StoreRepository storeRepository; private final StoreRepository storeRepository;
private final InventoryRepository inventoryRepository; private final InventoryRepository inventoryRepository;
private final EmployeeRepository employeeRepository;
private final EmployeeStoreRepository employeeStoreRepository;
private final UserRepository userRepository; 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.saleRepository = saleRepository;
this.productRepository = productRepository; this.productRepository = productRepository;
this.storeRepository = storeRepository; this.storeRepository = storeRepository;
this.inventoryRepository = inventoryRepository; this.inventoryRepository = inventoryRepository;
this.employeeRepository = employeeRepository;
this.employeeStoreRepository = employeeStoreRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.customerRepository = customerRepository;
} }
@Transactional(readOnly = true) @Transactional(readOnly = true)
@@ -60,18 +54,16 @@ public class SaleService {
@Transactional @Transactional
public SaleResponse createSale(SaleRequest request) { public SaleResponse createSale(SaleRequest request) {
User user = AuthenticationHelper.getAuthenticatedUser(userRepository); User employee = 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();
StoreLocation store = storeRepository.findById(request.getStoreId()) StoreLocation store = storeRepository.findById(request.getStoreId())
.orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + request.getStoreId())); .orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + request.getStoreId()));
if (user.getRole() == User.Role.STAFF && !employeeStoreId.equals(store.getStoreId())) { if (employee.getRole() == User.Role.STAFF) {
throw new BusinessException("Staff can only create sales for their assigned store"); 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(); Sale sale = new Sale();
@@ -82,7 +74,7 @@ public class SaleService {
sale.setIsRefund(request.getIsRefund() != null ? request.getIsRefund() : false); sale.setIsRefund(request.getIsRefund() != null ? request.getIsRefund() : false);
if (request.getCustomerId() != null) { 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())); .orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
sale.setCustomer(customer); sale.setCustomer(customer);
} }
@@ -185,7 +177,7 @@ public class SaleService {
SaleResponse response = new SaleResponse(); SaleResponse response = new SaleResponse();
response.setSaleId(sale.getSaleId()); response.setSaleId(sale.getSaleId());
response.setSaleDate(sale.getSaleDate()); response.setSaleDate(sale.getSaleDate());
response.setEmployeeId(sale.getEmployee().getEmployeeId()); response.setEmployeeId(sale.getEmployee().getId());
response.setEmployeeName(sale.getEmployee().getFirstName() + " " + sale.getEmployee().getLastName()); response.setEmployeeName(sale.getEmployee().getFirstName() + " " + sale.getEmployee().getLastName());
if (sale.getStore() != null) { 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 UserRepository userRepository;
private final PasswordEncoder passwordEncoder; private final PasswordEncoder passwordEncoder;
private final UserBusinessLinkageService userBusinessLinkageService;
private final StoreRepository storeRepository; 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.userRepository = userRepository;
this.passwordEncoder = passwordEncoder; this.passwordEncoder = passwordEncoder;
this.userBusinessLinkageService = userBusinessLinkageService;
this.storeRepository = storeRepository; this.storeRepository = storeRepository;
} }
@@ -79,8 +77,6 @@ public class UserService {
user = userRepository.save(user); user = userRepository.save(user);
userBusinessLinkageService.syncLinkedRecords(user);
return mapToResponse(user); return mapToResponse(user);
} }
@@ -117,7 +113,6 @@ public class UserService {
} }
user = userRepository.save(user); user = userRepository.save(user);
userBusinessLinkageService.syncLinkedRecords(user);
return mapToResponse(user); return mapToResponse(user);
} }

View File

@@ -1,10 +1,6 @@
package com.petshop.backend.util; 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.entity.User;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.EmployeeRepository;
import com.petshop.backend.repository.UserRepository; import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.security.AppPrincipal; import com.petshop.backend.security.AppPrincipal;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@@ -47,16 +43,4 @@ public class AuthenticationHelper {
return userRepository.findByUsername(username) return userRepository.findByUsername(username)
.orElseThrow(() -> new RuntimeException("User not found: " + 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()));
}
} }