feat: implement User and UserCredential models with repository and service layers
This commit is contained in:
@@ -0,0 +1,73 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.domain.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "app_user")
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "username", nullable = false)
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@Column(name = "email", nullable = false)
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||||
|
private List<UserCredential> credentials = new ArrayList<>();
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UserCredential> getCredentials() {
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCredentials(List<UserCredential> credentials) {
|
||||||
|
this.credentials = credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCredential(UserCredential credential) {
|
||||||
|
this.credentials.add(credential);
|
||||||
|
credential.setUser(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeCredential(UserCredential credential) {
|
||||||
|
this.credentials.remove(credential);
|
||||||
|
credential.setUser(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.domain.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.AttributeOverride;
|
||||||
|
import jakarta.persistence.AttributeOverrides;
|
||||||
|
import jakarta.persistence.EmbeddedId;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.ForeignKey;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.MapsId;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "app_user_credential")
|
||||||
|
public class UserCredential {
|
||||||
|
|
||||||
|
@EmbeddedId
|
||||||
|
@AttributeOverrides({
|
||||||
|
@AttributeOverride(name = "userId", column = @Column(name = "user_id")),
|
||||||
|
@AttributeOverride(name = "provider", column = @Column(name = "provider"))
|
||||||
|
})
|
||||||
|
private UserCredentialId id = new UserCredentialId();
|
||||||
|
|
||||||
|
@MapsId("userId")
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||||
|
@JoinColumn(name = "user_id", nullable = false, foreignKey = @ForeignKey(name = "fk_user_credential_user"))
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
@Column(name = "credential", nullable = false, length = 255)
|
||||||
|
private String credential;
|
||||||
|
|
||||||
|
public UserCredentialId getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(UserCredentialId id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
if (this.id == null) {
|
||||||
|
this.id = new UserCredentialId();
|
||||||
|
}
|
||||||
|
this.id.setUserId(user == null ? null : user.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getUserId() {
|
||||||
|
return id == null ? null : id.getUserId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(Long userId) {
|
||||||
|
if (this.id == null) {
|
||||||
|
this.id = new UserCredentialId();
|
||||||
|
}
|
||||||
|
this.id.setUserId(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProvider() {
|
||||||
|
return id == null ? null : id.getProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProvider(String provider) {
|
||||||
|
if (this.id == null) {
|
||||||
|
this.id = new UserCredentialId();
|
||||||
|
}
|
||||||
|
this.id.setProvider(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCredential() {
|
||||||
|
return credential;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCredential(String credential) {
|
||||||
|
this.credential = credential;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.domain.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.Embeddable;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
public class UserCredentialId implements Serializable {
|
||||||
|
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
private String provider;
|
||||||
|
|
||||||
|
public Long getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(Long userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProvider() {
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProvider(String provider) {
|
||||||
|
this.provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
UserCredentialId that = (UserCredentialId) o;
|
||||||
|
return Objects.equals(userId, that.userId) && Objects.equals(provider, that.provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(userId, provider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.manager;
|
||||||
|
|
||||||
|
import com.onixbyte.deltaforceguide.domain.entity.UserCredential;
|
||||||
|
import com.onixbyte.deltaforceguide.repository.UserCredentialRepository;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class UserCredentialManager {
|
||||||
|
|
||||||
|
private final UserCredentialRepository userCredentialRepository;
|
||||||
|
|
||||||
|
public UserCredentialManager(UserCredentialRepository userCredentialRepository) {
|
||||||
|
this.userCredentialRepository = userCredentialRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<UserCredential> findAllByUserId(Long userId) {
|
||||||
|
return userCredentialRepository.findAllByUserId(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Optional<UserCredential> findByUserIdAndProvider(Long userId, String provider) {
|
||||||
|
return userCredentialRepository.findByUserIdAndProvider(userId, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public UserCredential save(UserCredential userCredential) {
|
||||||
|
return userCredentialRepository.save(userCredential);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteByUserIdAndProvider(Long userId, String provider) {
|
||||||
|
userCredentialRepository.deleteByUserIdAndProvider(userId, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteAllByUserId(Long userId) {
|
||||||
|
userCredentialRepository.deleteAllByUserId(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.manager;
|
||||||
|
|
||||||
|
import com.onixbyte.deltaforceguide.domain.entity.User;
|
||||||
|
import com.onixbyte.deltaforceguide.repository.UserRepository;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class UserManager {
|
||||||
|
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
|
public UserManager(UserRepository userRepository) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Optional<User> findById(Long id) {
|
||||||
|
return userRepository.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<User> findAll() {
|
||||||
|
return userRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Optional<User> findByUsername(String username) {
|
||||||
|
return userRepository.findByUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Optional<User> findByEmail(String email) {
|
||||||
|
return userRepository.findByEmail(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public User save(User user) {
|
||||||
|
return userRepository.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteById(Long id) {
|
||||||
|
userRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.repository;
|
||||||
|
|
||||||
|
import com.onixbyte.deltaforceguide.domain.entity.UserCredential;
|
||||||
|
import com.onixbyte.deltaforceguide.domain.entity.UserCredentialId;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
|
import org.springframework.data.jpa.repository.EntityGraph;
|
||||||
|
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 UserCredentialRepository extends JpaRepository<UserCredential, UserCredentialId> {
|
||||||
|
|
||||||
|
@EntityGraph(attributePaths = {"user"})
|
||||||
|
@Query("""
|
||||||
|
select uc
|
||||||
|
from UserCredential uc
|
||||||
|
where uc.user.id = :userId
|
||||||
|
""")
|
||||||
|
List<UserCredential> findAllByUserId(@Param("userId") Long userId);
|
||||||
|
|
||||||
|
@EntityGraph(attributePaths = {"user"})
|
||||||
|
@Query("""
|
||||||
|
select uc
|
||||||
|
from UserCredential uc
|
||||||
|
where uc.user.id = :userId
|
||||||
|
and uc.id.provider = :provider
|
||||||
|
""")
|
||||||
|
Optional<UserCredential> findByUserIdAndProvider(@Param("userId") Long userId, @Param("provider") String provider);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query("""
|
||||||
|
delete from UserCredential uc
|
||||||
|
where uc.user.id = :userId
|
||||||
|
and uc.id.provider = :provider
|
||||||
|
""")
|
||||||
|
void deleteByUserIdAndProvider(@Param("userId") Long userId, @Param("provider") String provider);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query("""
|
||||||
|
delete from UserCredential uc
|
||||||
|
where uc.user.id = :userId
|
||||||
|
""")
|
||||||
|
void deleteAllByUserId(@Param("userId") Long userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.repository;
|
||||||
|
|
||||||
|
import com.onixbyte.deltaforceguide.domain.entity.User;
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.springframework.data.jpa.repository.EntityGraph;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface UserRepository extends JpaRepository<User, Long> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@EntityGraph(attributePaths = {"credentials"})
|
||||||
|
@NonNull
|
||||||
|
Optional<User> findById(@NonNull Long id);
|
||||||
|
|
||||||
|
@EntityGraph(attributePaths = {"credentials"})
|
||||||
|
Optional<User> findByUsername(String username);
|
||||||
|
|
||||||
|
@EntityGraph(attributePaths = {"credentials"})
|
||||||
|
Optional<User> findByEmail(String email);
|
||||||
|
|
||||||
|
boolean existsByUsername(String username);
|
||||||
|
|
||||||
|
boolean existsByEmail(String email);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.service;
|
||||||
|
|
||||||
|
import com.onixbyte.deltaforceguide.domain.entity.User;
|
||||||
|
import com.onixbyte.deltaforceguide.domain.entity.UserCredential;
|
||||||
|
import com.onixbyte.deltaforceguide.manager.UserCredentialManager;
|
||||||
|
import com.onixbyte.deltaforceguide.manager.UserManager;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserService {
|
||||||
|
|
||||||
|
private final UserManager userManager;
|
||||||
|
private final UserCredentialManager userCredentialManager;
|
||||||
|
|
||||||
|
public UserService(UserManager userManager, UserCredentialManager userCredentialManager) {
|
||||||
|
this.userManager = userManager;
|
||||||
|
this.userCredentialManager = userCredentialManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<User> findAll() {
|
||||||
|
return userManager.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public User queryById(Long id) {
|
||||||
|
return userManager.findById(id)
|
||||||
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found: " + id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public User queryByUsername(String username) {
|
||||||
|
return userManager.findByUsername(username)
|
||||||
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found: " + username));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public User create(User user) {
|
||||||
|
return userManager.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public User update(User user) {
|
||||||
|
if (user.getId() == null || userManager.findById(user.getId()).isEmpty()) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found: " + user.getId());
|
||||||
|
}
|
||||||
|
return userManager.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<UserCredential> findCredentials(Long userId) {
|
||||||
|
ensureUserExists(userId);
|
||||||
|
return userCredentialManager.findAllByUserId(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public UserCredential queryCredential(Long userId, String provider) {
|
||||||
|
ensureUserExists(userId);
|
||||||
|
return userCredentialManager.findByUserIdAndProvider(userId, provider)
|
||||||
|
.orElseThrow(() -> new ResponseStatusException(
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
"User credential not found: userId=" + userId + ", provider=" + provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public UserCredential upsertCredential(Long userId, String provider, String credential) {
|
||||||
|
User user = ensureUserExists(userId);
|
||||||
|
UserCredential userCredential = userCredentialManager.findByUserIdAndProvider(userId, provider)
|
||||||
|
.orElseGet(UserCredential::new);
|
||||||
|
userCredential.setUser(user);
|
||||||
|
userCredential.setProvider(provider);
|
||||||
|
userCredential.setCredential(credential);
|
||||||
|
return userCredentialManager.save(userCredential);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteCredential(Long userId, String provider) {
|
||||||
|
ensureUserExists(userId);
|
||||||
|
userCredentialManager.deleteByUserIdAndProvider(userId, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteById(Long id) {
|
||||||
|
ensureUserExists(id);
|
||||||
|
userCredentialManager.deleteAllByUserId(id);
|
||||||
|
userManager.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private User ensureUserExists(Long userId) {
|
||||||
|
return userManager.findById(userId)
|
||||||
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found: " + userId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
DROP TABLE IF EXISTS app_user;
|
||||||
|
CREATE TABLE app_user
|
||||||
|
(
|
||||||
|
id BIGSERIAL NOT NULL PRIMARY KEY,
|
||||||
|
username VARCHAR(255) NOT NULL,
|
||||||
|
email VARCHAR(255) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS app_user_credential;
|
||||||
|
CREATE TABLE app_user_credential
|
||||||
|
(
|
||||||
|
user_id BIGINT NOT NULL REFERENCES app_user (id),
|
||||||
|
provider VARCHAR(255) NOT NULL,
|
||||||
|
credential VARCHAR(255) NOT NULL,
|
||||||
|
CONSTRAINT app_user_credential_pkey PRIMARY KEY (user_id, provider)
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user