add user phone
This commit is contained in:
@@ -35,6 +35,7 @@ public class DataInitializer implements CommandLineRunner {
|
||||
admin.setPassword(passwordEncoder.encode("admin123"));
|
||||
admin.setEmail("admin@petshop.com");
|
||||
admin.setFullName("Admin User");
|
||||
admin.setPhone("000-000-1000");
|
||||
admin.setRole(User.Role.ADMIN);
|
||||
admin.setActive(true);
|
||||
admin = userRepository.save(admin);
|
||||
@@ -51,6 +52,10 @@ public class DataInitializer implements CommandLineRunner {
|
||||
admin.setEmail("admin@petshop.com");
|
||||
updated = true;
|
||||
}
|
||||
if (admin.getPhone() == null || admin.getPhone().isEmpty()) {
|
||||
admin.setPhone("000-000-1000");
|
||||
updated = true;
|
||||
}
|
||||
if (admin.getActive() == null) {
|
||||
admin.setActive(true);
|
||||
updated = true;
|
||||
@@ -75,6 +80,7 @@ public class DataInitializer implements CommandLineRunner {
|
||||
staff.setPassword(passwordEncoder.encode("staff123"));
|
||||
staff.setEmail("staff@petshop.com");
|
||||
staff.setFullName("Staff User");
|
||||
staff.setPhone("000-000-1001");
|
||||
staff.setRole(User.Role.STAFF);
|
||||
staff.setActive(true);
|
||||
staff = userRepository.save(staff);
|
||||
@@ -91,6 +97,10 @@ public class DataInitializer implements CommandLineRunner {
|
||||
staff.setEmail("staff@petshop.com");
|
||||
updated = true;
|
||||
}
|
||||
if (staff.getPhone() == null || staff.getPhone().isEmpty()) {
|
||||
staff.setPhone("000-000-1001");
|
||||
updated = true;
|
||||
}
|
||||
if (staff.getActive() == null) {
|
||||
staff.setActive(true);
|
||||
updated = true;
|
||||
@@ -115,6 +125,7 @@ public class DataInitializer implements CommandLineRunner {
|
||||
customer.setPassword(passwordEncoder.encode("customer123"));
|
||||
customer.setEmail("customer@petshop.com");
|
||||
customer.setFullName("Test Customer");
|
||||
customer.setPhone("000-000-1002");
|
||||
customer.setRole(User.Role.CUSTOMER);
|
||||
customer.setActive(true);
|
||||
customer = userRepository.save(customer);
|
||||
@@ -131,6 +142,10 @@ public class DataInitializer implements CommandLineRunner {
|
||||
customer.setEmail("customer@petshop.com");
|
||||
updated = true;
|
||||
}
|
||||
if (customer.getPhone() == null || customer.getPhone().isEmpty()) {
|
||||
customer.setPhone("000-000-1002");
|
||||
updated = true;
|
||||
}
|
||||
if (customer.getActive() == null) {
|
||||
customer.setActive(true);
|
||||
updated = true;
|
||||
|
||||
@@ -74,11 +74,19 @@ public class AuthController {
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
}
|
||||
|
||||
String phone = trimToNull(request.getPhone());
|
||||
if (phone != null && userRepository.findByPhone(phone).isPresent()) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Phone already exists");
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
}
|
||||
|
||||
User user = new User();
|
||||
user.setUsername(request.getUsername());
|
||||
user.setPassword(passwordEncoder.encode(request.getPassword()));
|
||||
user.setEmail(request.getEmail());
|
||||
user.setFullName(request.getFullName());
|
||||
user.setPhone(phone);
|
||||
user.setRole(User.Role.CUSTOMER);
|
||||
user.setActive(true);
|
||||
|
||||
@@ -93,6 +101,7 @@ public class AuthController {
|
||||
savedUser.getId(),
|
||||
savedUser.getUsername(),
|
||||
savedUser.getEmail(),
|
||||
savedUser.getPhone(),
|
||||
savedUser.getRole().name(),
|
||||
token
|
||||
));
|
||||
@@ -145,6 +154,7 @@ public class AuthController {
|
||||
user.getUsername(),
|
||||
user.getEmail(),
|
||||
user.getFullName(),
|
||||
user.getPhone(),
|
||||
user.getAvatarUrl(),
|
||||
user.getRole().name(),
|
||||
employeeStore != null ? employeeStore.getStore().getStoreId() : null,
|
||||
@@ -180,6 +190,20 @@ public class AuthController {
|
||||
user.setFullName(request.getFullName());
|
||||
}
|
||||
|
||||
if (request.getPhone() != null) {
|
||||
String phone = trimToNull(request.getPhone());
|
||||
if (!java.util.Objects.equals(phone, user.getPhone())) {
|
||||
if (phone != null && userRepository.findByPhone(phone)
|
||||
.filter(existing -> !existing.getId().equals(user.getId()))
|
||||
.isPresent()) {
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("message", "Phone already exists");
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||
}
|
||||
user.setPhone(phone);
|
||||
}
|
||||
}
|
||||
|
||||
if (request.getPassword() != null && !request.getPassword().isEmpty()) {
|
||||
user.setPassword(passwordEncoder.encode(request.getPassword()));
|
||||
invalidateToken = true;
|
||||
@@ -190,6 +214,7 @@ public class AuthController {
|
||||
}
|
||||
|
||||
User updatedUser = userRepository.save(user);
|
||||
userBusinessLinkageService.syncLinkedRecords(updatedUser);
|
||||
|
||||
EmployeeStore employeeStore = resolveEmployeeStore(updatedUser);
|
||||
|
||||
@@ -198,6 +223,7 @@ public class AuthController {
|
||||
updatedUser.getUsername(),
|
||||
updatedUser.getEmail(),
|
||||
updatedUser.getFullName(),
|
||||
updatedUser.getPhone(),
|
||||
updatedUser.getAvatarUrl(),
|
||||
updatedUser.getRole().name(),
|
||||
employeeStore != null ? employeeStore.getStore().getStoreId() : null,
|
||||
@@ -215,6 +241,14 @@ public class AuthController {
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String trimmed = value.trim();
|
||||
return trimmed.isEmpty() ? null : trimmed;
|
||||
}
|
||||
|
||||
@PostMapping("/me/avatar")
|
||||
public ResponseEntity<?> uploadAvatar(@RequestParam("avatar") MultipartFile file) {
|
||||
User user = getAuthenticatedUser();
|
||||
|
||||
@@ -14,6 +14,9 @@ public class ProfileUpdateRequest {
|
||||
@Size(max = 100, message = "Full name must not exceed 100 characters")
|
||||
private String fullName;
|
||||
|
||||
@Size(max = 20, message = "Phone must not exceed 20 characters")
|
||||
private String phone;
|
||||
|
||||
@Size(min = 6, message = "Password must be at least 6 characters")
|
||||
private String password;
|
||||
|
||||
@@ -41,6 +44,14 @@ public class ProfileUpdateRequest {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
@@ -57,12 +68,13 @@ public class ProfileUpdateRequest {
|
||||
return Objects.equals(username, that.username) &&
|
||||
Objects.equals(email, that.email) &&
|
||||
Objects.equals(fullName, that.fullName) &&
|
||||
Objects.equals(phone, that.phone) &&
|
||||
Objects.equals(password, that.password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(username, email, fullName, password);
|
||||
return Objects.hash(username, email, fullName, phone, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,6 +83,7 @@ public class ProfileUpdateRequest {
|
||||
"username='" + username + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
", phone='" + phone + '\'' +
|
||||
", password='" + password + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ public class RegisterRequest {
|
||||
@Size(max = 100, message = "Full name must not exceed 100 characters")
|
||||
private String fullName;
|
||||
|
||||
@NotBlank(message = "Phone is required")
|
||||
@Size(max = 20, message = "Phone must not exceed 20 characters")
|
||||
private String phone;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
@@ -54,6 +58,14 @@ public class RegisterRequest {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@@ -62,12 +74,13 @@ public class RegisterRequest {
|
||||
return Objects.equals(username, that.username) &&
|
||||
Objects.equals(password, that.password) &&
|
||||
Objects.equals(email, that.email) &&
|
||||
Objects.equals(fullName, that.fullName);
|
||||
Objects.equals(fullName, that.fullName) &&
|
||||
Objects.equals(phone, that.phone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(username, password, email, fullName);
|
||||
return Objects.hash(username, password, email, fullName, phone);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,6 +90,7 @@ public class RegisterRequest {
|
||||
", password='" + password + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
", phone='" + phone + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,16 +6,18 @@ public class RegisterResponse {
|
||||
private Long id;
|
||||
private String username;
|
||||
private String email;
|
||||
private String phone;
|
||||
private String role;
|
||||
private String token;
|
||||
|
||||
public RegisterResponse() {
|
||||
}
|
||||
|
||||
public RegisterResponse(Long id, String username, String email, String role, String token) {
|
||||
public RegisterResponse(Long id, String username, String email, String phone, String role, String token) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.email = email;
|
||||
this.phone = phone;
|
||||
this.role = role;
|
||||
this.token = token;
|
||||
}
|
||||
@@ -44,6 +46,14 @@ public class RegisterResponse {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
@@ -68,13 +78,14 @@ public class RegisterResponse {
|
||||
return Objects.equals(id, that.id) &&
|
||||
Objects.equals(username, that.username) &&
|
||||
Objects.equals(email, that.email) &&
|
||||
Objects.equals(phone, that.phone) &&
|
||||
Objects.equals(role, that.role) &&
|
||||
Objects.equals(token, that.token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, username, email, role, token);
|
||||
return Objects.hash(id, username, email, phone, role, token);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,6 +94,7 @@ public class RegisterResponse {
|
||||
"id=" + id +
|
||||
", username='" + username + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", phone='" + phone + '\'' +
|
||||
", role='" + role + '\'' +
|
||||
", token='" + token + '\'' +
|
||||
'}';
|
||||
|
||||
@@ -7,6 +7,7 @@ public class UserInfoResponse {
|
||||
private String username;
|
||||
private String email;
|
||||
private String fullName;
|
||||
private String phone;
|
||||
private String avatarUrl;
|
||||
private String role;
|
||||
private Long storeId;
|
||||
@@ -15,11 +16,12 @@ public class UserInfoResponse {
|
||||
public UserInfoResponse() {
|
||||
}
|
||||
|
||||
public UserInfoResponse(Long id, String username, String email, String fullName, String avatarUrl, String role, Long storeId, String storeName) {
|
||||
public UserInfoResponse(Long id, String username, String email, String fullName, String phone, String avatarUrl, String role, Long storeId, String storeName) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.email = email;
|
||||
this.fullName = fullName;
|
||||
this.phone = phone;
|
||||
this.avatarUrl = avatarUrl;
|
||||
this.role = role;
|
||||
this.storeId = storeId;
|
||||
@@ -58,6 +60,14 @@ public class UserInfoResponse {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getAvatarUrl() {
|
||||
return avatarUrl;
|
||||
}
|
||||
@@ -99,6 +109,7 @@ public class UserInfoResponse {
|
||||
Objects.equals(username, that.username) &&
|
||||
Objects.equals(email, that.email) &&
|
||||
Objects.equals(fullName, that.fullName) &&
|
||||
Objects.equals(phone, that.phone) &&
|
||||
Objects.equals(avatarUrl, that.avatarUrl) &&
|
||||
Objects.equals(role, that.role) &&
|
||||
Objects.equals(storeId, that.storeId) &&
|
||||
@@ -107,7 +118,7 @@ public class UserInfoResponse {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, username, email, fullName, avatarUrl, role, storeId, storeName);
|
||||
return Objects.hash(id, username, email, fullName, phone, avatarUrl, role, storeId, storeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,6 +128,7 @@ public class UserInfoResponse {
|
||||
", username='" + username + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
", phone='" + phone + '\'' +
|
||||
", avatarUrl='" + avatarUrl + '\'' +
|
||||
", role='" + role + '\'' +
|
||||
", storeId=" + storeId +
|
||||
|
||||
@@ -21,6 +21,9 @@ public class UserRequest {
|
||||
@Email(message = "Invalid email format")
|
||||
private String email;
|
||||
|
||||
@Size(max = 20, message = "Phone must not exceed 20 characters")
|
||||
private String phone;
|
||||
|
||||
@NotNull(message = "Role is required")
|
||||
private User.Role role;
|
||||
|
||||
@@ -58,6 +61,14 @@ public class UserRequest {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public User.Role getRole() {
|
||||
return role;
|
||||
}
|
||||
@@ -83,13 +94,14 @@ public class UserRequest {
|
||||
Objects.equals(password, that.password) &&
|
||||
Objects.equals(fullName, that.fullName) &&
|
||||
Objects.equals(email, that.email) &&
|
||||
Objects.equals(phone, that.phone) &&
|
||||
role == that.role &&
|
||||
Objects.equals(active, that.active);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(username, password, fullName, email, role, active);
|
||||
return Objects.hash(username, password, fullName, email, phone, role, active);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,6 +111,7 @@ public class UserRequest {
|
||||
", password='" + password + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", phone='" + phone + '\'' +
|
||||
", role=" + role +
|
||||
", active=" + active +
|
||||
'}';
|
||||
|
||||
@@ -8,6 +8,7 @@ public class UserResponse {
|
||||
private String username;
|
||||
private String fullName;
|
||||
private String email;
|
||||
private String phone;
|
||||
private String role;
|
||||
private Boolean active;
|
||||
private LocalDateTime createdAt;
|
||||
@@ -16,11 +17,12 @@ public class UserResponse {
|
||||
public UserResponse() {
|
||||
}
|
||||
|
||||
public UserResponse(Long id, String username, String fullName, String email, String role, Boolean active, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
public UserResponse(Long id, String username, String fullName, String email, String phone, String role, Boolean active, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.fullName = fullName;
|
||||
this.email = email;
|
||||
this.phone = phone;
|
||||
this.role = role;
|
||||
this.active = active;
|
||||
this.createdAt = createdAt;
|
||||
@@ -59,6 +61,14 @@ public class UserResponse {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
@@ -96,12 +106,12 @@ public class UserResponse {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
UserResponse that = (UserResponse) o;
|
||||
return Objects.equals(id, that.id) && Objects.equals(username, that.username) && Objects.equals(fullName, that.fullName) && Objects.equals(email, that.email) && Objects.equals(role, that.role) && Objects.equals(active, that.active) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
|
||||
return Objects.equals(id, that.id) && Objects.equals(username, that.username) && Objects.equals(fullName, that.fullName) && Objects.equals(email, that.email) && Objects.equals(phone, that.phone) && Objects.equals(role, that.role) && Objects.equals(active, that.active) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, username, fullName, email, role, active, createdAt, updatedAt);
|
||||
return Objects.hash(id, username, fullName, email, phone, role, active, createdAt, updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,6 +121,7 @@ public class UserResponse {
|
||||
", username='" + username + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", phone='" + phone + '\'' +
|
||||
", role='" + role + '\'' +
|
||||
", active=" + active +
|
||||
", createdAt=" + createdAt +
|
||||
|
||||
@@ -27,6 +27,9 @@ public class User {
|
||||
@Column(length = 100)
|
||||
private String fullName;
|
||||
|
||||
@Column(length = 20)
|
||||
private String phone;
|
||||
|
||||
@Column(length = 255)
|
||||
private String avatarUrl;
|
||||
|
||||
@@ -55,12 +58,13 @@ public class User {
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(Long id, String username, String password, String email, String fullName, String avatarUrl, Role role, Boolean active, Integer tokenVersion, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
public User(Long id, String username, String password, String email, String fullName, String phone, String avatarUrl, Role role, Boolean active, Integer tokenVersion, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.email = email;
|
||||
this.fullName = fullName;
|
||||
this.phone = phone;
|
||||
this.avatarUrl = avatarUrl;
|
||||
this.role = role;
|
||||
this.active = active;
|
||||
@@ -109,6 +113,14 @@ public class User {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getAvatarUrl() {
|
||||
return avatarUrl;
|
||||
}
|
||||
@@ -178,6 +190,7 @@ public class User {
|
||||
", password='" + password + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
", phone='" + phone + '\'' +
|
||||
", avatarUrl='" + avatarUrl + '\'' +
|
||||
", role=" + role +
|
||||
", active=" + active +
|
||||
|
||||
@@ -14,9 +14,13 @@ import java.util.Optional;
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
Optional<User> findByUsername(String username);
|
||||
Optional<User> findByEmail(String email);
|
||||
Optional<User> findByPhone(String phone);
|
||||
boolean existsByUsername(String username);
|
||||
|
||||
@Query("SELECT u FROM User u WHERE " +
|
||||
"LOWER(u.username) LIKE LOWER(CONCAT('%', :q, '%'))")
|
||||
"LOWER(u.username) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
|
||||
"LOWER(COALESCE(u.fullName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
|
||||
"LOWER(COALESCE(u.email, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
|
||||
"LOWER(COALESCE(u.phone, '')) LIKE LOWER(CONCAT('%', :q, '%'))")
|
||||
Page<User> searchUsers(@Param("q") String query, Pageable pageable);
|
||||
}
|
||||
|
||||
@@ -25,88 +25,116 @@ public class UserBusinessLinkageService {
|
||||
|
||||
@Transactional
|
||||
public Employee ensureLinkedEmployee(User user) {
|
||||
// Check if already linked
|
||||
if (user.getId() != null) {
|
||||
var existing = employeeRepository.findByUserId(user.getId());
|
||||
if (existing.isPresent()) {
|
||||
return existing.get();
|
||||
return syncEmployee(existing.get(), user);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for email matches
|
||||
List<Employee> emailMatches = employeeRepository.findAllByEmail(user.getEmail());
|
||||
|
||||
// If exactly one match exists and has no userId, link it
|
||||
if (emailMatches.size() == 1) {
|
||||
Employee employee = emailMatches.get(0);
|
||||
if (employee.getUserId() == null) {
|
||||
employee.setUserId(user.getId());
|
||||
return employeeRepository.save(employee);
|
||||
return syncEmployee(employee, user);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise create a new linked Employee
|
||||
Employee newEmployee = new Employee();
|
||||
newEmployee.setUserId(user.getId());
|
||||
newEmployee.setEmail(user.getEmail());
|
||||
|
||||
// Split fullName into firstName and lastName
|
||||
String[] nameParts = splitFullName(user.getFullName());
|
||||
newEmployee.setFirstName(nameParts[0]);
|
||||
newEmployee.setLastName(nameParts[1]);
|
||||
|
||||
// Set required fields with deterministic values
|
||||
newEmployee.setPhone("000-000-0000");
|
||||
newEmployee.setPhone(normalizePhone(user.getPhone(), "000-000-0000"));
|
||||
newEmployee.setIsActive(true);
|
||||
|
||||
// Map role based on user role
|
||||
if (user.getRole() == User.Role.ADMIN) {
|
||||
newEmployee.setRole("Manager");
|
||||
} else if (user.getRole() == User.Role.STAFF) {
|
||||
newEmployee.setRole("Staff");
|
||||
} else {
|
||||
newEmployee.setRole("Staff"); // fallback
|
||||
newEmployee.setRole("Staff");
|
||||
}
|
||||
|
||||
return employeeRepository.save(newEmployee);
|
||||
return syncEmployee(newEmployee, user);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Customer ensureLinkedCustomer(User user) {
|
||||
// Check if already linked
|
||||
if (user.getId() != null) {
|
||||
var existing = customerRepository.findByUserId(user.getId());
|
||||
if (existing.isPresent()) {
|
||||
return existing.get();
|
||||
return syncCustomer(existing.get(), user);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for email matches
|
||||
List<Customer> emailMatches = customerRepository.findAllByEmail(user.getEmail());
|
||||
|
||||
// If exactly one match exists and has no userId, link it
|
||||
if (emailMatches.size() == 1) {
|
||||
Customer customer = emailMatches.get(0);
|
||||
if (customer.getUserId() == null) {
|
||||
customer.setUserId(user.getId());
|
||||
return customerRepository.save(customer);
|
||||
return syncCustomer(customer, user);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise create a new linked Customer
|
||||
Customer newCustomer = new Customer();
|
||||
newCustomer.setUserId(user.getId());
|
||||
newCustomer.setEmail(user.getEmail());
|
||||
|
||||
// Split fullName into firstName and lastName
|
||||
String[] nameParts = splitFullName(user.getFullName());
|
||||
newCustomer.setFirstName(nameParts[0]);
|
||||
newCustomer.setLastName(nameParts[1]);
|
||||
|
||||
// Set required fields with deterministic values
|
||||
newCustomer.setPhone("000-000-0001");
|
||||
newCustomer.setPhone(normalizePhone(user.getPhone(), "000-000-0001"));
|
||||
|
||||
return customerRepository.save(newCustomer);
|
||||
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]);
|
||||
employee.setPhone(normalizePhone(user.getPhone(), employee.getPhone()));
|
||||
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]);
|
||||
customer.setPhone(normalizePhone(user.getPhone(), customer.getPhone()));
|
||||
return customerRepository.save(customer);
|
||||
}
|
||||
|
||||
private String normalizePhone(String phone, String fallback) {
|
||||
if (phone == null || phone.trim().isEmpty()) {
|
||||
return fallback;
|
||||
}
|
||||
return phone.trim();
|
||||
}
|
||||
|
||||
private String[] splitFullName(String fullName) {
|
||||
@@ -118,11 +146,9 @@ public class UserBusinessLinkageService {
|
||||
int spaceIndex = trimmed.indexOf(' ');
|
||||
|
||||
if (spaceIndex == -1) {
|
||||
// Single token
|
||||
return new String[]{trimmed, "User"};
|
||||
}
|
||||
|
||||
// Multiple tokens
|
||||
String firstName = trimmed.substring(0, spaceIndex).trim();
|
||||
String lastName = trimmed.substring(spaceIndex + 1).trim();
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ 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 UserService {
|
||||
@@ -48,17 +51,15 @@ public class UserService {
|
||||
user.setPassword(passwordEncoder.encode(request.getPassword()));
|
||||
user.setFullName(request.getFullName());
|
||||
user.setEmail(request.getEmail());
|
||||
user.setPhone(trimToNull(request.getPhone()));
|
||||
user.setRole(request.getRole());
|
||||
user.setActive(request.getActive() != null ? request.getActive() : true);
|
||||
|
||||
validateUniquePhone(user.getPhone(), null);
|
||||
|
||||
user = userRepository.save(user);
|
||||
|
||||
// Create or link business entity based on role
|
||||
if (user.getRole() == User.Role.STAFF || user.getRole() == User.Role.ADMIN) {
|
||||
userBusinessLinkageService.ensureLinkedEmployee(user);
|
||||
} else if (user.getRole() == User.Role.CUSTOMER) {
|
||||
userBusinessLinkageService.ensureLinkedCustomer(user);
|
||||
}
|
||||
userBusinessLinkageService.syncLinkedRecords(user);
|
||||
|
||||
return mapToResponse(user);
|
||||
}
|
||||
@@ -80,6 +81,11 @@ public class UserService {
|
||||
}
|
||||
user.setFullName(request.getFullName());
|
||||
user.setEmail(request.getEmail());
|
||||
String phone = trimToNull(request.getPhone());
|
||||
if (!java.util.Objects.equals(user.getPhone(), phone)) {
|
||||
validateUniquePhone(phone, user.getId());
|
||||
}
|
||||
user.setPhone(phone);
|
||||
user.setRole(request.getRole());
|
||||
user.setActive(request.getActive() != null ? request.getActive() : true);
|
||||
if (invalidateToken) {
|
||||
@@ -87,6 +93,7 @@ public class UserService {
|
||||
}
|
||||
|
||||
user = userRepository.save(user);
|
||||
userBusinessLinkageService.syncLinkedRecords(user);
|
||||
return mapToResponse(user);
|
||||
}
|
||||
|
||||
@@ -109,10 +116,30 @@ public class UserService {
|
||||
response.setUsername(user.getUsername());
|
||||
response.setFullName(user.getFullName());
|
||||
response.setEmail(user.getEmail());
|
||||
response.setPhone(user.getPhone());
|
||||
response.setRole(user.getRole().toString());
|
||||
response.setActive(user.getActive());
|
||||
response.setCreatedAt(user.getCreatedAt());
|
||||
response.setUpdatedAt(user.getUpdatedAt());
|
||||
return response;
|
||||
}
|
||||
|
||||
private void validateUniquePhone(String phone, Long currentUserId) {
|
||||
if (phone == null || phone.isBlank()) {
|
||||
return;
|
||||
}
|
||||
userRepository.findByPhone(phone)
|
||||
.filter(existing -> !existing.getId().equals(currentUserId))
|
||||
.ifPresent(existing -> {
|
||||
throw new ResponseStatusException(CONFLICT, "Phone already exists");
|
||||
});
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String trimmed = value.trim();
|
||||
return trimmed.isEmpty() ? null : trimmed;
|
||||
}
|
||||
}
|
||||
|
||||
8
src/main/resources/db/migration/V6__user_phone.sql
Normal file
8
src/main/resources/db/migration/V6__user_phone.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
ALTER TABLE users
|
||||
ADD COLUMN phone VARCHAR(20) NULL AFTER fullName;
|
||||
|
||||
UPDATE users u
|
||||
LEFT JOIN customer c ON c.user_id = u.id
|
||||
LEFT JOIN employee e ON e.user_id = u.id
|
||||
SET u.phone = COALESCE(NULLIF(c.phone, ''), NULLIF(e.phone, ''), u.phone)
|
||||
WHERE u.phone IS NULL OR u.phone = '';
|
||||
Reference in New Issue
Block a user