feat: 初始提交
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
package com.onixbyte.helix;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
|
||||
/**
|
||||
* Application entrance.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @see SpringBootApplication
|
||||
* @see EnableCaching
|
||||
* @see SpringApplication
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@EnableCaching
|
||||
@SpringBootApplication
|
||||
public class HelixApplication {
|
||||
|
||||
/**
|
||||
* Main method that serves as the entry point for the Helix application.
|
||||
*
|
||||
* @param args command-line arguments passed to the application, which can be used to override
|
||||
* default configuration properties or specify runtime options
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(HelixApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.onixbyte.helix.client;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A client class for interacting with Redis, providing simple and type-safe access to common Redis
|
||||
* operations such as setting, retrieving, incrementing, decrementing, and deleting keys.
|
||||
* <p>
|
||||
* This class abstracts the direct usage of {@link RedisTemplate} for value operations.
|
||||
*
|
||||
* @author zihluwang
|
||||
*/
|
||||
@Component
|
||||
public class RedisClient {
|
||||
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
/**
|
||||
* Constructs a new RedisClient with the specified {@link RedisTemplate}.
|
||||
*
|
||||
* @param redisTemplate the template used for Redis interaction
|
||||
*/
|
||||
@Autowired
|
||||
public RedisClient(RedisTemplate<String, Object> redisTemplate) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a value for a given key in Redis.
|
||||
*
|
||||
* @param key the key to set
|
||||
* @param value the value associated with the key
|
||||
* @param <T> the type of the value
|
||||
*/
|
||||
public <T> void set(String key, T value) {
|
||||
redisTemplate.opsForValue().set(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a value for a given key in Redis with a specified expiry timeout.
|
||||
*
|
||||
* @param key the key to set
|
||||
* @param value the value associated with the key
|
||||
* @param timeout the time after which the key should expire
|
||||
* @param <T> the type of the value
|
||||
*/
|
||||
public <T> void set(String key, T value, Duration timeout) {
|
||||
redisTemplate.opsForValue().set(key, value, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value associated with a given key from Redis.
|
||||
* <p>
|
||||
* The returned object is of type {@code Object} and may require manual casting.
|
||||
*
|
||||
* @param key the key to retrieve
|
||||
* @return the value associated with the key, or {@code null} if the key does not exist
|
||||
*/
|
||||
public Object get(String key) {
|
||||
return redisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value associated with a given key from Redis, attempting to cast it to the
|
||||
* specified type.
|
||||
*
|
||||
* @param key the key to retrieve
|
||||
* @param type the class type to cast the retrieved value to
|
||||
* @param <T> the type of the expected value
|
||||
* @return the value associated with the key, cast to type T, or {@code null} if the key does
|
||||
* not exist
|
||||
* @throws IllegalStateException if the retrieved value cannot be cast to the specified type
|
||||
*/
|
||||
public <T> T get(String key, Class<T> type) {
|
||||
var value = redisTemplate.opsForValue().get(key);
|
||||
if (Objects.isNull(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (type.isInstance(value)) {
|
||||
return type.cast(value);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot cast " + value.getClass().getName() + " to " + type.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the value of the key by one.
|
||||
* <p>
|
||||
* If the key does not exist, it is created and set to 0 before the increment operation. If the
|
||||
* value stored at the key is not an integer, an exception may be thrown by Redis.
|
||||
*
|
||||
* @param key the key to increment
|
||||
* @return the new value of the key after the increment
|
||||
*/
|
||||
public Long increment(String key) {
|
||||
return redisTemplate.opsForValue().increment(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the value of the key by one.
|
||||
* <p>
|
||||
* If the key does not exist, it is created and set to 0 before the decrement operation. If the
|
||||
* value stored at the key is not an integer, an exception may be thrown by Redis.
|
||||
*
|
||||
* @param key the key to decrement
|
||||
* @return the new value of the key after the decrement
|
||||
*/
|
||||
public Long decrement(String key) {
|
||||
return redisTemplate.opsForValue().decrement(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a key from Redis.
|
||||
*
|
||||
* @param key the key to delete
|
||||
* @return {@code true} if the key was deleted, {@code false} if the key did not exist
|
||||
*/
|
||||
public boolean delete(String key) {
|
||||
return redisTemplate.delete(key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.onixbyte.helix.client;
|
||||
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.onixbyte.helix.domain.entity.User;
|
||||
import com.onixbyte.helix.properties.TokenProperties;
|
||||
import com.onixbyte.helix.utils.DateTimeUtil;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* A client class responsible for generating JSON Web Tokens (JWT) for user authentication and
|
||||
* authorisation purposes.
|
||||
* <p>
|
||||
* It uses the {@link com.auth0.jwt.JWT} library to create and sign tokens based on user details and
|
||||
* configured token properties.
|
||||
*
|
||||
* @author zihluwang
|
||||
*/
|
||||
@Component
|
||||
public class TokenClient {
|
||||
|
||||
private final Algorithm algorithm;
|
||||
private final TokenProperties tokenProperties;
|
||||
|
||||
/**
|
||||
* Constructs a new TokenClient with the necessary algorithm and token properties.
|
||||
*
|
||||
* @param algorithm the signing algorithm used to secure the JWT
|
||||
* @param tokenProperties the configuration properties for the token, such as issuer and
|
||||
* validity period
|
||||
*/
|
||||
public TokenClient(Algorithm algorithm, TokenProperties tokenProperties) {
|
||||
this.algorithm = algorithm;
|
||||
this.tokenProperties = tokenProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a JSON Web Token to the current user.
|
||||
*
|
||||
* @param user the current user for whom the token is being generated
|
||||
* @return a JWT string
|
||||
*/
|
||||
public String generateToken(User user) {
|
||||
var issuedAt = LocalDateTime.now();
|
||||
var expiresAt = issuedAt.plus(tokenProperties.validTime());
|
||||
|
||||
return JWT.create()
|
||||
.withSubject(user.getUsername())
|
||||
.withAudience("Helix Web")
|
||||
.withIssuer(tokenProperties.issuer())
|
||||
.withIssuedAt(DateTimeUtil.asInstant(issuedAt))
|
||||
.withExpiresAt(DateTimeUtil.asInstant(expiresAt))
|
||||
.sign(algorithm);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.onixbyte.helix.common.datetime;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* Utility class providing predefined {@link DateTimeFormatter} instances for common date and
|
||||
* time patterns. These formatters can be used to parse and format {@code java.time} objects
|
||||
* consistently throughout an application.
|
||||
*
|
||||
* @author zihluwang
|
||||
*/
|
||||
public class DateTimeFormatters {
|
||||
|
||||
/**
|
||||
* A {@link DateTimeFormatter} for formatting and parsing full date and time with seconds, using
|
||||
* the pattern "yyyy-MM-dd HH:mm:ss".
|
||||
* <p>
|
||||
* Example: "{@code 2023-10-27 15:30:45}"
|
||||
*/
|
||||
public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* A {@link DateTimeFormatter} for formatting and parsing dates only, using the
|
||||
* pattern "yyyy-MM-dd".
|
||||
* <p>
|
||||
* Example: "{@code 2023-10-27}"
|
||||
*/
|
||||
public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
|
||||
/**
|
||||
* A {@link DateTimeFormatter} for formatting and parsing times only, using the
|
||||
* pattern "HH:mm:ss".
|
||||
* <p>
|
||||
* Example: "{@code 15:30:45}"
|
||||
*/
|
||||
public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
|
||||
|
||||
/**
|
||||
* A {@link DateTimeFormatter} for formatting and parsing year and month only, using the
|
||||
* pattern "yyyy-MM".
|
||||
* <p>
|
||||
* Example: "{@code 2023-10}"
|
||||
*/
|
||||
public static final DateTimeFormatter YEAR_MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.onixbyte.helix.common.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
|
||||
import com.onixbyte.helix.common.datetime.DateTimeFormatters;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
|
||||
public class JacksonModules {
|
||||
|
||||
public static final SimpleModule DATE_TIME_MODULE = initialiseDateTimeModule();
|
||||
|
||||
private static SimpleModule initialiseDateTimeModule() {
|
||||
var javaTimeModule = new JavaTimeModule();
|
||||
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatters.DATE_TIME_FORMATTER));
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatters.DATE_TIME_FORMATTER));
|
||||
|
||||
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatters.DATE_FORMATTER));
|
||||
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatters.DATE_FORMATTER));
|
||||
|
||||
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatters.TIME_FORMATTER));
|
||||
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatters.TIME_FORMATTER));
|
||||
|
||||
return javaTimeModule;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.onixbyte.helix.common.regex;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Patterns {
|
||||
|
||||
public static final Pattern EMAIL = Pattern.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
|
||||
|
||||
public static final Pattern IMAGE_URL = Pattern.compile("^https?://.*\\.(?:png|jpg|jpeg|gif|webp|svg|avif)(?:\\?.*)?$", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
public static final Pattern GRAVATAR_IMAGE_URL = Pattern.compile("^https?://(?:[a-z0-9-]+\\.)?gravatar\\.com/avatar/([a-f0-9]{32})(?:\\?.*)?$", Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.onixbyte.helix.config;
|
||||
|
||||
import com.onixbyte.helix.properties.AssetProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.s3.S3Client;
|
||||
import software.amazon.awssdk.services.s3.S3Configuration;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Configuration class for asset storage services.
|
||||
* <p>
|
||||
* Enables configuration properties for S3 file storage services. Individual service beans are
|
||||
* created by their respective service classes to better support conditional configuration.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({AssetProperties.class})
|
||||
public class AssetConfig {
|
||||
|
||||
/**
|
||||
* S3Client to store assets into S3 service.
|
||||
*
|
||||
* @param assetProperties asset properties
|
||||
* @return an S3 Client reference to custom S3 configuration properties
|
||||
*/
|
||||
@Bean
|
||||
public S3Client s3Client(AssetProperties assetProperties) {
|
||||
// initialise AWS credentials
|
||||
var credentials = AwsBasicCredentials.create(
|
||||
assetProperties.accessKeyId(),
|
||||
assetProperties.secretAccessKey()
|
||||
);
|
||||
|
||||
// prepare s3 client
|
||||
var s3ClientBuilder = S3Client.builder()
|
||||
.region(Region.of(assetProperties.region()))
|
||||
.credentialsProvider(StaticCredentialsProvider.create(credentials));
|
||||
|
||||
// override endpoint
|
||||
Optional.ofNullable(assetProperties.endpoint())
|
||||
.ifPresent((endpoint) -> {
|
||||
try {
|
||||
s3ClientBuilder.endpointOverride(new URI(endpoint));
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException("Endpoint is not valid.");
|
||||
}
|
||||
});
|
||||
|
||||
// set path style
|
||||
s3ClientBuilder.serviceConfiguration(S3Configuration.builder()
|
||||
.pathStyleAccessEnabled(assetProperties.pathStyle())
|
||||
.build()
|
||||
);
|
||||
|
||||
return s3ClientBuilder.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.onixbyte.helix.config;
|
||||
|
||||
import com.onixbyte.helix.properties.ApplicationProperties;
|
||||
import com.onixbyte.helix.properties.MsalProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configuration class for authentication-related components and properties.
|
||||
* <p>
|
||||
* This configuration class is responsible for enabling and managing custom configuration properties
|
||||
* related to authentication mechanisms within the Helix application. It specifically enables the
|
||||
* {@link MsalProperties} configuration properties to support Microsoft Authentication Library
|
||||
* (MSAL) integration.
|
||||
* <p>
|
||||
* The class serves as a central point for authentication configuration, ensuring that all
|
||||
* authentication-related properties are properly loaded and made available to the Spring
|
||||
* application context.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @since 1.0.0
|
||||
* @see MsalProperties
|
||||
* @see EnableConfigurationProperties
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({MsalProperties.class, ApplicationProperties.class})
|
||||
public class AuthenticationConfig {
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.onixbyte.helix.config;
|
||||
|
||||
import com.onixbyte.helix.extension.redis.serializer.JacksonSerialiser;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* Configuration class for Redis-based caching components.
|
||||
* <p>
|
||||
* This configuration class provides beans for Redis cache management and template operations
|
||||
* within the Helix application. It configures custom serialisation strategies using
|
||||
* {@link GenericJackson2JsonRedisSerializer} for values and string serialisation for keys,
|
||||
* ensuring optimal performance and compatibility with JSON-based data structures.
|
||||
* <p>
|
||||
* The configuration includes:
|
||||
* <ul>
|
||||
* <li>Custom {@link RedisCacheManager} with JSON serialisation support</li>
|
||||
* <li>Configured {@link RedisTemplate} for direct Redis operations</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author zihluwang
|
||||
* @see RedisCacheManager
|
||||
* @see RedisTemplate
|
||||
* @see GenericJackson2JsonRedisSerializer
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration
|
||||
public class CacheConfig {
|
||||
|
||||
/**
|
||||
* Creates a custom Redis cache manager with JSON serialisation support.
|
||||
* <p>
|
||||
* This method configures a {@link RedisCacheManager} that uses string serialisation for cache
|
||||
* keys and {@link GenericJackson2JsonRedisSerializer} for cache values. This setup ensures that
|
||||
* complex objects can be stored and retrieved from Redis cache whilst maintaining readability
|
||||
* and compatibility with JSON-based systems.
|
||||
*
|
||||
* @param connectionFactory the Redis connection factory used to establish connections
|
||||
* @return a configured {@link RedisCacheManager} with custom serialisation settings
|
||||
* @see RedisCacheManager
|
||||
* @see GenericJackson2JsonRedisSerializer
|
||||
* @see RedisSerializationContext
|
||||
*/
|
||||
@Bean
|
||||
public RedisCacheManager cacheManager(
|
||||
RedisConnectionFactory connectionFactory
|
||||
) {
|
||||
var _keySerializer = RedisSerializer.string();
|
||||
|
||||
var cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
|
||||
.serializeKeysWith(RedisSerializationContext.SerializationPair
|
||||
.fromSerializer(_keySerializer))
|
||||
.serializeValuesWith(RedisSerializationContext.SerializationPair
|
||||
.fromSerializer(JacksonSerialiser.INSTANCE))
|
||||
.entryTtl(Duration.ofMinutes(90L));
|
||||
|
||||
return RedisCacheManager.RedisCacheManagerBuilder
|
||||
.fromConnectionFactory(connectionFactory)
|
||||
.cacheDefaults(cacheConfiguration)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Redis template for direct Redis operations with custom serialisation.
|
||||
* <p>
|
||||
* This method configures a {@link RedisTemplate} that uses string serialisation for keys
|
||||
* and {@link GenericJackson2JsonRedisSerializer} for values. This template provides low-level
|
||||
* access to Redis operations whilst ensuring consistent serialisation strategies across
|
||||
* the application.
|
||||
* <p>
|
||||
* The template is fully configured and ready for use after bean creation.
|
||||
*
|
||||
* @param connectionFactory the Redis connection factory used to establish connections
|
||||
* @return a fully configured {@link RedisTemplate} for Redis operations
|
||||
* @see RedisTemplate
|
||||
* @see GenericJackson2JsonRedisSerializer
|
||||
* @see RedisSerializer
|
||||
*/
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(
|
||||
RedisConnectionFactory connectionFactory
|
||||
) {
|
||||
var redisTemplate = new RedisTemplate<String, Object>();
|
||||
redisTemplate.setConnectionFactory(connectionFactory);
|
||||
redisTemplate.setKeySerializer(RedisSerializer.string());
|
||||
redisTemplate.setValueSerializer(JacksonSerialiser.INSTANCE);
|
||||
redisTemplate.afterPropertiesSet();
|
||||
return redisTemplate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.onixbyte.helix.config;
|
||||
|
||||
import com.onixbyte.captcha.Producer;
|
||||
import com.onixbyte.captcha.impl.DefaultCaptchaProducer;
|
||||
import com.onixbyte.captcha.text.impl.DefaultTextProducer;
|
||||
import com.onixbyte.helix.properties.CaptchaProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({CaptchaProperties.class})
|
||||
public class CaptchaConfig {
|
||||
|
||||
@Bean
|
||||
public Producer producer(CaptchaProperties captchaProperties) {
|
||||
var textProducer = DefaultTextProducer.builder()
|
||||
.length(captchaProperties.length())
|
||||
.build();
|
||||
return DefaultCaptchaProducer.builder()
|
||||
.textProducer(textProducer)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.onixbyte.helix.config;
|
||||
|
||||
import com.onixbyte.identitygenerator.IdentityGenerator;
|
||||
import com.onixbyte.identitygenerator.impl.SequentialUuidGenerator;
|
||||
import com.onixbyte.identitygenerator.impl.SnowflakeIdentityGenerator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configuration class for GUID (Globally Unique Identifier) generation components.
|
||||
* <p>
|
||||
* This configuration class provides beans for generating unique identifiers throughout the
|
||||
* Helix application. It utilises the Snowflake algorithm implementation to ensure globally unique,
|
||||
* time-ordered identifiers that are suitable for distributed systems.
|
||||
* <p>
|
||||
* The Snowflake algorithm generates 64-bit identifiers composed of:
|
||||
* <ul>
|
||||
* <li>Timestamp (41 bits) - milliseconds since epoch</li>
|
||||
* <li>Machine ID (10 bits) - identifies the generating machine</li>
|
||||
* <li>Sequence number (12 bits) - counter for same millisecond</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author zihluwang
|
||||
* @since 1.0.0
|
||||
* @see IdentityGenerator
|
||||
* @see SnowflakeIdentityGenerator
|
||||
*/
|
||||
@Configuration
|
||||
public class GuidConfig {
|
||||
|
||||
/**
|
||||
* Creates a Snowflake-based identity generator for user IDs.
|
||||
* <p>
|
||||
* This method configures a {@link SnowflakeIdentityGenerator} with machine ID and data centre
|
||||
* ID both set to 0. The generator produces unique 64-bit Long identifiers suitable for user
|
||||
* entity primary keys in distributed environments.
|
||||
* <p>
|
||||
* The generated IDs are:
|
||||
* <ul>
|
||||
* <li>Globally unique across all instances</li>
|
||||
* <li>Time-ordered (newer IDs have higher values)</li>
|
||||
* <li>Highly performant with minimal coordination overhead</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return a configured {@link SnowflakeIdentityGenerator} instance for generating user IDs
|
||||
* @see SnowflakeIdentityGenerator
|
||||
* @see IdentityGenerator
|
||||
*/
|
||||
@Bean
|
||||
public IdentityGenerator<Long> userIdentityGenerator() {
|
||||
return new SnowflakeIdentityGenerator(0x0, 0x0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.onixbyte.helix.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.onixbyte.helix.common.jackson.JacksonModules;
|
||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class JacksonConfig {
|
||||
|
||||
@Bean
|
||||
public Jackson2ObjectMapperBuilderCustomizer jacksonCustomiser() {
|
||||
return (builder) -> {
|
||||
builder.modules(JacksonModules.DATE_TIME_MODULE);
|
||||
builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.onixbyte.helix.config;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@MapperScan(basePackages = {"com.onixbyte.helix.mapper"})
|
||||
public class MyBatisConfig {
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
package com.onixbyte.helix.config;
|
||||
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.onixbyte.helix.filter.TokenAuthenticationFilter;
|
||||
import com.onixbyte.helix.properties.CorsProperties;
|
||||
import com.onixbyte.helix.properties.TokenProperties;
|
||||
import com.onixbyte.helix.security.entrypoint.UnauthorisedAuthenticationEntryPoint;
|
||||
import com.onixbyte.helix.security.provider.UsernamePasswordAuthenticationProvider;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Configuration class for Spring Security components and policies.
|
||||
* <p>
|
||||
* This configuration class establishes comprehensive security settings for the Helix application,
|
||||
* including CORS policies, authentication mechanisms, password encoding, and JWT token handling.
|
||||
* It configures a stateless security architecture suitable for modern web applications and APIs.
|
||||
* <p>
|
||||
* Key security features configured:
|
||||
* <ul>
|
||||
* <li>CORS (Cross-Origin Resource Sharing) configuration</li>
|
||||
* <li>Stateless session management</li>
|
||||
* <li>JWT-based authentication with HMAC256 algorithm</li>
|
||||
* <li>BCrypt password encoding</li>
|
||||
* <li>Method-level security annotations</li>
|
||||
* <li>Custom authentication providers</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author zihluwang
|
||||
* @see EnableWebSecurity
|
||||
* @see EnableMethodSecurity
|
||||
* @see TokenProperties
|
||||
* @see CorsProperties
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity
|
||||
@EnableConfigurationProperties({TokenProperties.class, CorsProperties.class})
|
||||
public class SecurityConfig {
|
||||
|
||||
/**
|
||||
* Creates a CORS configuration source based on application properties.
|
||||
* <p>
|
||||
* This method configures Cross-Origin Resource Sharing (CORS) policies using the
|
||||
* {@link CorsProperties} configuration. It sets up allowed origins, headers, methods,
|
||||
* credentials handling, and other CORS-related settings to enable secure cross-origin requests
|
||||
* from web browsers.
|
||||
* <p>
|
||||
* The configuration is applied globally to all endpoints (/**) within the application.
|
||||
*
|
||||
* @param properties the CORS configuration properties containing allowed origins, headers,
|
||||
* methods, etc
|
||||
* @return a configured {@link CorsConfigurationSource} for handling cross-origin requests
|
||||
* @see CorsProperties
|
||||
* @see CorsConfiguration
|
||||
* @see UrlBasedCorsConfigurationSource
|
||||
*/
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfigurationSource(
|
||||
CorsProperties properties
|
||||
) {
|
||||
var corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.setAllowCredentials(properties.allowCredentials());
|
||||
corsConfiguration.setAllowedOrigins(List.of(properties.allowedOrigins()));
|
||||
corsConfiguration.setAllowedHeaders(List.of(properties.allowedHeaders()));
|
||||
corsConfiguration.setAllowedMethods(Stream.of(properties.allowedMethods())
|
||||
.map(HttpMethod::name)
|
||||
.toList());
|
||||
corsConfiguration.setMaxAge(properties.maxAge());
|
||||
corsConfiguration.setAllowPrivateNetwork(properties.allowPrivateNetwork());
|
||||
corsConfiguration.setExposedHeaders(List.of(properties.exposedHeaders()));
|
||||
|
||||
var corsConfigurationSource = new UrlBasedCorsConfigurationSource();
|
||||
corsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
|
||||
return corsConfigurationSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the Spring Security filter chain for HTTP requests.
|
||||
* <p>
|
||||
* This method establishes the core security policies for the application, including:
|
||||
* <ul>
|
||||
* <li>CORS configuration integration</li>
|
||||
* <li>CSRF protection disabled (suitable for stateless APIs)</li>
|
||||
* <li>Stateless session management</li>
|
||||
* <li>Request authorization rules with public and protected endpoints</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* The configuration permits access to error pages and authentication endpoints whilst requiring
|
||||
* authentication for all other requests. Logout endpoints require authentication to prevent
|
||||
* unauthorised session termination.
|
||||
*
|
||||
* @param httpSecurity the HTTP security configuration builder
|
||||
* @param corsConfigurationSource the CORS configuration source for cross-origin requests
|
||||
* @return a configured {@link SecurityFilterChain} for processing HTTP requests
|
||||
* @throws Exception if any exception occurs during security filter chain construction
|
||||
* @see HttpSecurity
|
||||
* @see SecurityFilterChain
|
||||
* @see SessionCreationPolicy#STATELESS
|
||||
*/
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(
|
||||
HttpSecurity httpSecurity,
|
||||
CorsConfigurationSource corsConfigurationSource,
|
||||
TokenAuthenticationFilter tokenAuthenticationFilter,
|
||||
UnauthorisedAuthenticationEntryPoint unauthorisedAuthenticationEntryPoint
|
||||
) throws Exception {
|
||||
return httpSecurity
|
||||
.cors((cors) -> cors
|
||||
.configurationSource(corsConfigurationSource))
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.sessionManagement((customiser) -> customiser
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
.authorizeHttpRequests((customiser) -> customiser
|
||||
.requestMatchers("/error", "/error/**").permitAll()
|
||||
.requestMatchers("/captcha", "/captcha/**").permitAll()
|
||||
.requestMatchers("/auth/**").permitAll()
|
||||
.requestMatchers("/auth/logout").authenticated()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.exceptionHandling((exceptionHandling) -> exceptionHandling
|
||||
.authenticationEntryPoint(unauthorisedAuthenticationEntryPoint))
|
||||
.addFilterAfter(tokenAuthenticationFilter, ExceptionTranslationFilter.class)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a password encoder for secure password hashing.
|
||||
* <p>
|
||||
* This method provides a {@link BCryptPasswordEncoder} instance that uses the BCrypt hashing
|
||||
* algorithm to securely encode passwords. BCrypt is a adaptive hash function designed for
|
||||
* password hashing that includes a salt to protect against rainbow table attacks and is
|
||||
* computationally expensive to resist brute-force attacks.
|
||||
* <p>
|
||||
* The encoder is used throughout the application for password verification during
|
||||
* authentication and for encoding new passwords during user registration.
|
||||
*
|
||||
* @return a {@link BCryptPasswordEncoder} instance for secure password operations
|
||||
* @see BCryptPasswordEncoder
|
||||
* @see PasswordEncoder
|
||||
*/
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an authentication manager with custom authentication providers.
|
||||
* <p>
|
||||
* This method configures a {@link ProviderManager} that coordinates multiple authentication
|
||||
* providers to handle different authentication mechanisms within the application. The manager
|
||||
* attempts authentication using each configured provider until one succeeds or all fail.
|
||||
* <p>
|
||||
* Currently configured for extensibility to support various authentication providers such as
|
||||
* Microsoft Entra ID, local database authentication, or other identity providers as needed.
|
||||
*
|
||||
* @return a {@link ProviderManager} instance configured with authentication providers
|
||||
* @see ProviderManager
|
||||
* @see AuthenticationManager
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager(
|
||||
UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider
|
||||
) {
|
||||
return new ProviderManager(
|
||||
usernamePasswordAuthenticationProvider
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the JWT signing algorithm using application token properties.
|
||||
* <p>
|
||||
* This method configures an HMAC256 algorithm instance using the secret key specified in
|
||||
* the {@link TokenProperties}. The algorithm is used for signing and verifying JWT tokens
|
||||
* throughout the application, ensuring token integrity and authenticity.
|
||||
* <p>
|
||||
* HMAC256 provides a good balance of security and performance for JWT token signing in
|
||||
* most applications.
|
||||
*
|
||||
* @param properties the token configuration properties containing the signing secret
|
||||
* @return a configured {@link Algorithm} instance for JWT token operations
|
||||
* @see Algorithm
|
||||
* @see TokenProperties
|
||||
*/
|
||||
@Bean
|
||||
public Algorithm algorithm(TokenProperties properties) {
|
||||
return Algorithm.HMAC256(properties.secret());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.onixbyte.helix.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
|
||||
@Configuration
|
||||
@EnableJpaRepositories(basePackages = {"com.onixbyte.helix.repository"})
|
||||
public class SpringDataConfig {
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.onixbyte.helix.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* Configuration class for Spring WebFlux reactive web components.
|
||||
* <p>
|
||||
* This configuration class provides beans for reactive web programming components within the Helix
|
||||
* application. It configures WebClient instances and other reactive web-related components that
|
||||
* enable non-blocking, asynchronous HTTP communication with external services.
|
||||
* <p>
|
||||
* The configuration supports:
|
||||
* <ul>
|
||||
* <li>Reactive HTTP client configuration</li>
|
||||
* <li>Non-blocking I/O operations</li>
|
||||
* <li>Asynchronous request/response handling</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author zihluwang
|
||||
* @since 1.0.0
|
||||
* @see WebClient
|
||||
* @see Configuration
|
||||
*/
|
||||
@Configuration
|
||||
public class WebFluxConfig {
|
||||
|
||||
/**
|
||||
* Creates a reactive WebClient for HTTP communication with external services.
|
||||
* <p>
|
||||
* This method configures a {@link WebClient} instance that provides a modern, reactive approach
|
||||
* to HTTP client operations. The WebClient supports non-blocking I/O and is built on top of
|
||||
* Reactor Netty, making it suitable for high-performance, scalable applications.
|
||||
* <p>
|
||||
* The client is configured with default settings and can be used throughout the application for
|
||||
* making HTTP requests to external APIs, microservices, or other web resources in a reactive,
|
||||
* non-blocking manner.
|
||||
* <p>
|
||||
* Key features:
|
||||
* <ul>
|
||||
* <li>Non-blocking I/O operations</li>
|
||||
* <li>Reactive streams support</li>
|
||||
* <li>Built-in support for JSON serialisation/deserialisation</li>
|
||||
* <li>Configurable timeouts and retry mechanisms</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return a configured {@link WebClient} instance for reactive HTTP operations
|
||||
* @see WebClient
|
||||
* @see WebClient.Builder
|
||||
*/
|
||||
@Bean
|
||||
public WebClient webClient() {
|
||||
return WebClient.builder()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.onixbyte.helix.constant;
|
||||
|
||||
public class AssetPrefix {
|
||||
|
||||
public static final String UPLOADS = "uploads";
|
||||
|
||||
public static final String AVATARS = "avatars";
|
||||
|
||||
public static final String PROFILES = "profiles";
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.onixbyte.helix.constant;
|
||||
|
||||
public class CacheName {
|
||||
|
||||
public static final String USER = "user";
|
||||
|
||||
public static final String AUTHORITIES_OF_USER = "user-authorities";
|
||||
|
||||
public static final String ASSET = "asset";
|
||||
|
||||
public static final String SETTING = "setting";
|
||||
|
||||
public static final String CAPTCHA = "captcha";
|
||||
|
||||
public static final String CAPTCHA_SETTING = "captcha-setting";
|
||||
|
||||
public static final String AUTH_SETTING = "auth-setting";
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.onixbyte.helix.constant;
|
||||
|
||||
/**
|
||||
* Constants for external host configurations and endpoints.
|
||||
* <p>
|
||||
* This utility class provides centralised definitions for external service hosts, API endpoints,
|
||||
* and third-party integration points used throughout the Helix application. It serves as a single
|
||||
* source of truth for external service configurations, promoting maintainability and consistency.
|
||||
* <p>
|
||||
* The class is designed to hold static final constants representing:
|
||||
* <ul>
|
||||
* <li>External API base URLs</li>
|
||||
* <li>Third-party service endpoints</li>
|
||||
* <li>Integration service hosts</li>
|
||||
* <li>External resource locations</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* This class cannot be instantiated as it serves purely as a constant container.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public final class ExternalHost {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.onixbyte.helix.constant;
|
||||
|
||||
public interface FileType {
|
||||
|
||||
String getExtension();
|
||||
|
||||
enum Image implements FileType {
|
||||
JPEG("jpeg"),
|
||||
PNG("png")
|
||||
;
|
||||
|
||||
private final String extension;
|
||||
|
||||
Image(String extension) {
|
||||
this.extension = extension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExtension() {
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.onixbyte.helix.constant;
|
||||
|
||||
import com.onixbyte.helix.config.AuthenticationConfig;
|
||||
|
||||
/**
|
||||
* Enumeration of supported identity providers for user authentication.
|
||||
* <p>
|
||||
* This enumeration defines the various identity providers that the Helix application supports for
|
||||
* user authentication and authorisation. Each provider represents a different authentication
|
||||
* mechanism or external identity service that can be used to verify user credentials and establish
|
||||
* user sessions.
|
||||
* <p>
|
||||
* The application supports both local authentication (using internal user database) and external
|
||||
* identity providers (such as Microsoft Entra ID) to provide flexible authentication options for
|
||||
* different deployment scenarios and organisational requirements.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @since 1.0.0
|
||||
* @see AuthenticationConfig
|
||||
*/
|
||||
public enum IdentityProvider {
|
||||
|
||||
/**
|
||||
* Local identity provider using the application's internal user database.
|
||||
* <p>
|
||||
* This provider authenticates users against locally stored credentials, typically using
|
||||
* username/email and password combinations. User accounts are managed entirely within the Helix
|
||||
* application's database.
|
||||
*/
|
||||
LOCAL,
|
||||
|
||||
/**
|
||||
* Microsoft Entra ID (formerly Azure Active Directory) identity provider.
|
||||
* <p>
|
||||
* This provider enables authentication through Microsoft's cloud-based identity and access
|
||||
* management service. Users authenticate using their organisational Microsoft accounts,
|
||||
* supporting features such as single sign-on (SSO) and multi-factor authentication (MFA).
|
||||
*/
|
||||
MICROSOFT_ENTRA_ID
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.onixbyte.helix.constant;
|
||||
|
||||
public class SettingName {
|
||||
|
||||
public static final String CAPTCHA_ENABLED = "captcha-enabled";
|
||||
|
||||
public static final String REGISTER_ENABLED = "register-enabled";
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.onixbyte.helix.constant;
|
||||
|
||||
public enum SettingType {
|
||||
|
||||
BOOLEAN,
|
||||
STRING,
|
||||
INT,
|
||||
;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.onixbyte.helix.constant;
|
||||
|
||||
/**
|
||||
* Enumeration representing general status states for system entities.
|
||||
* <p>
|
||||
* This enumeration provides a standardised way to represent the operational status of various
|
||||
* system entities, resources, or components within the Helix application. It offers a binary state
|
||||
* model that can be applied across different domain objects to indicate their current
|
||||
* operational condition.
|
||||
* <p>
|
||||
* The status values are designed to be generic and reusable across multiple contexts, such as
|
||||
* system configurations, feature toggles, service states, or any other binary operational
|
||||
* indicators within the application.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public enum Status {
|
||||
|
||||
/**
|
||||
* Indicates that the entity is currently active and operational.
|
||||
* <p>
|
||||
* When an entity has an ACTIVE status, it means the entity is:
|
||||
* <ul>
|
||||
* <li>Currently enabled and functioning</li>
|
||||
* <li>Available for use by the system or users</li>
|
||||
* <li>Participating in normal application operations</li>
|
||||
* </ul>
|
||||
*/
|
||||
ACTIVE,
|
||||
|
||||
/**
|
||||
* Indicates that the entity is currently inactive or disabled.
|
||||
* <p>
|
||||
* When an entity has an INACTIVE status, it means the entity is:
|
||||
* <ul>
|
||||
* <li>Currently disabled or not functioning</li>
|
||||
* <li>Temporarily or permanently unavailable</li>
|
||||
* <li>Excluded from normal application operations</li>
|
||||
* </ul>
|
||||
*/
|
||||
INACTIVE
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.onixbyte.helix.constant;
|
||||
|
||||
/**
|
||||
* Enumeration representing the various states of user accounts within the system.
|
||||
* <p>
|
||||
* This enumeration defines the possible status values for user accounts in the Helix application,
|
||||
* providing a standardised way to manage user account lifecycle and access control. Each status
|
||||
* represents a different level of account accessibility and operational capability within
|
||||
* the system.
|
||||
* <p>
|
||||
* User status directly affects authentication, authorisation, and system access permissions.
|
||||
* The status is typically managed through administrative functions and security policies to ensure
|
||||
* proper access control and account management.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @since 1.0.0
|
||||
* @see com.onixbyte.helix.config.SecurityConfiguration
|
||||
*/
|
||||
public enum UserStatus {
|
||||
|
||||
/**
|
||||
* Indicates that the user account is active and fully operational.
|
||||
* <p>
|
||||
* An ACTIVE user account has:
|
||||
* <ul>
|
||||
* <li>Full access to system features and resources</li>
|
||||
* <li>Ability to authenticate and establish sessions</li>
|
||||
* <li>Normal operational privileges as per assigned roles</li>
|
||||
* <li>No restrictions on account usage</li>
|
||||
* </ul>
|
||||
*/
|
||||
ACTIVE,
|
||||
|
||||
/**
|
||||
* Indicates that the user account is inactive or disabled.
|
||||
* <p>
|
||||
* An INACTIVE user account:
|
||||
* <ul>
|
||||
* <li>Cannot authenticate or access the system</li>
|
||||
* <li>Is temporarily or permanently disabled</li>
|
||||
* <li>Retains user data but blocks all system access</li>
|
||||
* <li>May be reactivated by administrators if appropriate</li>
|
||||
* </ul>
|
||||
*/
|
||||
INACTIVE,
|
||||
|
||||
/**
|
||||
* Indicates that the user account is locked due to security concerns.
|
||||
* <p>
|
||||
* A LOCKED user account:
|
||||
* <ul>
|
||||
* <li>Is temporarily blocked from system access</li>
|
||||
* <li>May result from failed authentication attempts</li>
|
||||
* <li>Could be locked due to security policy violations</li>
|
||||
* <li>Requires administrative intervention to unlock</li>
|
||||
* <li>Maintains user data whilst preventing access</li>
|
||||
* </ul>
|
||||
*/
|
||||
LOCKED
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.onixbyte.helix.controller;
|
||||
|
||||
import com.onixbyte.helix.constant.AssetPrefix;
|
||||
import com.onixbyte.helix.domain.web.response.FileUploadResponse;
|
||||
import com.onixbyte.helix.exception.BizException;
|
||||
import com.onixbyte.helix.service.AssetService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* REST controller for file storage operations. Provides endpoints for uploading, downloading, and
|
||||
* deleting assets using the configured storage service.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/assets")
|
||||
public class AssetController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AssetController.class);
|
||||
|
||||
private final AssetService assetService;
|
||||
|
||||
/**
|
||||
* Constructs a new FileController with the specified file service.
|
||||
*
|
||||
* @param assetService the file service to use for file operations
|
||||
*/
|
||||
public AssetController(AssetService assetService) {
|
||||
this.assetService = assetService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a file to the configured storage service.
|
||||
*
|
||||
* @param file the multipart file to upload
|
||||
* @return ResponseEntity containing the file URL and metadata, or error message
|
||||
*/
|
||||
@PostMapping
|
||||
public ResponseEntity<FileUploadResponse> uploadFile(
|
||||
@RequestParam MultipartFile file
|
||||
) {
|
||||
try {
|
||||
if (file.isEmpty()) {
|
||||
throw new BizException(HttpStatus.BAD_REQUEST, "File cannot be empty.");
|
||||
}
|
||||
|
||||
var fileUrl = assetService.uploadFile(AssetPrefix.UPLOADS, file);
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.header("Location", fileUrl)
|
||||
.body(new FileUploadResponse(
|
||||
file.getOriginalFilename(),
|
||||
file.getContentType(),
|
||||
file.getSize(),
|
||||
fileUrl
|
||||
));
|
||||
} catch (Exception e) {
|
||||
log.error("File upload failed: {}", e.getMessage(), e);
|
||||
throw new BizException(HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
"Failed upload file: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an asset by asset ID.
|
||||
*
|
||||
* @param assetId asset ID
|
||||
*/
|
||||
@DeleteMapping("/{id:\\d+}")
|
||||
public void deleteFile(
|
||||
@PathVariable("id") Long assetId
|
||||
) {
|
||||
assetService.deleteAsset(assetId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.onixbyte.helix.controller;
|
||||
|
||||
import com.onixbyte.helix.domain.web.request.UsernamePasswordLoginRequest;
|
||||
import com.onixbyte.helix.domain.web.response.LoginSuccessResponse;
|
||||
import com.onixbyte.helix.service.AuthService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/auth")
|
||||
public class AuthController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AuthController.class);
|
||||
private final AuthService authService;
|
||||
|
||||
public AuthController(AuthService authService) {
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform login with username and password.
|
||||
*
|
||||
* @param request login request
|
||||
* @return detailed user info and authentication token
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
public LoginSuccessResponse loginWithUsernameAndPassword(
|
||||
@Validated @RequestBody UsernamePasswordLoginRequest request
|
||||
) {
|
||||
return authService.login(request);
|
||||
}
|
||||
|
||||
@GetMapping("/register-enabled")
|
||||
public boolean getRegisterEnabled() {
|
||||
return authService.getRegisterEnabled();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.onixbyte.helix.controller;
|
||||
|
||||
import com.onixbyte.helix.domain.web.response.CaptchaResponse;
|
||||
import com.onixbyte.helix.service.CaptchaService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/captcha")
|
||||
public class CaptchaController {
|
||||
|
||||
private final CaptchaService captchaService;
|
||||
|
||||
public CaptchaController(CaptchaService captchaService) {
|
||||
this.captchaService = captchaService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<CaptchaResponse> getCaptcha() {
|
||||
var captchaTuple = captchaService.buildCaptcha();
|
||||
return Optional.ofNullable(captchaTuple)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElseGet(() -> ResponseEntity.noContent().build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.onixbyte.helix.controller;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.Department;
|
||||
import com.onixbyte.helix.domain.model.TreeNode;
|
||||
import com.onixbyte.helix.service.DepartmentService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/departments")
|
||||
public class DepartmentController {
|
||||
|
||||
private final DepartmentService departmentService;
|
||||
|
||||
public DepartmentController(DepartmentService departmentService) {
|
||||
this.departmentService = departmentService;
|
||||
}
|
||||
|
||||
@GetMapping("/tree")
|
||||
public TreeNode<Department> getDepartmentTree() {
|
||||
return departmentService.getDepartmentTree();
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<Department> getDepartments() {
|
||||
return departmentService.getDepartments();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.onixbyte.helix.controller;
|
||||
|
||||
import com.onixbyte.helix.domain.web.response.BizExceptionResponse;
|
||||
import com.onixbyte.helix.exception.BizException;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Global exception handler for the Helix application.
|
||||
* <p>
|
||||
* This controller advice provides centralised exception handling across all controllers in
|
||||
* the application. It intercepts exceptions thrown during request processing and converts them into
|
||||
* appropriate HTTP responses with standardised error formats.
|
||||
* <p>
|
||||
* The controller handles various types of exceptions including:
|
||||
* <ul>
|
||||
* <li>Business logic exceptions ({@link BizException})</li>
|
||||
* <li>Bean validation constraint violations ({@link ConstraintViolationException})</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* All error responses are formatted consistently using {@link BizExceptionResponse} to provide a
|
||||
* uniform API error structure for client applications.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @see BizException
|
||||
* @see BizExceptionResponse
|
||||
* @see RestControllerAdvice
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class ExceptionController {
|
||||
|
||||
|
||||
/**
|
||||
* Handles business logic exceptions thrown throughout the application.
|
||||
* <p>
|
||||
* This method intercepts {@link BizException} instances and converts them into appropriate HTTP
|
||||
* responses. The HTTP status code is determined by the exception's status property, whilst the
|
||||
* error message is extracted from the exception and included in the response body.
|
||||
* <p>
|
||||
* The response includes a timestamp indicating when the error occurred and the specific error
|
||||
* message describing the business logic violation.
|
||||
*
|
||||
* @param ex the business exception that was thrown
|
||||
* @return a {@link ResponseEntity} containing the error response with appropriate HTTP status
|
||||
* and {@link BizExceptionResponse} body
|
||||
* @see BizException
|
||||
* @see BizExceptionResponse
|
||||
*/
|
||||
@ExceptionHandler(BizException.class)
|
||||
public ResponseEntity<BizExceptionResponse> handleBizException(BizException ex) {
|
||||
return ResponseEntity.status(ex.getStatus())
|
||||
.body(new BizExceptionResponse(
|
||||
LocalDateTime.now(),
|
||||
ex.getMessage())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles bean validation constraint violation exceptions.
|
||||
* <p>
|
||||
* This method processes {@link ConstraintViolationException} instances that occur when bean
|
||||
* validation constraints are violated during request processing. It extracts all constraint
|
||||
* violations, formats them into a readable error message, and returns a standardised
|
||||
* error response.
|
||||
* <p>
|
||||
* The error message includes the property path and violation message for each constraint that
|
||||
* was violated, separated by commas for multiple violations. The response is automatically
|
||||
* assigned a {@code 400 Bad Request} status.
|
||||
*
|
||||
* @param ex the constraint violation exception containing validation errors
|
||||
* @return a {@link BizExceptionResponse} containing the formatted validation
|
||||
* error messages and timestamp
|
||||
* @see ConstraintViolationException
|
||||
* @see BizExceptionResponse
|
||||
* @see jakarta.validation.constraints
|
||||
*/
|
||||
@ExceptionHandler(ConstraintViolationException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public BizExceptionResponse handleConstraintViolation(ConstraintViolationException ex) {
|
||||
var errorMessage = ex.getConstraintViolations().stream()
|
||||
.map((violation) -> violation.getPropertyPath() + ": " + violation.getMessage())
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
return new BizExceptionResponse(
|
||||
LocalDateTime.now(),
|
||||
errorMessage
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.onixbyte.helix.controller;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.Menu;
|
||||
import com.onixbyte.helix.domain.model.TreeNode;
|
||||
import com.onixbyte.helix.service.MenuService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/menus")
|
||||
public class MenuController {
|
||||
|
||||
private final MenuService menuService;
|
||||
|
||||
public MenuController(MenuService menuService) {
|
||||
this.menuService = menuService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<TreeNode<Menu>> getMenuTree() {
|
||||
return menuService.getMenuTree();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.onixbyte.helix.controller;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.Position;
|
||||
import com.onixbyte.helix.service.PositionService;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/positions")
|
||||
public class PositionController {
|
||||
|
||||
private final PositionService positionService;
|
||||
|
||||
public PositionController(PositionService positionService) {
|
||||
this.positionService = positionService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Page<Position> getPositions(
|
||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize
|
||||
) {
|
||||
var pageRequest = PageRequest.of(pageNum - 1, pageSize, Sort.by(Sort.Order.asc("id")));
|
||||
return positionService.getPositions(pageRequest);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.onixbyte.helix.controller;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.Role;
|
||||
import com.onixbyte.helix.domain.web.request.QueryRoleRequest;
|
||||
import com.onixbyte.helix.service.RoleService;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/roles")
|
||||
public class RoleController {
|
||||
|
||||
private final RoleService roleService;
|
||||
|
||||
public RoleController(RoleService roleService) {
|
||||
this.roleService = roleService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Page<Role> getRoles(
|
||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
||||
@Validated @ModelAttribute QueryRoleRequest request
|
||||
) {
|
||||
var pageRequest = PageRequest.of(pageNum - 1, pageSize, Sort.by(Sort.Order.asc("id")));
|
||||
return roleService.getRoles(pageRequest, request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.onixbyte.helix.controller;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.Setting;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping
|
||||
public class SettingController {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.onixbyte.helix.controller;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.User;
|
||||
import com.onixbyte.helix.domain.web.request.AddUserRequest;
|
||||
import com.onixbyte.helix.domain.web.request.QueryUserRequest;
|
||||
import com.onixbyte.helix.domain.web.request.ResetPasswordRequest;
|
||||
import com.onixbyte.helix.domain.web.request.UpdateUserRequest;
|
||||
import com.onixbyte.helix.domain.web.response.UserDetailResponse;
|
||||
import com.onixbyte.helix.service.UserService;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/users")
|
||||
public class UserController {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
public UserController(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user list.
|
||||
*
|
||||
* @param pageNum page number
|
||||
* @param pageSize page size
|
||||
* @return paginated user list
|
||||
*/
|
||||
@PreAuthorize("hasAnyAuthority('system:user:read')")
|
||||
@GetMapping
|
||||
public Page<UserDetailResponse> queryUsers(
|
||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
||||
@Validated @ModelAttribute QueryUserRequest request
|
||||
) {
|
||||
var pageRequest = PageRequest.of(pageNum - 1, pageSize, Sort.by(Sort.Order.asc("id")));
|
||||
return userService.queryUserDetailsPage(pageRequest, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user by user ID.
|
||||
*
|
||||
* @param userId user ID
|
||||
* @return paginated user list
|
||||
*/
|
||||
@PreAuthorize("hasAnyAuthority('system:user:read')")
|
||||
@GetMapping("/{userId:\\d+}")
|
||||
public UserDetailResponse getUserDetailByUserId(@PathVariable Long userId) {
|
||||
return userService.getUserDetailByUserId(userId);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAnyAuthority('system:user:write')")
|
||||
public ResponseEntity<Void> addUser(@Validated @RequestBody AddUserRequest request) {
|
||||
userService.addUser(request);
|
||||
return ResponseEntity.ok(null);
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
public ResponseEntity<Void> editUser(@Validated @RequestBody UpdateUserRequest request) {
|
||||
userService.updateUser(request);
|
||||
return ResponseEntity.ok(null);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAnyAuthority('system:user:reset-password')")
|
||||
@PatchMapping("/reset-password")
|
||||
public ResponseEntity<Void> resetPassword(@Validated @RequestBody ResetPasswordRequest request) {
|
||||
userService.resetPassword(request);
|
||||
return ResponseEntity.ok(null);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAnyAuthority('system:user:write')")
|
||||
@DeleteMapping("/{userId:\\d+}")
|
||||
public ResponseEntity<Void> deleteUser(@PathVariable Long userId) {
|
||||
userService.deleteUser(userId);
|
||||
return ResponseEntity.ok(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.onixbyte.helix.domain.common;
|
||||
|
||||
/**
|
||||
* Represents an element that can be part of a tree structure.
|
||||
* <p>
|
||||
* This interface provides the basic methods necessary for an object to be identified,
|
||||
* linked to a parent, and sorted within its siblings in a tree-like hierarchy.
|
||||
*
|
||||
* @param <K> the type of the key used for identification
|
||||
*/
|
||||
public interface Treeable<K> {
|
||||
/**
|
||||
* Retrieves the sort order value for this element amongst its siblings.
|
||||
*
|
||||
* @return the integer sort value
|
||||
*/
|
||||
Integer getSort();
|
||||
|
||||
/**
|
||||
* Retrieves the unique identifier for this element.
|
||||
*
|
||||
* @return the element's unique key
|
||||
*/
|
||||
K getId();
|
||||
|
||||
/**
|
||||
* Retrieves the identifier of this element's parent.
|
||||
* <p>
|
||||
* If the element is a root element, this method should return {@code null}.
|
||||
*
|
||||
* @return the key of the parent element, or {@code null} if it is a root
|
||||
*/
|
||||
K getParentId();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.onixbyte.helix.domain.database.query.wrapper;
|
||||
|
||||
import com.onixbyte.helix.constant.Status;
|
||||
|
||||
public class QueryRoleWrapper {
|
||||
private String name;
|
||||
private String code;
|
||||
private Status status;
|
||||
|
||||
public QueryRoleWrapper() {
|
||||
}
|
||||
|
||||
public QueryRoleWrapper(String name, String code, Status status) {
|
||||
this.name = name;
|
||||
this.code = code;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.onixbyte.helix.domain.database.query.wrapper;
|
||||
|
||||
import com.onixbyte.helix.constant.UserStatus;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class QueryUserWrapper {
|
||||
private Long departmentId;
|
||||
private String username;
|
||||
private String regionAbbreviation;
|
||||
private String phoneNumber;
|
||||
private UserStatus status;
|
||||
private LocalDateTime createdAtStart;
|
||||
private LocalDateTime createdAtEnd;
|
||||
|
||||
public QueryUserWrapper() {
|
||||
}
|
||||
|
||||
public QueryUserWrapper(Long departmentId, String username, String regionAbbreviation, String phoneNumber, UserStatus status, LocalDateTime createdAtStart, LocalDateTime createdAtEnd) {
|
||||
this.departmentId = departmentId;
|
||||
this.username = username;
|
||||
this.regionAbbreviation = regionAbbreviation;
|
||||
this.phoneNumber = phoneNumber;
|
||||
this.status = status;
|
||||
this.createdAtStart = createdAtStart;
|
||||
this.createdAtEnd = createdAtEnd;
|
||||
}
|
||||
|
||||
public Long getDepartmentId() {
|
||||
return departmentId;
|
||||
}
|
||||
|
||||
public void setDepartmentId(Long departmentId) {
|
||||
this.departmentId = departmentId;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getRegionAbbreviation() {
|
||||
return regionAbbreviation;
|
||||
}
|
||||
|
||||
public void setRegionAbbreviation(String regionAbbreviation) {
|
||||
this.regionAbbreviation = regionAbbreviation;
|
||||
}
|
||||
|
||||
public String getPhoneNumber() {
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
public void setPhoneNumber(String phoneNumber) {
|
||||
this.phoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
public UserStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(UserStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAtStart() {
|
||||
return createdAtStart;
|
||||
}
|
||||
|
||||
public void setCreatedAtStart(LocalDateTime createdAtStart) {
|
||||
this.createdAtStart = createdAtStart;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAtEnd() {
|
||||
return createdAtEnd;
|
||||
}
|
||||
|
||||
public void setCreatedAtEnd(LocalDateTime createdAtEnd) {
|
||||
this.createdAtEnd = createdAtEnd;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package com.onixbyte.helix.domain.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
@Entity
|
||||
@Table(name = "assets")
|
||||
public class Asset {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(unique = true, nullable = false)
|
||||
private String key;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long uploadBy;
|
||||
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime uploadTime;
|
||||
|
||||
public Asset() {
|
||||
}
|
||||
|
||||
public Asset(Long id, String key, Long uploadBy, LocalDateTime uploadTime) {
|
||||
this.id = id;
|
||||
this.key = key;
|
||||
this.uploadBy = uploadBy;
|
||||
this.uploadTime = uploadTime;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public Long getUploadBy() {
|
||||
return uploadBy;
|
||||
}
|
||||
|
||||
public void setUploadBy(Long uploadBy) {
|
||||
this.uploadBy = uploadBy;
|
||||
}
|
||||
|
||||
public LocalDateTime getUploadTime() {
|
||||
return uploadTime;
|
||||
}
|
||||
|
||||
public void setUploadTime(LocalDateTime uploadTime) {
|
||||
this.uploadTime = uploadTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Asset that = (Asset) o;
|
||||
return Objects.equals(id, that.id) && Objects.equals(key, that.key) && Objects.equals(uploadBy, that.uploadBy) && Objects.equals(uploadTime, that.uploadTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, key, uploadBy, uploadTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Attachment{" +
|
||||
"id=" + id +
|
||||
", key='" + key + '\'' +
|
||||
", uploadBy=" + uploadBy +
|
||||
", uploadTime=" + uploadTime +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static AssetBuilder builder() {
|
||||
return new AssetBuilder();
|
||||
}
|
||||
|
||||
public static class AssetBuilder {
|
||||
private Long id;
|
||||
private String key;
|
||||
private Long uploadBy;
|
||||
private LocalDateTime uploadTime;
|
||||
|
||||
private AssetBuilder() {
|
||||
}
|
||||
|
||||
public AssetBuilder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AssetBuilder key(String key) {
|
||||
this.key = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AssetBuilder uploadBy(Long uploadBy) {
|
||||
this.uploadBy = uploadBy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AssetBuilder uploadTime(LocalDateTime uploadTime) {
|
||||
this.uploadTime = uploadTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Asset build() {
|
||||
return new Asset(id, key, uploadBy, uploadTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
package com.onixbyte.helix.domain.entity;
|
||||
|
||||
import com.onixbyte.helix.constant.Status;
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.JdbcType;
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents an authority (permission) entity within the access control system.
|
||||
* <p>
|
||||
* Authorities define specific permissions or capabilities that can be granted to roles. They
|
||||
* represent the finest level of access control granularity, allowing for precise
|
||||
* permission management. Authorities are typically associated with specific actions or resources
|
||||
* within the application.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "authorities")
|
||||
public class Authority {
|
||||
|
||||
/**
|
||||
* The unique identifier for the authority.
|
||||
* <p>
|
||||
* This serves as the primary key in the database and is used for all internal references to the
|
||||
* authority entity.
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* The unique code identifier for the authority.
|
||||
* <p>
|
||||
* This field contains a system-level identifier that uniquely identifies the permission. It is
|
||||
* typically used in code for permission checks and should follow a consistent naming convention
|
||||
* (e.g., "USER_READ", "ADMIN_WRITE").
|
||||
*/
|
||||
@Column(unique = true, nullable = false, length = 128)
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* The human-readable name of the authority.
|
||||
* <p>
|
||||
* This field contains the display name of the authority as it should appear in user interfaces
|
||||
* and administrative panels for permission management.
|
||||
*/
|
||||
@Column(nullable = false, length = 128)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* A detailed description of what this authority grants.
|
||||
* <p>
|
||||
* This field provides additional context about what specific permissions or capabilities this
|
||||
* authority represents, helping administrators understand the implications of granting
|
||||
* this authority.
|
||||
*/
|
||||
@Column
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* The current status of the authority.
|
||||
* <p>
|
||||
* This field determines whether the authority is active, inactive, or in any other state as
|
||||
* defined by the {@link Status} enumeration.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
@Enumerated
|
||||
@JdbcType(PostgreSQLEnumJdbcType.class)
|
||||
private Status status;
|
||||
|
||||
/**
|
||||
* The timestamp when this authority record was created.
|
||||
* <p>
|
||||
* This field is automatically set when the authority entity is first persisted and provides
|
||||
* audit information about when the authority was established.
|
||||
*/
|
||||
@Column
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* The timestamp when this authority record was last updated.
|
||||
* <p>
|
||||
* This field is automatically updated whenever any changes are made to the authority entity and
|
||||
* provides audit information about the most recent modification.
|
||||
*/
|
||||
@Column
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public Authority() {
|
||||
}
|
||||
|
||||
public Authority(Long id, String code, String name, String description, Status status, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.status = status;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Authority authority = (Authority) o;
|
||||
return Objects.equals(id, authority.id) && Objects.equals(code, authority.code) && Objects.equals(name, authority.name) && Objects.equals(description, authority.description) && status == authority.status && Objects.equals(createdAt, authority.createdAt) && Objects.equals(updatedAt, authority.updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, code, name, description, status, createdAt, updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Authority{" +
|
||||
"id=" + id +
|
||||
", code='" + code + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", description='" + description + '\'' +
|
||||
", status=" + status +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Builder instance for constructing Authority objects.
|
||||
*
|
||||
* @return a new AuthorityBuilder instance
|
||||
*/
|
||||
public static AuthorityBuilder builder() {
|
||||
return new AuthorityBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for constructing Authority instances with a fluent API.
|
||||
* <p>
|
||||
* This builder provides a convenient way to construct Authority objects with optional parameters,
|
||||
* following the Builder pattern for improved readability and maintainability.
|
||||
*/
|
||||
public static class AuthorityBuilder {
|
||||
private Long id;
|
||||
private String code;
|
||||
private String name;
|
||||
private String description;
|
||||
private Status status;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private AuthorityBuilder() {
|
||||
}
|
||||
|
||||
public AuthorityBuilder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorityBuilder code(String code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorityBuilder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorityBuilder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorityBuilder status(Status status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorityBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorityBuilder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a new Authority instance with the configured properties.
|
||||
*
|
||||
* @return a new Authority instance
|
||||
*/
|
||||
public Authority build() {
|
||||
return new Authority(id, code, name, description, status, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
|
||||
public GrantedAuthority asGrantedAuthority() {
|
||||
return this::getCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
package com.onixbyte.helix.domain.entity;
|
||||
|
||||
import com.onixbyte.helix.constant.Status;
|
||||
import com.onixbyte.helix.domain.common.Treeable;
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.JdbcType;
|
||||
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a department entity within the organisational hierarchy.
|
||||
* <p>
|
||||
* This entity models departments as hierarchical structures where each department can have a
|
||||
* parent department, creating a tree-like organisational structure. Departments are used to group
|
||||
* users and define organisational boundaries within the Helix system.
|
||||
* </p>
|
||||
*
|
||||
* @author zihluwang
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "departments")
|
||||
public class Department implements Treeable<Long> {
|
||||
|
||||
/**
|
||||
* The unique identifier for the department.
|
||||
* <p>
|
||||
* This serves as the primary key in the database and is used for all
|
||||
* internal references to the department entity.
|
||||
* </p>
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* The name of the department.
|
||||
* <p>
|
||||
* This field contains the human-readable name of the department as it
|
||||
* should appear in the organisational chart and user interfaces.
|
||||
* </p>
|
||||
*/
|
||||
@Column(length = 128, nullable = false)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The identifier of the parent department.
|
||||
* <p>
|
||||
* This field establishes the hierarchical relationship between departments.
|
||||
* A null value indicates that this is a root-level department with no parent.
|
||||
* </p>
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* The sort order for displaying departments.
|
||||
* <p>
|
||||
* This field determines the order in which departments should be displayed
|
||||
* when listed alongside their siblings in the hierarchy. Lower values
|
||||
* indicate higher priority in sorting.
|
||||
* </p>
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* The current status of the department.
|
||||
* <p>
|
||||
* This field determines whether the department is active, inactive, or in any
|
||||
* other state as defined by the {@link Status} enumeration.
|
||||
* </p>
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
@Enumerated
|
||||
@JdbcType(PostgreSQLEnumJdbcType.class)
|
||||
private Status status;
|
||||
|
||||
/**
|
||||
* The timestamp when this department record was created.
|
||||
* <p>
|
||||
* This field is automatically set when the department entity is first persisted
|
||||
* and provides audit information about when the department was established.
|
||||
* </p>
|
||||
*/
|
||||
@Column
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* The timestamp when this department record was last updated.
|
||||
* <p>
|
||||
* This field is automatically updated whenever any changes are made to the
|
||||
* department entity and provides audit information about the most recent modification.
|
||||
* </p>
|
||||
*/
|
||||
@Column
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public Integer getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public void setSort(Integer sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor for Department.
|
||||
* <p>
|
||||
* Creates a new Department instance with all fields initialised to their default values.
|
||||
* This constructor is typically used by JPA and other frameworks for entity instantiation.
|
||||
* </p>
|
||||
*/
|
||||
public Department() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new Department with all specified parameters.
|
||||
* <p>
|
||||
* This constructor allows for the creation of a fully initialised Department entity
|
||||
* with all field values provided at instantiation time.
|
||||
* </p>
|
||||
*
|
||||
* @param id the unique identifier for the department
|
||||
* @param name the name of the department
|
||||
* @param parentId the identifier of the parent department (null for root departments)
|
||||
* @param sort the sort order for display purposes
|
||||
* @param status the current status of the department
|
||||
* @param createdAt the timestamp when the department was created
|
||||
* @param updatedAt the timestamp when the department was last updated
|
||||
*/
|
||||
public Department(Long id, String name, Long parentId, Integer sort, Status status, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.parentId = parentId;
|
||||
this.sort = sort;
|
||||
this.status = status;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Department that = (Department) o;
|
||||
return Objects.equals(id, that.id) && Objects.equals(name, that.name) && Objects.equals(parentId, that.parentId) && Objects.equals(sort, that.sort) && status == that.status && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name, parentId, sort, status, createdAt, updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Department{" +
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' +
|
||||
", parentId=" + parentId +
|
||||
", sort=" + sort +
|
||||
", status=" + status +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Builder instance for constructing Department objects.
|
||||
*
|
||||
* @return a new DepartmentBuilder instance
|
||||
*/
|
||||
public static DepartmentBuilder builder() {
|
||||
return new DepartmentBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for constructing Department instances with a fluent API.
|
||||
* <p>
|
||||
* This builder provides a convenient way to construct Department objects with optional parameters,
|
||||
* following the Builder pattern for improved readability and maintainability.
|
||||
*/
|
||||
public static class DepartmentBuilder {
|
||||
private Long id;
|
||||
private String name;
|
||||
private Long parentId;
|
||||
private Integer sort;
|
||||
private Status status;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private DepartmentBuilder() {
|
||||
}
|
||||
|
||||
public DepartmentBuilder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DepartmentBuilder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DepartmentBuilder parentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DepartmentBuilder sort(Integer sort) {
|
||||
this.sort = sort;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DepartmentBuilder status(Status status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DepartmentBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DepartmentBuilder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a new Department instance with the configured properties.
|
||||
*
|
||||
* @return a new Department instance
|
||||
*/
|
||||
public Department build() {
|
||||
return new Department(id, name, parentId, sort, status, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
package com.onixbyte.helix.domain.entity;
|
||||
|
||||
import com.onixbyte.helix.constant.Status;
|
||||
import com.onixbyte.helix.domain.common.Treeable;
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.JdbcType;
|
||||
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
@Entity
|
||||
@Table(name = "menus")
|
||||
public class Menu implements Treeable<Long> {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, length = 50)
|
||||
private String name;
|
||||
|
||||
@Column
|
||||
private Long parentId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String code;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Integer sort;
|
||||
|
||||
@Column
|
||||
private String path;
|
||||
|
||||
@Column
|
||||
private Boolean isExternalLink;
|
||||
|
||||
@Column
|
||||
private Boolean isVisible;
|
||||
|
||||
@Column(nullable = false)
|
||||
@Enumerated
|
||||
@JdbcType(PostgreSQLEnumJdbcType.class)
|
||||
private Status status;
|
||||
|
||||
@Column(length = 128)
|
||||
private String authorityCode;
|
||||
|
||||
@Column(length = 128)
|
||||
private String icon;
|
||||
|
||||
@Column
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Menu() {
|
||||
}
|
||||
|
||||
public Menu(Long id, String name, Long parentId, String code, Integer sort, String path, Boolean isExternalLink, Boolean isVisible, Status status, String authorityCode, String icon, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.parentId = parentId;
|
||||
this.code = code;
|
||||
this.sort = sort;
|
||||
this.path = path;
|
||||
this.isExternalLink = isExternalLink;
|
||||
this.isVisible = isVisible;
|
||||
this.status = status;
|
||||
this.authorityCode = authorityCode;
|
||||
this.icon = icon;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Boolean getExternalLink() {
|
||||
return isExternalLink;
|
||||
}
|
||||
|
||||
public void setExternalLink(Boolean externalLink) {
|
||||
isExternalLink = externalLink;
|
||||
}
|
||||
|
||||
public Boolean getVisible() {
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
public void setVisible(Boolean visible) {
|
||||
isVisible = visible;
|
||||
}
|
||||
|
||||
public Integer getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public void setSort(Integer sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public Boolean getIsExternalLink() {
|
||||
return isExternalLink;
|
||||
}
|
||||
|
||||
public void setIsExternalLink(Boolean isExternalLink) {
|
||||
this.isExternalLink = isExternalLink;
|
||||
}
|
||||
|
||||
public Boolean getIsVisible() {
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
public void setIsVisible(Boolean isVisible) {
|
||||
this.isVisible = isVisible;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getAuthorityCode() {
|
||||
return authorityCode;
|
||||
}
|
||||
|
||||
public void setAuthorityCode(String authorityCode) {
|
||||
this.authorityCode = authorityCode;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
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 (o == null || getClass() != o.getClass()) return false;
|
||||
Menu menu = (Menu) o;
|
||||
return Objects.equals(id, menu.id) && Objects.equals(name, menu.name) && Objects.equals(parentId, menu.parentId) && Objects.equals(code, menu.code) && Objects.equals(sort, menu.sort) && Objects.equals(path, menu.path) && Objects.equals(isExternalLink, menu.isExternalLink) && Objects.equals(isVisible, menu.isVisible) && status == menu.status && Objects.equals(authorityCode, menu.authorityCode) && Objects.equals(icon, menu.icon) && Objects.equals(createdAt, menu.createdAt) && Objects.equals(updatedAt, menu.updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name, parentId, code, sort, path, isExternalLink, isVisible, status, authorityCode, icon, createdAt, updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Menu{" +
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' +
|
||||
", parentId=" + parentId +
|
||||
", code=" + code +
|
||||
", sort=" + sort +
|
||||
", path=" + path +
|
||||
", isExternalLink=" + isExternalLink +
|
||||
", isVisible=" + isVisible +
|
||||
", status=" + status +
|
||||
", authorityCode='" + authorityCode + '\'' +
|
||||
", icon='" + icon + '\'' +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
package com.onixbyte.helix.domain.entity;
|
||||
|
||||
import com.onixbyte.helix.constant.Status;
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.JdbcType;
|
||||
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a position entity within the organisational structure.
|
||||
* <p>
|
||||
* Positions define job roles or titles that can be assigned to users within the organisation. They
|
||||
* provide a way to categorise users based on their responsibilities and functions, complementing
|
||||
* the department-based organisational hierarchy. Positions can be used for reporting,
|
||||
* access control, and organisational management purposes.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "positions")
|
||||
public class Position {
|
||||
|
||||
/**
|
||||
* The unique identifier for the position.
|
||||
* <p>
|
||||
* This serves as the primary key in the database and is used for all internal references to the
|
||||
* position entity.
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* The human-readable name of the position.
|
||||
* <p>
|
||||
* This field contains the job title or position name as it should appear in
|
||||
* organisational charts, user profiles, and administrative interfaces.
|
||||
*/
|
||||
@Column(nullable = false, length = 128, unique = true)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The unique code identifier for the position.
|
||||
* <p>
|
||||
* This field contains a system-level identifier that uniquely identifies the position. It is
|
||||
* typically used for integration purposes and should follow a consistent naming convention.
|
||||
*/
|
||||
@Column(nullable = false, length = 64, unique = true)
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* A detailed description of the position's responsibilities and requirements.
|
||||
* <p>
|
||||
* This field provides additional context about the role, including key responsibilities,
|
||||
* required skills, or other relevant information that helps define what this position entails.
|
||||
*/
|
||||
@Column
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* The sort order for displaying positions.
|
||||
* <p>
|
||||
* This field determines the order in which positions should be displayed in lists and
|
||||
* selection interfaces. Lower values indicate higher priority in sorting, which can reflect
|
||||
* organisational hierarchy or importance.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* The current status of the position.
|
||||
* <p>
|
||||
* This field determines whether the position is active, inactive, or in any other state as
|
||||
* defined by the {@link Status} enumeration.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
@Enumerated
|
||||
@JdbcType(PostgreSQLEnumJdbcType.class)
|
||||
private Status status;
|
||||
|
||||
/**
|
||||
* The timestamp when this position record was created.
|
||||
* <p>
|
||||
* This field is automatically set when the position entity is first persisted and provides
|
||||
* audit information about when the position was established.
|
||||
*/
|
||||
@Column
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* The timestamp when this position record was last updated.
|
||||
* <p>
|
||||
* This field is automatically updated whenever any changes are made to the position entity and
|
||||
* provides audit information about the most recent modification.
|
||||
*/
|
||||
@Column
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Integer getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public void setSort(Integer sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public Position() {
|
||||
}
|
||||
|
||||
public Position(Long id, String name, String code, String description, Integer sort, Status status, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
this.sort = sort;
|
||||
this.status = status;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Position position = (Position) o;
|
||||
return Objects.equals(id, position.id) && Objects.equals(name, position.name) && Objects.equals(code, position.code) && Objects.equals(description, position.description) && Objects.equals(sort, position.sort) && status == position.status && Objects.equals(createdAt, position.createdAt) && Objects.equals(updatedAt, position.updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name, code, description, sort, status, createdAt, updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Position{" +
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' +
|
||||
", code='" + code + '\'' +
|
||||
", description='" + description + '\'' +
|
||||
", sort=" + sort +
|
||||
", status=" + status +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Builder instance for constructing Position objects.
|
||||
*
|
||||
* @return a new PositionBuilder instance
|
||||
*/
|
||||
public static PositionBuilder builder() {
|
||||
return new PositionBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for constructing Position instances with a fluent API.
|
||||
* <p>
|
||||
* This builder provides a convenient way to construct Position objects with optional parameters,
|
||||
* following the Builder pattern for improved readability and maintainability.
|
||||
*/
|
||||
public static class PositionBuilder {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String code;
|
||||
private String description;
|
||||
private Integer sort;
|
||||
private Status status;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private PositionBuilder() {
|
||||
}
|
||||
|
||||
public PositionBuilder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionBuilder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionBuilder code(String code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionBuilder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionBuilder sort(Integer sort) {
|
||||
this.sort = sort;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionBuilder status(Status status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionBuilder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a new Position instance with the configured properties.
|
||||
*
|
||||
* @return a new Position instance
|
||||
*/
|
||||
public Position build() {
|
||||
return new Position(id, name, code, description, sort, status, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
package com.onixbyte.helix.domain.entity;
|
||||
|
||||
import com.onixbyte.helix.constant.Status;
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.JdbcType;
|
||||
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a role entity within the access control system.
|
||||
* <p>
|
||||
* Roles define sets of permissions and responsibilities that can be assigned to users. They form
|
||||
* the foundation of the role-based access control (RBAC) system, allowing for flexible and scalable
|
||||
* permission management across the Helix application.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "roles")
|
||||
public class Role {
|
||||
|
||||
/**
|
||||
* The unique identifier for the role.
|
||||
* <p>
|
||||
* This serves as the primary key in the database and is used for all internal references to the
|
||||
* role entity.
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* The human-readable name of the role.
|
||||
* <p>
|
||||
* This field contains the display name of the role as it should appear in user interfaces and
|
||||
* administrative panels.
|
||||
*/
|
||||
@Column(nullable = false, unique = true, length = 128)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The unique code identifier for the role.
|
||||
* <p>
|
||||
* This field contains a system-level identifier that is typically used in code
|
||||
* and configuration. It should be unique across all roles and follow a consistent
|
||||
* naming convention.
|
||||
*/
|
||||
@Column(nullable = false, unique = true, length = 64)
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* The sort order for displaying roles.
|
||||
* <p>
|
||||
* This field determines the order in which roles should be displayed in lists and
|
||||
* selection interfaces. Lower values indicate higher priority in sorting.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* Indicates whether this role is assigned by default to new users.
|
||||
* <p>
|
||||
* When set to true, this role will be automatically assigned to newly created user accounts.
|
||||
* This is useful for defining baseline permissions that all users should have.
|
||||
*/
|
||||
@Column
|
||||
private Boolean defaultValue;
|
||||
|
||||
/**
|
||||
* A detailed description of the role's purpose and permissions.
|
||||
* <p>
|
||||
* This field provides additional context about what the role represents and what capabilities
|
||||
* it grants to users who are assigned to it.
|
||||
*/
|
||||
@Column
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* The current status of the role.
|
||||
* <p>
|
||||
* This field determines whether the role is active, inactive, or in any other state as defined
|
||||
* by the {@link Status} enumeration.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
@Enumerated
|
||||
@JdbcType(PostgreSQLEnumJdbcType.class)
|
||||
private Status status;
|
||||
|
||||
/**
|
||||
* The timestamp when this role record was created.
|
||||
* <p>
|
||||
* This field is automatically set when the role entity is first persisted and provides audit
|
||||
* information about when the role was established.
|
||||
*/
|
||||
@Column
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* The timestamp when this role record was last updated.
|
||||
* <p>
|
||||
* This field is automatically updated whenever any changes are made to the role entity and
|
||||
* provides audit information about the most recent modification.
|
||||
*/
|
||||
@Column
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public void setSort(Integer sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
public Boolean getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void setDefaultValue(Boolean defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public Role() {
|
||||
}
|
||||
|
||||
public Role(Long id, String name, String code, Integer sort, Boolean defaultValue, String description, Status status, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.code = code;
|
||||
this.sort = sort;
|
||||
this.defaultValue = defaultValue;
|
||||
this.description = description;
|
||||
this.status = status;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Role role = (Role) o;
|
||||
return Objects.equals(id, role.id) && Objects.equals(name, role.name) && Objects.equals(code, role.code) && Objects.equals(sort, role.sort) && Objects.equals(defaultValue, role.defaultValue) && Objects.equals(description, role.description) && status == role.status && Objects.equals(createdAt, role.createdAt) && Objects.equals(updatedAt, role.updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name, code, sort, defaultValue, description, status, createdAt, updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Role{" +
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' +
|
||||
", code='" + code + '\'' +
|
||||
", sort=" + sort +
|
||||
", defaultValue=" + defaultValue +
|
||||
", description='" + description + '\'' +
|
||||
", status=" + status +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Builder instance for constructing Role objects.
|
||||
*
|
||||
* @return a new RoleBuilder instance
|
||||
*/
|
||||
public static RoleBuilder builder() {
|
||||
return new RoleBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for constructing Role instances with a fluent API.
|
||||
* <p>
|
||||
* This builder provides a convenient way to construct Role objects with optional parameters,
|
||||
* following the Builder pattern for improved readability and maintainability.
|
||||
*/
|
||||
public static class RoleBuilder {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String code;
|
||||
private Integer sort;
|
||||
private Boolean defaultValue;
|
||||
private String description;
|
||||
private Status status;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private RoleBuilder() {
|
||||
}
|
||||
|
||||
public RoleBuilder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleBuilder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleBuilder code(String code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleBuilder sort(Integer sort) {
|
||||
this.sort = sort;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleBuilder defaultValue(Boolean defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleBuilder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleBuilder status(Status status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleBuilder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a new Role instance with the configured properties.
|
||||
*
|
||||
* @return a new Role instance
|
||||
*/
|
||||
public Role build() {
|
||||
return new Role(id, name, code, sort, defaultValue, description, status, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.onixbyte.helix.domain.entity;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.embeddable.RoleAuthorityId;
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents the association entity between a Role and an Authority in a many-to-many relationship.
|
||||
* <p>
|
||||
* This entity is mapped to the 'role_authorities' table and uses a composite primary key
|
||||
* {@code @EmbeddedId} defined in the RoleAuthorityId class. It also includes the 'createdAt'
|
||||
* auditing field.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "role_authorities")
|
||||
public class RoleAuthority {
|
||||
|
||||
/**
|
||||
* The composite primary key of the association, mapped to the 'role_id' and 'authority_id' columns.
|
||||
* <p>
|
||||
* This field embeds the RoleAuthorityId object which holds the foreign keys to the Role and Authority tables.
|
||||
*/
|
||||
@EmbeddedId
|
||||
private RoleAuthorityId id;
|
||||
|
||||
/**
|
||||
* The timestamp when this role-authority association was created.
|
||||
* <p>
|
||||
* This field is automatically set upon creation in the database. {@code @Column} maps the field
|
||||
* to the 'created_at' column.
|
||||
*/
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
public Long getRoleId() {
|
||||
return this.id != null ? this.id.getRoleId() : null;
|
||||
}
|
||||
|
||||
public Long getAuthorityId() {
|
||||
return this.id != null ? this.id.getAuthorityId() : null;
|
||||
}
|
||||
|
||||
public RoleAuthorityId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(RoleAuthorityId id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public RoleAuthority() {
|
||||
// Initialise the ID for safety when created via JPA
|
||||
this.id = new RoleAuthorityId();
|
||||
}
|
||||
|
||||
public RoleAuthority(Long roleId, Long authorityId, LocalDateTime createdAt) {
|
||||
this.id = new RoleAuthorityId(roleId, authorityId);
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RoleAuthority that = (RoleAuthority) o;
|
||||
// Only check the primary key for entity equality
|
||||
return Objects.equals(id, that.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// Hash code based on the primary key
|
||||
return Objects.hash(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RoleAuthority{" +
|
||||
"roleId=" + (id != null ? id.getRoleId() : "null") +
|
||||
", authorityId=" + (id != null ? id.getAuthorityId() : "null") +
|
||||
", createdAt=" + createdAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static RoleAuthorityBuilder builder() {
|
||||
return new RoleAuthorityBuilder();
|
||||
}
|
||||
|
||||
public static class RoleAuthorityBuilder {
|
||||
private Long roleId;
|
||||
private Long authorityId;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
private RoleAuthorityBuilder() {
|
||||
}
|
||||
|
||||
public RoleAuthorityBuilder roleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleAuthorityBuilder authorityId(Long authorityId) {
|
||||
this.authorityId = authorityId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleAuthorityBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleAuthority build() {
|
||||
return new RoleAuthority(roleId, authorityId, createdAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
package com.onixbyte.helix.domain.entity;
|
||||
|
||||
import com.onixbyte.helix.constant.SettingType;
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.JdbcType;
|
||||
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a hot-deployable application setting, stored in the 'settings' database table.
|
||||
* <p>
|
||||
* This entity allows for dynamic configuration changes without application restarts.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "settings")
|
||||
public class Setting {
|
||||
|
||||
/**
|
||||
* Setting unique identifier, mapped to the primary key 'id'.
|
||||
* <p>
|
||||
* Uses BIGSERIAL (Long) and is set to auto-increment by the database.
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* Setting name. Used as a unique key for retrieval.
|
||||
*/
|
||||
@Column(nullable = false, unique = true)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Setting description.
|
||||
*/
|
||||
@Column
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* The type of the value (e.g., BOOLEAN, INT, STRING).
|
||||
* <p>
|
||||
* Mapped to the custom SQL type SETTING_TYPE, typically handled by JPA as an Enum.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
@Enumerated
|
||||
@JdbcType(PostgreSQLEnumJdbcType.class)
|
||||
private SettingType type;
|
||||
|
||||
/**
|
||||
* Setting current value. Stored as a string regardless of the actual type.
|
||||
*/
|
||||
@Column
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* Setting default value.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private String defaultValue;
|
||||
|
||||
/**
|
||||
* The timestamp when this setting was created.
|
||||
* <p>
|
||||
* Set only on creation and remains unchanged.
|
||||
*/
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* The timestamp when this setting was last updated.
|
||||
* <p>
|
||||
* Updated on every change to the entity.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
protected void onUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public Setting() {
|
||||
}
|
||||
|
||||
public Setting(Long id, String name, String description, SettingType type, String value, String defaultValue, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
this.defaultValue = defaultValue;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public SettingType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(SettingType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void setDefaultValue(String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public String fetchValueOrDefault() {
|
||||
if (Objects.nonNull(value) && !value.isBlank()) {
|
||||
return value;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public Boolean asBoolean() {
|
||||
var val = fetchValueOrDefault();
|
||||
if (type == SettingType.BOOLEAN) {
|
||||
return Boolean.parseBoolean(val);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Integer asInt() {
|
||||
try {
|
||||
var val = fetchValueOrDefault();
|
||||
if (type == SettingType.INT) {
|
||||
return Integer.parseInt(val);
|
||||
}
|
||||
return null;
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Long asLong() {
|
||||
try {
|
||||
var val = fetchValueOrDefault();
|
||||
if (type == SettingType.INT) {
|
||||
return Long.parseLong(val);
|
||||
}
|
||||
return null;
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static SettingBuilder builder() {
|
||||
return new SettingBuilder();
|
||||
}
|
||||
|
||||
public static class SettingBuilder {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String description;
|
||||
private SettingType type;
|
||||
private String value;
|
||||
private String defaultValue;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private SettingBuilder() {
|
||||
}
|
||||
|
||||
public SettingBuilder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SettingBuilder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SettingBuilder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SettingBuilder type(SettingType type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SettingBuilder value(String value) {
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SettingBuilder defaultValue(String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SettingBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SettingBuilder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Setting build() {
|
||||
return new Setting(id, name, description, type, value, defaultValue, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
package com.onixbyte.helix.domain.entity;
|
||||
|
||||
import com.onixbyte.helix.constant.UserStatus;
|
||||
import jakarta.persistence.*; // 导入 Jakarta Persistence API
|
||||
import org.hibernate.annotations.JdbcType;
|
||||
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a user entity in the Helix system.
|
||||
* <p>
|
||||
* This entity encapsulates all user-related information including authentication credentials,
|
||||
* personal details, contact information, and organisational associations. Users are the core
|
||||
* entities that interact with the system and are associated with departments and positions within
|
||||
* the organisational hierarchy.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(
|
||||
name = "users",
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(name = "uidx_users_username", columnNames = {"username"}),
|
||||
@UniqueConstraint(name = "uidx_users_email", columnNames = {"email"}),
|
||||
@UniqueConstraint(name = "uidx_users_region_abbreviation_phone_number", columnNames = {"region_abbreviation", "phone_number"})
|
||||
},
|
||||
indexes = {@Index(name = "users_username_index", columnList = "username")}
|
||||
)
|
||||
public class User {
|
||||
|
||||
/**
|
||||
* The unique identifier for the user.
|
||||
* <p>
|
||||
* This serves as the primary key in the database and is used for all internal references to the
|
||||
* user entity. Since the SQL uses `BIGINT PRIMARY KEY` without `SERIAL`, we assume the ID is assigned manually or by an external service.
|
||||
*/
|
||||
@Id
|
||||
@Column(nullable = false)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* The unique username for authentication purposes.
|
||||
*/
|
||||
@Column(nullable = false, length = 64)
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* The encrypted password for user authentication.
|
||||
*/
|
||||
@Column
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* The user's complete full name.
|
||||
*/
|
||||
@Column(nullable = false, length = 128)
|
||||
private String fullName;
|
||||
|
||||
/**
|
||||
* The user's email address.
|
||||
*/
|
||||
@Column(length = 128)
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* The region abbreviation for the user's phone number.
|
||||
*/
|
||||
@Column(length = 10)
|
||||
private String regionAbbreviation;
|
||||
|
||||
/**
|
||||
* The user's phone number without the country code.
|
||||
*/
|
||||
@Column(length = 32)
|
||||
private String phoneNumber;
|
||||
|
||||
/**
|
||||
* The URL to the user's avatar image.
|
||||
*/
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String avatarUrl;
|
||||
|
||||
/**
|
||||
* The current status of the user account.
|
||||
* <p>
|
||||
* Mapped to the custom SQL type USER_STATUS.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
@Enumerated
|
||||
@JdbcType(PostgreSQLEnumJdbcType.class)
|
||||
private UserStatus status;
|
||||
|
||||
/**
|
||||
* The identifier of the department to which this user belongs.
|
||||
* <p>
|
||||
* {@code @ManyToOne} is the standard way to map a foreign key (department_id) in JPA.
|
||||
* You might replace this with a direct {@code @ManyToOne} mapping to the Department entity later.
|
||||
*/
|
||||
@Column
|
||||
private Long departmentId;
|
||||
|
||||
/**
|
||||
* The identifier of the position held by this user.
|
||||
*/
|
||||
@Column
|
||||
private Long positionId;
|
||||
|
||||
/**
|
||||
* The timestamp when this user record was created.
|
||||
*/
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* The timestamp when this user record was last updated.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
// --- JPA Lifecycle Callbacks for Auditing ---
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
if (this.createdAt == null) {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
}
|
||||
if (this.updatedAt == null) {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
// Ensure status default is applied if not set
|
||||
if (this.status == null) {
|
||||
this.status = UserStatus.ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
protected void onUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
// --- Constructors, Getters, Setters, and Builder (omitted for brevity) ---
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(Long id, String username, String password, String fullName, String email, String regionAbbreviation, String phoneNumber, String avatarUrl, UserStatus status, Long departmentId, Long positionId, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.fullName = fullName;
|
||||
this.email = email;
|
||||
this.regionAbbreviation = regionAbbreviation;
|
||||
this.phoneNumber = phoneNumber;
|
||||
this.avatarUrl = avatarUrl;
|
||||
this.status = status;
|
||||
this.departmentId = departmentId;
|
||||
this.positionId = positionId;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique identifier for this user.
|
||||
*
|
||||
* @return the user's unique identifier
|
||||
*/
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the unique identifier for this user.
|
||||
*
|
||||
* @param id the user's unique identifier
|
||||
*/
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the username for authentication.
|
||||
*
|
||||
* @return the username
|
||||
*/
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the username for authentication.
|
||||
*
|
||||
* @param username the username, must be unique across the system
|
||||
*/
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the encrypted password.
|
||||
*
|
||||
* @return the encrypted password
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the encrypted password.
|
||||
*
|
||||
* @param password the encrypted password (never plain text)
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public void setFullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user's email address.
|
||||
*
|
||||
* @return the email address
|
||||
*/
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user's email address.
|
||||
*
|
||||
* @param email the email address, must be unique across the system
|
||||
*/
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getRegionAbbreviation() {
|
||||
return regionAbbreviation;
|
||||
}
|
||||
|
||||
public void setRegionAbbreviation(String countryCode) {
|
||||
this.regionAbbreviation = countryCode;
|
||||
}
|
||||
|
||||
public String getPhoneNumber() {
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
public void setPhoneNumber(String phoneNumber) {
|
||||
this.phoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
public String getAvatarUrl() {
|
||||
return avatarUrl;
|
||||
}
|
||||
|
||||
public void setAvatarUrl(String avatarUrl) {
|
||||
this.avatarUrl = avatarUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current status of the user account.
|
||||
*
|
||||
* @return the user status
|
||||
*/
|
||||
public UserStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current status of the user account.
|
||||
*
|
||||
* @param status the user status
|
||||
*/
|
||||
public void setStatus(UserStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Long getDepartmentId() {
|
||||
return departmentId;
|
||||
}
|
||||
|
||||
public void setDepartmentId(Long departmentId) {
|
||||
this.departmentId = departmentId;
|
||||
}
|
||||
|
||||
public Long getPositionId() {
|
||||
return positionId;
|
||||
}
|
||||
|
||||
public void setPositionId(Long positionId) {
|
||||
this.positionId = positionId;
|
||||
}
|
||||
|
||||
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 (o == null || getClass() != o.getClass()) return false;
|
||||
User user = (User) o;
|
||||
return Objects.equals(id, user.id); // Typically only check the Primary Key for entity equality
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id); // Hash code based on the Primary Key
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User{" +
|
||||
"id=" + id +
|
||||
", username='" + username + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", status=" + status +
|
||||
", departmentId=" + departmentId +
|
||||
", positionId=" + positionId +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Builder instance for constructing User objects.
|
||||
*
|
||||
* @return a new UserBuilder instance
|
||||
*/
|
||||
public static UserBuilder builder() {
|
||||
return new UserBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for constructing User instances with a fluent API.
|
||||
* <p>
|
||||
* This builder provides a convenient way to construct User objects with optional parameters,
|
||||
* following the Builder pattern for improved readability and maintainability.
|
||||
*/
|
||||
public static class UserBuilder {
|
||||
private Long id;
|
||||
private String username;
|
||||
private String password;
|
||||
private String fullName;
|
||||
private String email;
|
||||
private String regionAbbreviation;
|
||||
private String phoneNumber;
|
||||
private String avatarUrl;
|
||||
private UserStatus status;
|
||||
private Long departmentId;
|
||||
private Long positionId;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private UserBuilder() {
|
||||
}
|
||||
|
||||
public UserBuilder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder username(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder password(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder fullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder email(String email) {
|
||||
this.email = email;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder regionAbbreviation(String regionAbbreviation) {
|
||||
this.regionAbbreviation = regionAbbreviation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder phoneNumber(String phoneNumber) {
|
||||
this.phoneNumber = phoneNumber;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder avatarUrl(String avatarUrl) {
|
||||
this.avatarUrl = avatarUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder status(UserStatus status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder departmentId(Long departmentId) {
|
||||
this.departmentId = departmentId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder positionId(Long positionId) {
|
||||
this.positionId = positionId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a new User instance with the configured properties.
|
||||
*
|
||||
* @return a new User instance
|
||||
*/
|
||||
public User build() {
|
||||
return new User(id, username, password, fullName, email, regionAbbreviation, phoneNumber, avatarUrl, status, departmentId, positionId, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
package com.onixbyte.helix.domain.entity;
|
||||
|
||||
import com.onixbyte.helix.constant.IdentityProvider;
|
||||
import com.onixbyte.helix.domain.entity.embeddable.UserIdentityId;
|
||||
import jakarta.persistence.*; // 导入 Jakarta Persistence API
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents an external identity mapping for a user.
|
||||
* <p>
|
||||
* This entity manages the relationship between internal user accounts and external identity
|
||||
* providers (such as OAuth providers, LDAP systems, or other authentication services). It enables
|
||||
* users to authenticate using external credentials while maintaining a consistent internal user
|
||||
* identity within the Helix system.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "user_identities")
|
||||
public class UserIdentity {
|
||||
|
||||
/**
|
||||
* The composite primary key for the entity, composed of userId, provider, and externalId.
|
||||
*/
|
||||
@EmbeddedId
|
||||
private UserIdentityId id;
|
||||
|
||||
/**
|
||||
* The timestamp when this identity mapping was created.
|
||||
*/
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* The timestamp when this identity mapping was last updated.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
// --- JPA Lifecycle Callbacks for Auditing ---
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
if (this.createdAt == null) {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
}
|
||||
if (this.updatedAt == null) {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
protected void onUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
|
||||
// --- Getters and Setters (Delegating to EmbeddedId) ---
|
||||
|
||||
/**
|
||||
* Gets the identifier of the internal user account from the composite primary key.
|
||||
* @return the user ID
|
||||
*/
|
||||
public Long getUserId() {
|
||||
return this.id != null ? this.id.getUserId() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier of the internal user account within the composite primary key.
|
||||
* @param userId the user ID
|
||||
*/
|
||||
public void setUserId(Long userId) {
|
||||
if (this.id == null) this.id = new UserIdentityId();
|
||||
this.id.setUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the external identity provider from the composite primary key.
|
||||
* @return the provider
|
||||
*/
|
||||
public IdentityProvider getProvider() {
|
||||
return this.id != null ? this.id.getProvider() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the external identity provider within the composite primary key.
|
||||
* @param provider the provider
|
||||
*/
|
||||
public void setProvider(IdentityProvider provider) {
|
||||
if (this.id == null) this.id = new UserIdentityId();
|
||||
this.id.setProvider(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique identifier from the external provider from the composite primary key.
|
||||
* @return the external ID
|
||||
*/
|
||||
public String getExternalId() {
|
||||
return this.id != null ? this.id.getExternalId() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the unique identifier from the external provider within the composite primary key.
|
||||
* @param externalId the external ID
|
||||
*/
|
||||
public void setExternalId(String externalId) {
|
||||
if (this.id == null) this.id = new UserIdentityId();
|
||||
this.id.setExternalId(externalId);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// --- Constructors (Adjusted for EmbeddedId) ---
|
||||
|
||||
public UserIdentity() {
|
||||
this.id = new UserIdentityId(); // Initialize ID object for safety
|
||||
}
|
||||
|
||||
public UserIdentity(Long userId, IdentityProvider provider, String externalId, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = new UserIdentityId(userId, provider, externalId);
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
// --- Overrides (Simplified to use the Id object for entity equality) ---
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
UserIdentity that = (UserIdentity) o;
|
||||
return Objects.equals(id, that.id); // Entity equality based on primary key
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id); // Hash code based on primary key
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UserIdentity{" +
|
||||
"userId=" + getUserId() +
|
||||
", provider=" + getProvider() +
|
||||
", externalId='" + getExternalId() + '\'' +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Builder instance for constructing UserIdentity objects.
|
||||
*
|
||||
* @return a new UserIdentityBuilder instance
|
||||
*/
|
||||
public static UserIdentityBuilder builder() {
|
||||
return new UserIdentityBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for constructing UserIdentity instances with a fluent API.
|
||||
* <p>
|
||||
* This builder provides a convenient way to construct UserIdentity objects with optional parameters,
|
||||
* following the Builder pattern for improved readability and maintainability.
|
||||
*/
|
||||
public static class UserIdentityBuilder {
|
||||
private Long userId;
|
||||
private IdentityProvider provider;
|
||||
private String externalId;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private UserIdentityBuilder() {
|
||||
}
|
||||
|
||||
public UserIdentityBuilder userId(Long userId) {
|
||||
this.userId = userId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserIdentityBuilder provider(IdentityProvider provider) {
|
||||
this.provider = provider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserIdentityBuilder externalId(String externalId) {
|
||||
this.externalId = externalId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserIdentityBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserIdentityBuilder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a new UserIdentity instance with the configured properties.
|
||||
*
|
||||
* @return a new UserIdentity instance
|
||||
*/
|
||||
public UserIdentity build() {
|
||||
return new UserIdentity(userId, provider, externalId, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package com.onixbyte.helix.domain.entity;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.embeddable.UserRoleId;
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents the association between users and roles in the access control system.
|
||||
* <p>
|
||||
* This entity implements the many-to-many relationship between users and roles, allowing users to
|
||||
* be assigned multiple roles and roles to be assigned to multiple users. It forms a fundamental
|
||||
* part of the role-based access control (RBAC) system by defining which roles are assigned to
|
||||
* each user, thereby determining their permissions and access levels within the system.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "user_roles")
|
||||
public class UserRole {
|
||||
|
||||
/**
|
||||
* The composite primary key of the association, mapped to the 'user_id' and 'role_id' columns.
|
||||
* <p>
|
||||
* This field embeds the UserRoleId object which holds the foreign keys to the User and Role tables.
|
||||
*/
|
||||
@EmbeddedId
|
||||
private UserRoleId id;
|
||||
|
||||
/**
|
||||
* The timestamp when this user-role assignment was created.
|
||||
* <p>
|
||||
* This field is automatically set upon creation in the database.
|
||||
*/
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
// --- JPA Lifecycle Callbacks for Auditing ---
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
if (this.createdAt == null) {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
|
||||
// --- Getters and Setters (Delegating to EmbeddedId and fields) ---
|
||||
|
||||
/**
|
||||
* Gets the identifier of the role from the composite primary key.
|
||||
*
|
||||
* @return the role's unique identifier
|
||||
*/
|
||||
public Long getRoleId() {
|
||||
return this.id != null ? this.id.getRoleId() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier of the role within the composite primary key.
|
||||
*
|
||||
* @param roleId the role's unique identifier
|
||||
*/
|
||||
public void setRoleId(Long roleId) {
|
||||
if (this.id == null) this.id = new UserRoleId();
|
||||
this.id.setRoleId(roleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the identifier of the user from the composite primary key.
|
||||
*
|
||||
* @return the user's unique identifier
|
||||
*/
|
||||
public Long getUserId() {
|
||||
return this.id != null ? this.id.getUserId() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier of the user within the composite primary key.
|
||||
*
|
||||
* @param userId the user's unique identifier
|
||||
*/
|
||||
public void setUserId(Long userId) {
|
||||
if (this.id == null) this.id = new UserRoleId();
|
||||
this.id.setUserId(userId);
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public UserRole() {
|
||||
this.id = new UserRoleId();
|
||||
}
|
||||
|
||||
public UserRole(Long roleId, Long userId, LocalDateTime createdAt) {
|
||||
this.id = new UserRoleId(userId, roleId);
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
// --- Overrides (Simplified to use the Id object for entity equality) ---
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
UserRole userRole = (UserRole) o;
|
||||
return Objects.equals(id, userRole.id); // Entity equality based on primary key
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id); // Hash code based on primary key
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UserRole{" +
|
||||
"roleId=" + getRoleId() +
|
||||
", userId=" + getUserId() +
|
||||
", createdAt=" + createdAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
// --- Builder Class (Adjusted to build the Id object) ---
|
||||
|
||||
/**
|
||||
* Creates a new Builder instance for constructing UserRole objects.
|
||||
*
|
||||
* @return a new UserRoleBuilder instance
|
||||
*/
|
||||
public static UserRoleBuilder builder() {
|
||||
return new UserRoleBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for constructing UserRole instances with a fluent API.
|
||||
*/
|
||||
public static class UserRoleBuilder {
|
||||
private Long roleId;
|
||||
private Long userId;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
private UserRoleBuilder() {
|
||||
}
|
||||
|
||||
public UserRoleBuilder roleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserRoleBuilder userId(Long userId) {
|
||||
this.userId = userId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserRoleBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a new UserRole instance with the configured properties.
|
||||
*
|
||||
* @return a new UserRole instance
|
||||
*/
|
||||
public UserRole build() {
|
||||
return new UserRole(roleId, userId, createdAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.onixbyte.helix.domain.entity.embeddable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents the composite primary key for the RoleAuthority association entity.
|
||||
*/
|
||||
@Embeddable
|
||||
public class RoleAuthorityId implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The identifier of the role, mapping to the 'role_id' column.
|
||||
* <p>
|
||||
* This field should match the corresponding field name in the RoleAuthority entity
|
||||
* if the naming is non-standard, but typically matches the column name in the database.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private Long roleId;
|
||||
|
||||
/**
|
||||
* The identifier of the authority, mapping to the 'authority_id' column.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private Long authorityId;
|
||||
|
||||
public RoleAuthorityId() {
|
||||
}
|
||||
|
||||
public RoleAuthorityId(Long roleId, Long authorityId) {
|
||||
this.roleId = roleId;
|
||||
this.authorityId = authorityId;
|
||||
}
|
||||
|
||||
public Long getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
public Long getAuthorityId() {
|
||||
return authorityId;
|
||||
}
|
||||
|
||||
public void setAuthorityId(Long authorityId) {
|
||||
this.authorityId = authorityId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RoleAuthorityId that = (RoleAuthorityId) o;
|
||||
return Objects.equals(roleId, that.roleId) && Objects.equals(authorityId, that.authorityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(roleId, authorityId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.onixbyte.helix.domain.entity.embeddable;
|
||||
|
||||
import com.onixbyte.helix.constant.IdentityProvider;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import org.hibernate.annotations.JdbcType;
|
||||
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents the composite primary key for the UserIdentity entity.
|
||||
* <p>
|
||||
* This key is composed of the internal user ID, the identity provider, and the external ID
|
||||
* from that provider.
|
||||
*/
|
||||
@Embeddable
|
||||
public class UserIdentityId implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The identifier of the internal user account, corresponding to the 'user_id' column.
|
||||
* <p>
|
||||
* This also serves as a foreign key reference to the User entity.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* The external identity provider, corresponding to the 'provider' column.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
@Enumerated
|
||||
@JdbcType(PostgreSQLEnumJdbcType.class)
|
||||
private IdentityProvider provider;
|
||||
|
||||
/**
|
||||
* The unique identifier from the external provider, corresponding to the 'external_id' column.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private String externalId;
|
||||
|
||||
// --- Constructors ---
|
||||
|
||||
public UserIdentityId() {
|
||||
}
|
||||
|
||||
public UserIdentityId(Long userId, IdentityProvider provider, String externalId) {
|
||||
this.userId = userId;
|
||||
this.provider = provider;
|
||||
this.externalId = externalId;
|
||||
}
|
||||
|
||||
// --- Getters and Setters (Omitted for brevity, but should exist) ---
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public IdentityProvider getProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public void setProvider(IdentityProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public String getExternalId() {
|
||||
return externalId;
|
||||
}
|
||||
|
||||
public void setExternalId(String externalId) {
|
||||
this.externalId = externalId;
|
||||
}
|
||||
|
||||
// --- equals and hashCode (REQUIRED for composite keys) ---
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
UserIdentityId that = (UserIdentityId) o;
|
||||
return Objects.equals(userId, that.userId) && provider == that.provider && Objects.equals(externalId, that.externalId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(userId, provider, externalId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.onixbyte.helix.domain.entity.embeddable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents the composite primary key for the UserRole association entity.
|
||||
* <p>
|
||||
* This class combines the userId and roleId to uniquely identify a user's role assignment.
|
||||
*/
|
||||
@Embeddable
|
||||
public class UserRoleId implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The identifier of the user in this association, mapping to the 'user_id' column.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* The identifier of the role in this association, mapping to the 'role_id' column.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private Long roleId;
|
||||
|
||||
// --- Constructors ---
|
||||
public UserRoleId() {
|
||||
}
|
||||
|
||||
public UserRoleId(Long userId, Long roleId) {
|
||||
this.userId = userId;
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
// --- Getters and Setters ---
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Long getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
// --- equals and hashCode (REQUIRED for composite keys) ---
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
UserRoleId that = (UserRoleId) o;
|
||||
return Objects.equals(userId, that.userId) && Objects.equals(roleId, that.roleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(userId, roleId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.onixbyte.helix.domain.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public record TreeNode<T>(
|
||||
T item,
|
||||
List<TreeNode<T>> children
|
||||
) {
|
||||
|
||||
/**
|
||||
* Helper constructor for building.
|
||||
* @param item the item
|
||||
*/
|
||||
public TreeNode(T item) {
|
||||
this(item, new ArrayList<>());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
package com.onixbyte.helix.domain.view;
|
||||
|
||||
import com.onixbyte.helix.constant.Status;
|
||||
import com.onixbyte.helix.domain.entity.Authority;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* View object for Authority entity.
|
||||
* <p>
|
||||
* This class represents the view layer abstraction of an Authority, providing a simplified
|
||||
* representation suitable for presentation in user interfaces and API responses.
|
||||
*
|
||||
* @author Zihlu Wang
|
||||
* @version 1.0.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class AuthorityView {
|
||||
|
||||
/**
|
||||
* The unique identifier for the authority.
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* The unique code identifier for the authority.
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* The human-readable name of the authority.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* A detailed description of what this authority grants.
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* The current status of the authority.
|
||||
*/
|
||||
private Status status;
|
||||
|
||||
/**
|
||||
* The timestamp when this authority record was created.
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* The timestamp when this authority record was last updated.
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* Default constructor for serialisation frameworks.
|
||||
*/
|
||||
public AuthorityView() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new AuthorityView with all fields specified.
|
||||
*
|
||||
* @param id the unique identifier
|
||||
* @param code the unique code identifier
|
||||
* @param name the human-readable name
|
||||
* @param description the detailed description
|
||||
* @param status the current status
|
||||
* @param createdAt the creation timestamp
|
||||
* @param updatedAt the last update timestamp
|
||||
*/
|
||||
public AuthorityView(Long id, String code, String name, String description, Status status,
|
||||
LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.status = status;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AuthorityView from an Authority entity.
|
||||
*
|
||||
* @param entity the Authority entity
|
||||
* @return a new AuthorityView instance
|
||||
*/
|
||||
public static AuthorityView fromEntity(Authority entity) {
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
return new AuthorityView(
|
||||
entity.getId(),
|
||||
entity.getCode(),
|
||||
entity.getName(),
|
||||
entity.getDescription(),
|
||||
entity.getStatus(),
|
||||
entity.getCreatedAt(),
|
||||
entity.getUpdatedAt()
|
||||
);
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
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;
|
||||
AuthorityView that = (AuthorityView) o;
|
||||
return Objects.equals(id, that.id) &&
|
||||
Objects.equals(code, that.code) &&
|
||||
Objects.equals(name, that.name) &&
|
||||
Objects.equals(description, that.description) &&
|
||||
status == that.status &&
|
||||
Objects.equals(createdAt, that.createdAt) &&
|
||||
Objects.equals(updatedAt, that.updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, code, name, description, status, createdAt, updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AuthorityView{" +
|
||||
"id=" + id +
|
||||
", code='" + code + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", description='" + description + '\'' +
|
||||
", status=" + status +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a builder for constructing AuthorityView instances.
|
||||
*
|
||||
* @return a new AuthorityViewBuilder instance
|
||||
*/
|
||||
public static AuthorityViewBuilder builder() {
|
||||
return new AuthorityViewBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for constructing {@link AuthorityView} instances.
|
||||
* <p>
|
||||
* This builder provides a fluent interface for creating AuthorityView objects with optional
|
||||
* parameters, following the builder pattern for improved readability and maintainability.
|
||||
*/
|
||||
public static class AuthorityViewBuilder {
|
||||
private Long id;
|
||||
private String code;
|
||||
private String name;
|
||||
private String description;
|
||||
private Status status;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private AuthorityViewBuilder() {
|
||||
}
|
||||
|
||||
public AuthorityViewBuilder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorityViewBuilder code(String code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorityViewBuilder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorityViewBuilder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorityViewBuilder status(Status status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorityViewBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorityViewBuilder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a new AuthorityView instance with the configured parameters.
|
||||
*
|
||||
* @return a new AuthorityView instance
|
||||
*/
|
||||
public AuthorityView build() {
|
||||
return new AuthorityView(id, code, name, description, status, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
package com.onixbyte.helix.domain.view;
|
||||
|
||||
import com.onixbyte.helix.constant.Status;
|
||||
import com.onixbyte.helix.domain.entity.Department;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* View object for Department entity.
|
||||
* <p>
|
||||
* This class represents the view layer abstraction of a Department, providing a simplified
|
||||
* representation suitable for presentation in user interfaces and API responses.
|
||||
*
|
||||
* @author Zihlu Wang
|
||||
* @version 1.0.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class DepartmentView {
|
||||
|
||||
/**
|
||||
* The unique identifier for the department.
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* The name of the department.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The identifier of the parent department.
|
||||
*/
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* The sort order for displaying departments.
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* The current status of the department.
|
||||
*/
|
||||
private Status status;
|
||||
|
||||
/**
|
||||
* The timestamp when this department record was created.
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* The timestamp when this department record was last updated.
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* Default constructor for serialisation frameworks.
|
||||
*/
|
||||
public DepartmentView() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new DepartmentView with all fields specified.
|
||||
*
|
||||
* @param id the unique identifier
|
||||
* @param name the name of the department
|
||||
* @param parentId the identifier of the parent department
|
||||
* @param sort the sort order for displaying departments
|
||||
* @param status the current status
|
||||
* @param createdAt the creation timestamp
|
||||
* @param updatedAt the last update timestamp
|
||||
*/
|
||||
public DepartmentView(Long id, String name, Long parentId, Integer sort,
|
||||
Status status, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.parentId = parentId;
|
||||
this.sort = sort;
|
||||
this.status = status;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DepartmentView from a Department entity.
|
||||
*
|
||||
* @param entity the Department entity
|
||||
* @return a new DepartmentView instance
|
||||
*/
|
||||
public static DepartmentView fromEntity(Department entity) {
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
return new DepartmentView(
|
||||
entity.getId(),
|
||||
entity.getName(),
|
||||
entity.getParentId(),
|
||||
entity.getSort(),
|
||||
entity.getStatus(),
|
||||
entity.getCreatedAt(),
|
||||
entity.getUpdatedAt()
|
||||
);
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public Integer getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public void setSort(Integer sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
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;
|
||||
DepartmentView that = (DepartmentView) o;
|
||||
return Objects.equals(id, that.id) &&
|
||||
Objects.equals(name, that.name) &&
|
||||
Objects.equals(parentId, that.parentId) &&
|
||||
Objects.equals(sort, that.sort) &&
|
||||
status == that.status &&
|
||||
Objects.equals(createdAt, that.createdAt) &&
|
||||
Objects.equals(updatedAt, that.updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name, parentId, sort, status, createdAt, updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DepartmentView{" +
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' +
|
||||
", parentId=" + parentId +
|
||||
", sort=" + sort +
|
||||
", status=" + status +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a builder for constructing DepartmentView instances.
|
||||
*
|
||||
* @return a new DepartmentViewBuilder instance
|
||||
*/
|
||||
public static DepartmentViewBuilder builder() {
|
||||
return new DepartmentViewBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for constructing {@link DepartmentView} instances.
|
||||
* <p>
|
||||
* This builder provides a fluent interface for creating DepartmentView objects with optional
|
||||
* parameters, following the builder pattern for improved readability and maintainability.
|
||||
*/
|
||||
public static class DepartmentViewBuilder {
|
||||
private Long id;
|
||||
private String name;
|
||||
private Long parentId;
|
||||
private String treePath;
|
||||
private Integer sort;
|
||||
private Status status;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private DepartmentViewBuilder() {
|
||||
}
|
||||
|
||||
public DepartmentViewBuilder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DepartmentViewBuilder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DepartmentViewBuilder parentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DepartmentViewBuilder treePath(String treePath) {
|
||||
this.treePath = treePath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DepartmentViewBuilder sort(Integer sort) {
|
||||
this.sort = sort;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DepartmentViewBuilder status(Status status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DepartmentViewBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DepartmentViewBuilder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a new DepartmentView instance with the configured parameters.
|
||||
*
|
||||
* @return a new DepartmentView instance
|
||||
*/
|
||||
public DepartmentView build() {
|
||||
return new DepartmentView(id, name, parentId, sort, status, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
package com.onixbyte.helix.domain.view;
|
||||
|
||||
import com.onixbyte.helix.constant.Status;
|
||||
import com.onixbyte.helix.domain.entity.Position;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* View object for Position entity.
|
||||
* <p>
|
||||
* This class represents a view of the Position entity, providing a data transfer object
|
||||
* for position information in the organisational structure.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
public class PositionView {
|
||||
|
||||
/**
|
||||
* The unique identifier for the position.
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* The human-readable name of the position.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The unique code identifier for the position.
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* A detailed description of the position's responsibilities and requirements.
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* The sort order for displaying positions.
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* The current status of the position.
|
||||
*/
|
||||
private Status status;
|
||||
|
||||
/**
|
||||
* The timestamp when this position record was created.
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* The timestamp when this position record was last updated.
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public PositionView() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with all fields.
|
||||
*/
|
||||
public PositionView(Long id, String name, String code, String description, Integer sort,
|
||||
Status status, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
this.sort = sort;
|
||||
this.status = status;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PositionView from a Position entity.
|
||||
*
|
||||
* @param position the Position entity
|
||||
* @return the PositionView object
|
||||
*/
|
||||
public static PositionView fromEntity(Position position) {
|
||||
if (position == null) {
|
||||
return null;
|
||||
}
|
||||
return new PositionView(
|
||||
position.getId(),
|
||||
position.getName(),
|
||||
position.getCode(),
|
||||
position.getDescription(),
|
||||
position.getSort(),
|
||||
position.getStatus(),
|
||||
position.getCreatedAt(),
|
||||
position.getUpdatedAt()
|
||||
);
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Integer getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public void setSort(Integer sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
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;
|
||||
PositionView that = (PositionView) o;
|
||||
return Objects.equals(id, that.id) &&
|
||||
Objects.equals(name, that.name) &&
|
||||
Objects.equals(code, that.code) &&
|
||||
Objects.equals(description, that.description) &&
|
||||
Objects.equals(sort, that.sort) &&
|
||||
status == that.status &&
|
||||
Objects.equals(createdAt, that.createdAt) &&
|
||||
Objects.equals(updatedAt, that.updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name, code, description, sort, status, createdAt, updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PositionView{" +
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' +
|
||||
", code='" + code + '\'' +
|
||||
", description='" + description + '\'' +
|
||||
", sort=" + sort +
|
||||
", status=" + status +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new builder instance.
|
||||
*
|
||||
* @return a new PositionViewBuilder
|
||||
*/
|
||||
public static PositionViewBuilder builder() {
|
||||
return new PositionViewBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for PositionView.
|
||||
*/
|
||||
public static class PositionViewBuilder {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String code;
|
||||
private String description;
|
||||
private Integer sort;
|
||||
private Status status;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private PositionViewBuilder() {
|
||||
}
|
||||
|
||||
public PositionViewBuilder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionViewBuilder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionViewBuilder code(String code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionViewBuilder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionViewBuilder sort(Integer sort) {
|
||||
this.sort = sort;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionViewBuilder status(Status status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionViewBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionViewBuilder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionView build() {
|
||||
return new PositionView(id, name, code, description, sort, status, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package com.onixbyte.helix.domain.view;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.RoleAuthority;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* View object for RoleAuthority entity.
|
||||
* <p>
|
||||
* This class represents a view of the RoleAuthority entity, providing a data transfer object
|
||||
* for role-authority association information in the access control system.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
public class RoleAuthorityView {
|
||||
|
||||
/**
|
||||
* The identifier of the role in this association.
|
||||
*/
|
||||
private Long roleId;
|
||||
|
||||
/**
|
||||
* The identifier of the authority in this association.
|
||||
*/
|
||||
private Long authorityId;
|
||||
|
||||
/**
|
||||
* The timestamp when this role-authority association was created.
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public RoleAuthorityView() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with all fields.
|
||||
*/
|
||||
public RoleAuthorityView(Long roleId, Long authorityId, LocalDateTime createdAt) {
|
||||
this.roleId = roleId;
|
||||
this.authorityId = authorityId;
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a RoleAuthorityView from a RoleAuthority entity.
|
||||
*
|
||||
* @param roleAuthority the RoleAuthority entity
|
||||
* @return the RoleAuthorityView object
|
||||
*/
|
||||
public static RoleAuthorityView fromEntity(RoleAuthority roleAuthority) {
|
||||
if (roleAuthority == null) {
|
||||
return null;
|
||||
}
|
||||
return new RoleAuthorityView(
|
||||
roleAuthority.getRoleId(),
|
||||
roleAuthority.getAuthorityId(),
|
||||
roleAuthority.getCreatedAt()
|
||||
);
|
||||
}
|
||||
|
||||
public Long getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
public Long getAuthorityId() {
|
||||
return authorityId;
|
||||
}
|
||||
|
||||
public void setAuthorityId(Long authorityId) {
|
||||
this.authorityId = authorityId;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RoleAuthorityView that = (RoleAuthorityView) o;
|
||||
return Objects.equals(roleId, that.roleId) &&
|
||||
Objects.equals(authorityId, that.authorityId) &&
|
||||
Objects.equals(createdAt, that.createdAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(roleId, authorityId, createdAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RoleAuthorityView{" +
|
||||
"roleId=" + roleId +
|
||||
", authorityId=" + authorityId +
|
||||
", createdAt=" + createdAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new builder instance.
|
||||
*
|
||||
* @return a new RoleAuthorityViewBuilder
|
||||
*/
|
||||
public static RoleAuthorityViewBuilder builder() {
|
||||
return new RoleAuthorityViewBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for RoleAuthorityView.
|
||||
*/
|
||||
public static class RoleAuthorityViewBuilder {
|
||||
private Long roleId;
|
||||
private Long authorityId;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
private RoleAuthorityViewBuilder() {
|
||||
}
|
||||
|
||||
public RoleAuthorityViewBuilder roleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleAuthorityViewBuilder authorityId(Long authorityId) {
|
||||
this.authorityId = authorityId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleAuthorityViewBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleAuthorityView build() {
|
||||
return new RoleAuthorityView(roleId, authorityId, createdAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
package com.onixbyte.helix.domain.view;
|
||||
|
||||
import com.onixbyte.helix.constant.Status;
|
||||
import com.onixbyte.helix.domain.entity.Role;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* View object for Role entity.
|
||||
* <p>
|
||||
* This class represents a view of the Role entity, providing a data transfer object
|
||||
* for role information within the access control system.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
public class RoleView {
|
||||
|
||||
/**
|
||||
* The unique identifier for the role.
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* The human-readable name of the role.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The unique code identifier for the role.
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* The sort order for displaying roles.
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* Indicates whether this role is assigned by default to new users.
|
||||
*/
|
||||
private Boolean defaultValue;
|
||||
|
||||
/**
|
||||
* A detailed description of the role's purpose and permissions.
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* The current status of the role.
|
||||
*/
|
||||
private Status status;
|
||||
|
||||
/**
|
||||
* The timestamp when this role record was created.
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* The timestamp when this role record was last updated.
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public RoleView() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with all fields.
|
||||
*/
|
||||
public RoleView(Long id, String name, String code, Integer sort, Boolean defaultValue,
|
||||
String description, Status status, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.code = code;
|
||||
this.sort = sort;
|
||||
this.defaultValue = defaultValue;
|
||||
this.description = description;
|
||||
this.status = status;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a RoleView from a Role entity.
|
||||
*
|
||||
* @param role the Role entity
|
||||
* @return the RoleView object
|
||||
*/
|
||||
public static RoleView fromEntity(Role role) {
|
||||
if (role == null) {
|
||||
return null;
|
||||
}
|
||||
return new RoleView(
|
||||
role.getId(),
|
||||
role.getName(),
|
||||
role.getCode(),
|
||||
role.getSort(),
|
||||
role.getDefaultValue(),
|
||||
role.getDescription(),
|
||||
role.getStatus(),
|
||||
role.getCreatedAt(),
|
||||
role.getUpdatedAt()
|
||||
);
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public void setSort(Integer sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
public Boolean getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void setDefaultValue(Boolean defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
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;
|
||||
RoleView roleView = (RoleView) o;
|
||||
return Objects.equals(id, roleView.id) &&
|
||||
Objects.equals(name, roleView.name) &&
|
||||
Objects.equals(code, roleView.code) &&
|
||||
Objects.equals(sort, roleView.sort) &&
|
||||
Objects.equals(defaultValue, roleView.defaultValue) &&
|
||||
Objects.equals(description, roleView.description) &&
|
||||
status == roleView.status &&
|
||||
Objects.equals(createdAt, roleView.createdAt) &&
|
||||
Objects.equals(updatedAt, roleView.updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name, code, sort, defaultValue, description, status, createdAt, updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RoleView{" +
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' +
|
||||
", code='" + code + '\'' +
|
||||
", sort=" + sort +
|
||||
", defaultValue=" + defaultValue +
|
||||
", description='" + description + '\'' +
|
||||
", status=" + status +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new builder instance.
|
||||
*
|
||||
* @return a new RoleViewBuilder
|
||||
*/
|
||||
public static RoleViewBuilder builder() {
|
||||
return new RoleViewBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for RoleView.
|
||||
*/
|
||||
public static class RoleViewBuilder {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String code;
|
||||
private Integer sort;
|
||||
private Boolean defaultValue;
|
||||
private String description;
|
||||
private Status status;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private RoleViewBuilder() {
|
||||
}
|
||||
|
||||
public RoleViewBuilder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleViewBuilder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleViewBuilder code(String code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleViewBuilder sort(Integer sort) {
|
||||
this.sort = sort;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleViewBuilder defaultValue(Boolean defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleViewBuilder description(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleViewBuilder status(Status status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleViewBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleViewBuilder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RoleView build() {
|
||||
return new RoleView(id, name, code, sort, defaultValue, description, status, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package com.onixbyte.helix.domain.view;
|
||||
|
||||
import com.onixbyte.helix.constant.IdentityProvider;
|
||||
import com.onixbyte.helix.domain.entity.UserIdentity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* View object for UserIdentity entity.
|
||||
* <p>
|
||||
* This class represents a view of the UserIdentity entity, providing a data transfer object
|
||||
* for external identity mapping information in the Helix system.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
public class UserIdentityView {
|
||||
|
||||
/**
|
||||
* The identifier of the internal user account.
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* The external identity provider.
|
||||
*/
|
||||
private IdentityProvider provider;
|
||||
|
||||
/**
|
||||
* The unique identifier from the external provider.
|
||||
*/
|
||||
private String externalId;
|
||||
|
||||
/**
|
||||
* The timestamp when this identity mapping was created.
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* The timestamp when this identity mapping was last updated.
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public UserIdentityView() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with all fields.
|
||||
*/
|
||||
public UserIdentityView(Long userId, IdentityProvider provider, String externalId,
|
||||
LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.userId = userId;
|
||||
this.provider = provider;
|
||||
this.externalId = externalId;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a UserIdentityView from a UserIdentity entity.
|
||||
*
|
||||
* @param userIdentity the UserIdentity entity
|
||||
* @return the UserIdentityView object
|
||||
*/
|
||||
public static UserIdentityView fromEntity(UserIdentity userIdentity) {
|
||||
if (userIdentity == null) {
|
||||
return null;
|
||||
}
|
||||
return new UserIdentityView(
|
||||
userIdentity.getUserId(),
|
||||
userIdentity.getProvider(),
|
||||
userIdentity.getExternalId(),
|
||||
userIdentity.getCreatedAt(),
|
||||
userIdentity.getUpdatedAt()
|
||||
);
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public IdentityProvider getProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public void setProvider(IdentityProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public String getExternalId() {
|
||||
return externalId;
|
||||
}
|
||||
|
||||
public void setExternalId(String externalId) {
|
||||
this.externalId = externalId;
|
||||
}
|
||||
|
||||
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;
|
||||
UserIdentityView that = (UserIdentityView) o;
|
||||
return Objects.equals(userId, that.userId) &&
|
||||
provider == that.provider &&
|
||||
Objects.equals(externalId, that.externalId) &&
|
||||
Objects.equals(createdAt, that.createdAt) &&
|
||||
Objects.equals(updatedAt, that.updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(userId, provider, externalId, createdAt, updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UserIdentityView{" +
|
||||
"userId=" + userId +
|
||||
", provider=" + provider +
|
||||
", externalId='" + externalId + '\'' +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new builder instance.
|
||||
*
|
||||
* @return a new UserIdentityViewBuilder
|
||||
*/
|
||||
public static UserIdentityViewBuilder builder() {
|
||||
return new UserIdentityViewBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for UserIdentityView.
|
||||
*/
|
||||
public static class UserIdentityViewBuilder {
|
||||
private Long userId;
|
||||
private IdentityProvider provider;
|
||||
private String externalId;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private UserIdentityViewBuilder() {
|
||||
}
|
||||
|
||||
public UserIdentityViewBuilder userId(Long userId) {
|
||||
this.userId = userId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserIdentityViewBuilder provider(IdentityProvider provider) {
|
||||
this.provider = provider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserIdentityViewBuilder externalId(String externalId) {
|
||||
this.externalId = externalId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserIdentityViewBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserIdentityViewBuilder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserIdentityView build() {
|
||||
return new UserIdentityView(userId, provider, externalId, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package com.onixbyte.helix.domain.view;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.UserRole;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* View object for UserRole entity.
|
||||
* <p>
|
||||
* This class represents a view of the UserRole entity, providing a data transfer object
|
||||
* for user-role association information in the access control system.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
public class UserRoleView {
|
||||
|
||||
/**
|
||||
* The identifier of the role in this association.
|
||||
*/
|
||||
private Long roleId;
|
||||
|
||||
/**
|
||||
* The identifier of the user in this association.
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* The timestamp when this user-role assignment was created.
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public UserRoleView() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with all fields.
|
||||
*/
|
||||
public UserRoleView(Long roleId, Long userId, LocalDateTime createdAt) {
|
||||
this.roleId = roleId;
|
||||
this.userId = userId;
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a UserRoleView from a UserRole entity.
|
||||
*
|
||||
* @param userRole the UserRole entity
|
||||
* @return the UserRoleView object
|
||||
*/
|
||||
public static UserRoleView fromEntity(UserRole userRole) {
|
||||
if (userRole == null) {
|
||||
return null;
|
||||
}
|
||||
return new UserRoleView(
|
||||
userRole.getRoleId(),
|
||||
userRole.getUserId(),
|
||||
userRole.getCreatedAt()
|
||||
);
|
||||
}
|
||||
|
||||
public Long getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
UserRoleView that = (UserRoleView) o;
|
||||
return Objects.equals(roleId, that.roleId) &&
|
||||
Objects.equals(userId, that.userId) &&
|
||||
Objects.equals(createdAt, that.createdAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(roleId, userId, createdAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UserRoleView{" +
|
||||
"roleId=" + roleId +
|
||||
", userId=" + userId +
|
||||
", createdAt=" + createdAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new builder instance.
|
||||
*
|
||||
* @return a new UserRoleViewBuilder
|
||||
*/
|
||||
public static UserRoleViewBuilder builder() {
|
||||
return new UserRoleViewBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for UserRoleView.
|
||||
*/
|
||||
public static class UserRoleViewBuilder {
|
||||
private Long roleId;
|
||||
private Long userId;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
private UserRoleViewBuilder() {
|
||||
}
|
||||
|
||||
public UserRoleViewBuilder roleId(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserRoleViewBuilder userId(Long userId) {
|
||||
this.userId = userId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserRoleViewBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserRoleView build() {
|
||||
return new UserRoleView(roleId, userId, createdAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
package com.onixbyte.helix.domain.view;
|
||||
|
||||
import com.onixbyte.helix.constant.UserStatus;
|
||||
import com.onixbyte.helix.domain.entity.User;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* View object for User entity.
|
||||
* <p>
|
||||
* This class represents a view of the User entity, providing a data transfer object
|
||||
* for user information in the Helix system.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @version 1.0
|
||||
* @since 1.0
|
||||
*/
|
||||
public class UserView {
|
||||
|
||||
/**
|
||||
* The unique identifier for the user.
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* The unique username for authentication purposes.
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* The user's complete full name.
|
||||
*/
|
||||
private String fullName;
|
||||
|
||||
/**
|
||||
* The user's email address.
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* The country code for the user's phone number.
|
||||
*/
|
||||
private String regionAbbreviation;
|
||||
|
||||
/**
|
||||
* The user's phone number without the country code.
|
||||
*/
|
||||
private String phoneNumber;
|
||||
|
||||
/**
|
||||
* The URL to the user's avatar image.
|
||||
*/
|
||||
private String avatarUrl;
|
||||
|
||||
/**
|
||||
* The current status of the user account.
|
||||
*/
|
||||
private UserStatus status;
|
||||
|
||||
/**
|
||||
* The identifier of the department to which this user belongs.
|
||||
*/
|
||||
private Long departmentId;
|
||||
|
||||
/**
|
||||
* The identifier of the position held by this user.
|
||||
*/
|
||||
private Long positionId;
|
||||
|
||||
/**
|
||||
* The timestamp when this user record was created.
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* The timestamp when this user record was last updated.
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public UserView() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with all fields (excluding password for security).
|
||||
*/
|
||||
public UserView(Long id, String username, String fullName, String email, String regionAbbreviation,
|
||||
String phoneNumber, String avatarUrl, UserStatus status, Long departmentId,
|
||||
Long positionId, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.fullName = fullName;
|
||||
this.email = email;
|
||||
this.regionAbbreviation = regionAbbreviation;
|
||||
this.phoneNumber = phoneNumber;
|
||||
this.avatarUrl = avatarUrl;
|
||||
this.status = status;
|
||||
this.departmentId = departmentId;
|
||||
this.positionId = positionId;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a UserView from a User entity (excluding password for security).
|
||||
*
|
||||
* @param user the User entity
|
||||
* @return the UserView object
|
||||
*/
|
||||
public static UserView fromEntity(User user) {
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
return new UserView(
|
||||
user.getId(),
|
||||
user.getUsername(),
|
||||
user.getFullName(),
|
||||
user.getEmail(),
|
||||
user.getRegionAbbreviation(),
|
||||
user.getPhoneNumber(),
|
||||
user.getAvatarUrl(),
|
||||
user.getStatus(),
|
||||
user.getDepartmentId(),
|
||||
user.getPositionId(),
|
||||
user.getCreatedAt(),
|
||||
user.getUpdatedAt()
|
||||
);
|
||||
}
|
||||
|
||||
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 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 getRegionAbbreviation() {
|
||||
return regionAbbreviation;
|
||||
}
|
||||
|
||||
public void setRegionAbbreviation(String regionAbbreviation) {
|
||||
this.regionAbbreviation = regionAbbreviation;
|
||||
}
|
||||
|
||||
public String getPhoneNumber() {
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
public void setPhoneNumber(String phoneNumber) {
|
||||
this.phoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
public String getAvatarUrl() {
|
||||
return avatarUrl;
|
||||
}
|
||||
|
||||
public void setAvatarUrl(String avatarUrl) {
|
||||
this.avatarUrl = avatarUrl;
|
||||
}
|
||||
|
||||
public UserStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(UserStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Long getDepartmentId() {
|
||||
return departmentId;
|
||||
}
|
||||
|
||||
public void setDepartmentId(Long departmentId) {
|
||||
this.departmentId = departmentId;
|
||||
}
|
||||
|
||||
public Long getPositionId() {
|
||||
return positionId;
|
||||
}
|
||||
|
||||
public void setPositionId(Long positionId) {
|
||||
this.positionId = positionId;
|
||||
}
|
||||
|
||||
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;
|
||||
UserView userView = (UserView) o;
|
||||
return Objects.equals(id, userView.id) &&
|
||||
Objects.equals(username, userView.username) &&
|
||||
Objects.equals(fullName, userView.fullName) &&
|
||||
Objects.equals(email, userView.email) &&
|
||||
Objects.equals(regionAbbreviation, userView.regionAbbreviation) &&
|
||||
Objects.equals(phoneNumber, userView.phoneNumber) &&
|
||||
Objects.equals(avatarUrl, userView.avatarUrl) &&
|
||||
status == userView.status &&
|
||||
Objects.equals(departmentId, userView.departmentId) &&
|
||||
Objects.equals(positionId, userView.positionId) &&
|
||||
Objects.equals(createdAt, userView.createdAt) &&
|
||||
Objects.equals(updatedAt, userView.updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, username, fullName, email, regionAbbreviation, phoneNumber, avatarUrl,
|
||||
status, departmentId, positionId, createdAt, updatedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UserView{" +
|
||||
"id=" + id +
|
||||
", username='" + username + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", regionAbbreviation='" + regionAbbreviation + '\'' +
|
||||
", phoneNumber='" + phoneNumber + '\'' +
|
||||
", avatarUrl='" + avatarUrl + '\'' +
|
||||
", status=" + status +
|
||||
", departmentId=" + departmentId +
|
||||
", positionId=" + positionId +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new builder instance.
|
||||
*
|
||||
* @return a new UserViewBuilder
|
||||
*/
|
||||
public static UserViewBuilder builder() {
|
||||
return new UserViewBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for UserView.
|
||||
*/
|
||||
public static class UserViewBuilder {
|
||||
private Long id;
|
||||
private String username;
|
||||
private String fullName;
|
||||
private String email;
|
||||
private String regionAbbreviation;
|
||||
private String phoneNumber;
|
||||
private String avatarUrl;
|
||||
private UserStatus status;
|
||||
private Long departmentId;
|
||||
private Long positionId;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private UserViewBuilder() {
|
||||
}
|
||||
|
||||
public UserViewBuilder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserViewBuilder username(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserViewBuilder fullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserViewBuilder email(String email) {
|
||||
this.email = email;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserViewBuilder regionAbbreviation(String regionAbbreviation) {
|
||||
this.regionAbbreviation = regionAbbreviation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserViewBuilder phoneNumber(String phoneNumber) {
|
||||
this.phoneNumber = phoneNumber;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserViewBuilder avatarUrl(String avatarUrl) {
|
||||
this.avatarUrl = avatarUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserViewBuilder status(UserStatus status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserViewBuilder departmentId(Long departmentId) {
|
||||
this.departmentId = departmentId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserViewBuilder positionId(Long positionId) {
|
||||
this.positionId = positionId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserViewBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserViewBuilder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserView build() {
|
||||
return new UserView(id, username, fullName, email, regionAbbreviation, phoneNumber, avatarUrl,
|
||||
status, departmentId, positionId, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.onixbyte.helix.domain.web.request;
|
||||
|
||||
import com.onixbyte.helix.constant.UserStatus;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record AddUserRequest(
|
||||
@NotBlank(message = "Username cannot be empty.")
|
||||
String username,
|
||||
@NotBlank(message = "Password cannot be empty.")
|
||||
String password,
|
||||
@NotBlank(message = "Full name cannot be empty.")
|
||||
String fullName,
|
||||
String email,
|
||||
String regionAbbreviation,
|
||||
String phoneNumber,
|
||||
String avatarUrl,
|
||||
UserStatus status,
|
||||
Long departmentId,
|
||||
Long positionId,
|
||||
List<Long> roleIds
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.onixbyte.helix.domain.web.request;
|
||||
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
|
||||
public record QueryRoleRequest(
|
||||
String name,
|
||||
String code,
|
||||
@Pattern(
|
||||
regexp = "^(ACTIVE|INACTIVE)?$",
|
||||
message = "状态仅可以是 ACTIVE、INACTIVE 其中之一")
|
||||
String status
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.onixbyte.helix.domain.web.request;
|
||||
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public record QueryUserRequest(
|
||||
Long departmentId,
|
||||
String username,
|
||||
String regionAbbreviation,
|
||||
String phoneNumber,
|
||||
@Pattern(
|
||||
regexp = "^(ACTIVE|INACTIVE|LOCKED)?$",
|
||||
message = "状态仅可以是 ACTIVE、INACTIVE 或 LOCKED 其中之一")
|
||||
String status,
|
||||
LocalDateTime createdAtStart,
|
||||
LocalDateTime createdAtEnd
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.onixbyte.helix.domain.web.request;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public record ResetPasswordRequest(
|
||||
@NotNull(message = "用户 ID 不能为空") Long id,
|
||||
@NotBlank(message = "密码不能为空") String password
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.onixbyte.helix.domain.web.request;
|
||||
|
||||
import com.onixbyte.helix.constant.UserStatus;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record UpdateUserRequest(
|
||||
@NotNull(message = "User ID cannot be null")
|
||||
@Positive(message = "User ID must be positive")
|
||||
Long id,
|
||||
String fullName,
|
||||
String email,
|
||||
String regionAbbreviation,
|
||||
String phoneNumber,
|
||||
String avatarUrl,
|
||||
UserStatus status,
|
||||
Long departmentId,
|
||||
Long positionId
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.onixbyte.helix.domain.web.request;
|
||||
|
||||
public record UsernamePasswordLoginRequest(
|
||||
String username,
|
||||
String password,
|
||||
String uuid,
|
||||
String captcha
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.onixbyte.helix.domain.web.response;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Record representing a standardised business exception response.
|
||||
* <p>
|
||||
* This record encapsulates the essential information returned to clients when business logic
|
||||
* exceptions occur within the Helix application. It provides a consistent structure for
|
||||
* error communication, ensuring that all business exception responses follow the same format and
|
||||
* contain the necessary information for client-side error handling.
|
||||
* <p>
|
||||
* The response includes a timestamp indicating when the exception occurred and a human-readable
|
||||
* message explaining the nature of the error. This standardised format enables consistent error
|
||||
* handling across different client applications and API consumers.
|
||||
* <p>
|
||||
* As a record, this class is immutable and provides automatic implementations of {@code equals()},
|
||||
* {@code hashCode()}, and {@code toString()} methods, making it suitable for use in functional
|
||||
* programming patterns and ensuring thread safety in concurrent environments.
|
||||
* <p>
|
||||
* This response entity is typically used by exception handlers and error processing components to
|
||||
* communicate business rule violations, validation failures, and other application-specific errors
|
||||
* to API clients.
|
||||
*
|
||||
* @param timestamp the timestamp when the exception occurred, providing temporal context for error
|
||||
* tracking and debugging
|
||||
* @param message a human-readable explanation of the exception, suitable for display to end users
|
||||
* or logging purposes
|
||||
* @author zihluwang
|
||||
* @see Serializable
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public record BizExceptionResponse(
|
||||
LocalDateTime timestamp,
|
||||
String message
|
||||
) implements Serializable {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.onixbyte.helix.domain.web.response;
|
||||
|
||||
public record CaptchaResponse(
|
||||
String captcha,
|
||||
String uuid
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.onixbyte.helix.domain.web.response;
|
||||
|
||||
public record FileUploadResponse(
|
||||
String originalFileName,
|
||||
String contentType,
|
||||
Long size,
|
||||
String url
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.onixbyte.helix.domain.web.response;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.User;
|
||||
|
||||
public record LoginSuccessResponse(
|
||||
String accessToken,
|
||||
User user
|
||||
) {
|
||||
|
||||
public LoginSuccessResponse {
|
||||
user.setPassword(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package com.onixbyte.helix.domain.web.response;
|
||||
|
||||
import com.onixbyte.helix.constant.UserStatus;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class UserDetailResponse {
|
||||
private String id;
|
||||
private String username;
|
||||
private String fullName;
|
||||
private String email;
|
||||
private String regionAbbreviation;
|
||||
private String phoneNumber;
|
||||
private String avatarUrl;
|
||||
private UserStatus status;
|
||||
private Long departmentId;
|
||||
private String departmentName;
|
||||
private Long positionId;
|
||||
private String positionName;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public UserDetailResponse() {
|
||||
}
|
||||
|
||||
public UserDetailResponse(String id, String username, String fullName, String email, String regionAbbreviation, String phoneNumber, String avatarUrl, UserStatus status, Long departmentId, String departmentName, Long positionId, String positionName, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.fullName = fullName;
|
||||
this.email = email;
|
||||
this.regionAbbreviation = regionAbbreviation;
|
||||
this.phoneNumber = phoneNumber;
|
||||
this.avatarUrl = avatarUrl;
|
||||
this.status = status;
|
||||
this.departmentId = departmentId;
|
||||
this.departmentName = departmentName;
|
||||
this.positionId = positionId;
|
||||
this.positionName = positionName;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
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 getRegionAbbreviation() {
|
||||
return regionAbbreviation;
|
||||
}
|
||||
|
||||
public void setRegionAbbreviation(String regionAbbreviation) {
|
||||
this.regionAbbreviation = regionAbbreviation;
|
||||
}
|
||||
|
||||
public String getPhoneNumber() {
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
public void setPhoneNumber(String phoneNumber) {
|
||||
this.phoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
public String getAvatarUrl() {
|
||||
return avatarUrl;
|
||||
}
|
||||
|
||||
public void setAvatarUrl(String avatarUrl) {
|
||||
this.avatarUrl = avatarUrl;
|
||||
}
|
||||
|
||||
public UserStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(UserStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Long getDepartmentId() {
|
||||
return departmentId;
|
||||
}
|
||||
|
||||
public void setDepartmentId(Long departmentId) {
|
||||
this.departmentId = departmentId;
|
||||
}
|
||||
|
||||
public String getDepartmentName() {
|
||||
return departmentName;
|
||||
}
|
||||
|
||||
public void setDepartmentName(String departmentName) {
|
||||
this.departmentName = departmentName;
|
||||
}
|
||||
|
||||
public Long getPositionId() {
|
||||
return positionId;
|
||||
}
|
||||
|
||||
public void setPositionId(Long positionId) {
|
||||
this.positionId = positionId;
|
||||
}
|
||||
|
||||
public String getPositionName() {
|
||||
return positionName;
|
||||
}
|
||||
|
||||
public void setPositionName(String positionName) {
|
||||
this.positionName = positionName;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public static UserDetailResponseBuilder builder() {
|
||||
return new UserDetailResponseBuilder();
|
||||
}
|
||||
|
||||
public static class UserDetailResponseBuilder {
|
||||
private String id;
|
||||
private String username;
|
||||
private String fullName;
|
||||
private String email;
|
||||
private String regionAbbreviation;
|
||||
private String phoneNumber;
|
||||
private String avatarUrl;
|
||||
private UserStatus status;
|
||||
private Long departmentId;
|
||||
private String departmentName;
|
||||
private Long positionId;
|
||||
private String positionName;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private UserDetailResponseBuilder() {
|
||||
}
|
||||
|
||||
public UserDetailResponseBuilder id(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserDetailResponseBuilder username(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserDetailResponseBuilder fullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserDetailResponseBuilder email(String email) {
|
||||
this.email = email;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserDetailResponseBuilder regionAbbreviation(String regionAbbreviation) {
|
||||
this.regionAbbreviation = regionAbbreviation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserDetailResponseBuilder phoneNumber(String phoneNumber) {
|
||||
this.phoneNumber = phoneNumber;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserDetailResponseBuilder avatarUrl(String avatarUrl) {
|
||||
this.avatarUrl = avatarUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserDetailResponseBuilder status(UserStatus status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserDetailResponseBuilder departmentId(Long departmentId) {
|
||||
this.departmentId = departmentId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserDetailResponseBuilder departmentName(String departmentName) {
|
||||
this.departmentName = departmentName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserDetailResponseBuilder positionId(Long positionId) {
|
||||
this.positionId = positionId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserDetailResponseBuilder positionName(String positionName) {
|
||||
this.positionName = positionName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserDetailResponseBuilder createdAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserDetailResponseBuilder updatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserDetailResponse build() {
|
||||
return new UserDetailResponse(id, username, fullName, email, regionAbbreviation, phoneNumber, avatarUrl, status, departmentId, departmentName, positionId, positionName, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.onixbyte.helix.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
/**
|
||||
* Custom runtime exception for business logic violations and application-specific errors.
|
||||
* <p>
|
||||
* This exception is designed to handle business rule violations, validation failures, and other
|
||||
* application-specific error conditions that occur during normal operation. Unlike
|
||||
* system exceptions, business exceptions are expected and should be handled gracefully by the
|
||||
* application's error handling mechanisms.
|
||||
* <p>
|
||||
* Each business exception carries an HTTP status code that indicates the appropriate response
|
||||
* status to return to clients when the exception occurs. This enables consistent error handling
|
||||
* across REST API endpoints and provides meaningful HTTP responses to API consumers.
|
||||
* <p>
|
||||
* Common use cases include:
|
||||
* <ul>
|
||||
* <li>Resource not found scenarios (404 Not Found)</li>
|
||||
* <li>Validation failures (400 Bad Request)</li>
|
||||
* <li>Authorisation violations (403 Forbidden)</li>
|
||||
* <li>Business rule violations (422 Unprocessable Entity)</li>
|
||||
* <li>Conflict situations (409 Conflict)</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* The exception integrates seamlessly with Spring Boot's exception handling framework and can be
|
||||
* caught by global exception handlers to produce standardised error responses.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @see RuntimeException
|
||||
* @see org.springframework.http.HttpStatus
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class BizException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* The HTTP status code associated with this business exception.
|
||||
* <p>
|
||||
* This status code indicates the appropriate HTTP response status that should be returned to
|
||||
* clients when this exception occurs. It enables consistent error handling across
|
||||
* REST API endpoints.
|
||||
*/
|
||||
private final HttpStatus status;
|
||||
|
||||
/**
|
||||
* Constructs a new business exception with the specified HTTP status and message.
|
||||
*
|
||||
* @param message the detailed error message explaining the business logic violation
|
||||
*/
|
||||
public BizException(String message) {
|
||||
super(message);
|
||||
this.status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new business exception with the specified HTTP status and message.
|
||||
*
|
||||
* @param status the HTTP status code to associate with this exception
|
||||
* @param message the detailed error message explaining the business logic violation
|
||||
*/
|
||||
public BizException(HttpStatus status, String message) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP status code associated with this business exception.
|
||||
*
|
||||
* @return the HTTP status code that should be used in the error response
|
||||
*/
|
||||
public HttpStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Extension and plugin support package for the Helix application.
|
||||
* <p>
|
||||
* This package is designed to contain extension points, plugin interfaces,
|
||||
* and extensibility mechanisms that allow the Helix application to be
|
||||
* extended with additional functionality without modifying core components.
|
||||
* <p>
|
||||
* <strong>Intended Contents:</strong>
|
||||
* <ul>
|
||||
* <li><strong>Extension Interfaces:</strong> Define contracts for pluggable components</li>
|
||||
* <li><strong>Plugin Loaders:</strong> Mechanisms for discovering and loading extensions</li>
|
||||
* <li>
|
||||
* <strong>Extension Points:</strong> Well-defined points where custom functionality can
|
||||
* be injected
|
||||
* </li>
|
||||
* <li><strong>Custom Annotations:</strong> Annotations for marking and configuring extensions</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* <strong>Design Principles:</strong>
|
||||
* <ul>
|
||||
* <li>
|
||||
* <strong>Loose Coupling:</strong> Extensions should be loosely coupled to core functionality
|
||||
* </li>
|
||||
* <li>
|
||||
* <strong>Service Provider Interface (SPI):</strong> Use SPI patterns for plugin discovery
|
||||
* </li>
|
||||
* <li>
|
||||
* <strong>Configuration-Driven:</strong> Allow extensions to be configured through
|
||||
* application properties
|
||||
* </li>
|
||||
* <li>
|
||||
* <strong>Lifecycle Management:</strong> Provide proper initialisation and cleanup for extensions
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* This package follows the extensibility patterns commonly used in enterprise applications to
|
||||
* support customisation and third-party integrations whilst maintaining system stability
|
||||
* and performance.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @since 1.0.0
|
||||
*/
|
||||
package com.onixbyte.helix.extension;
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.onixbyte.helix.extension.redis.serializer;
|
||||
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
|
||||
public class JacksonSerialiser {
|
||||
|
||||
public static final GenericJackson2JsonRedisSerializer INSTANCE = initialiseSerializer();
|
||||
|
||||
private static GenericJackson2JsonRedisSerializer initialiseSerializer() {
|
||||
var serializer = new GenericJackson2JsonRedisSerializer();
|
||||
|
||||
serializer.configure((configurer) -> {
|
||||
configurer.registerModule(new JavaTimeModule());
|
||||
configurer.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
});
|
||||
|
||||
return serializer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.onixbyte.helix.filter;
|
||||
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.auth0.jwt.exceptions.JWTVerificationException;
|
||||
import com.onixbyte.helix.manager.AuthorityManager;
|
||||
import com.onixbyte.helix.manager.UserManager;
|
||||
import com.onixbyte.helix.security.authentication.UsernamePasswordAuthentication;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
@Component
|
||||
public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(TokenAuthenticationFilter.class);
|
||||
|
||||
private final Algorithm algorithm;
|
||||
private final UserManager userManager;
|
||||
private final AuthorityManager authorityManager;
|
||||
|
||||
public TokenAuthenticationFilter(Algorithm algorithm, UserManager userManager, AuthorityManager authorityManager) {
|
||||
this.algorithm = algorithm;
|
||||
this.userManager = userManager;
|
||||
this.authorityManager = authorityManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(
|
||||
@NonNull HttpServletRequest request,
|
||||
@NonNull HttpServletResponse response,
|
||||
@NonNull FilterChain filterChain
|
||||
) throws ServletException, IOException {
|
||||
var token = request.getHeader("Authorization");
|
||||
if (Objects.isNull(token) || token.isBlank()) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!token.startsWith("Bearer ")) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
token = token.substring(7);
|
||||
var verifier = JWT.require(algorithm)
|
||||
.withIssuer("Helix Server")
|
||||
.build();
|
||||
|
||||
try {
|
||||
var decodedToken = verifier.verify(token);
|
||||
var username = decodedToken.getSubject();
|
||||
|
||||
var user = userManager.selectByUsername(username);
|
||||
var authorities = authorityManager.queryByUserId(user.getId())
|
||||
.stream()
|
||||
.map((authority) -> (GrantedAuthority) authority::getCode)
|
||||
.toList();
|
||||
|
||||
user.setPassword(null);
|
||||
|
||||
var authentication = UsernamePasswordAuthentication.authenticated(user, authorities);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
filterChain.doFilter(request, response);
|
||||
} catch (JWTVerificationException e) {
|
||||
log.error("JWT verification failed.", e);
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.onixbyte.helix.manager;
|
||||
|
||||
import com.onixbyte.helix.properties.ApplicationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ApplicationManager {
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
public ApplicationManager(ApplicationProperties applicationProperties) {
|
||||
this.applicationProperties = applicationProperties;
|
||||
}
|
||||
|
||||
public String getDefaultEmail() {
|
||||
return applicationProperties.defaultEmail();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.onixbyte.helix.manager;
|
||||
|
||||
import com.onixbyte.helix.constant.CacheName;
|
||||
import com.onixbyte.helix.domain.entity.Asset;
|
||||
import com.onixbyte.helix.exception.BizException;
|
||||
import com.onixbyte.helix.mapper.AssetMapper;
|
||||
import com.onixbyte.helix.repository.AssetRepository;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.CachePut;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class AssetManager {
|
||||
|
||||
private final AssetMapper assetMapper;
|
||||
private final AssetRepository assetRepository;
|
||||
|
||||
public AssetManager(AssetMapper assetMapper, AssetRepository assetRepository) {
|
||||
this.assetMapper = assetMapper;
|
||||
this.assetRepository = assetRepository;
|
||||
}
|
||||
|
||||
@CachePut(cacheNames = CacheName.ASSET, key = "#result.id", unless = "#result == null")
|
||||
public Asset save(Asset asset) {
|
||||
return assetRepository.save(asset);
|
||||
}
|
||||
|
||||
@Cacheable(cacheNames = CacheName.ASSET, key = "#assetId")
|
||||
public Asset queryByAssetId(Long assetId) {
|
||||
return assetRepository.findById(assetId)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@CacheEvict(cacheNames = CacheName.ASSET, key = "#assetId")
|
||||
public void deleteById(Long assetId) {
|
||||
assetRepository.deleteById(assetId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.onixbyte.helix.manager;
|
||||
|
||||
import com.onixbyte.helix.constant.CacheName;
|
||||
import com.onixbyte.helix.domain.entity.Authority;
|
||||
import com.onixbyte.helix.mapper.AuthorityMapper;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class AuthorityManager {
|
||||
|
||||
private final AuthorityMapper authorityMapper;
|
||||
|
||||
public AuthorityManager(AuthorityMapper authorityMapper) {
|
||||
this.authorityMapper = authorityMapper;
|
||||
}
|
||||
|
||||
@Cacheable(cacheNames = CacheName.AUTHORITIES_OF_USER, key = "#userId")
|
||||
public List<Authority> queryByUserId(Long userId) {
|
||||
return authorityMapper.selectByUserId(userId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.onixbyte.helix.manager;
|
||||
|
||||
import com.onixbyte.helix.client.RedisClient;
|
||||
import com.onixbyte.helix.constant.CacheName;
|
||||
import com.onixbyte.helix.properties.CaptchaProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
@Component
|
||||
public class CaptchaManager {
|
||||
|
||||
private final RedisClient redisClient;
|
||||
|
||||
@Autowired
|
||||
public CaptchaManager(RedisClient redisClient) {
|
||||
this.redisClient = redisClient;
|
||||
}
|
||||
|
||||
public void setCaptcha(String uuid, String captchaCode) {
|
||||
redisClient.set(buildCacheKey(uuid), captchaCode, Duration.ofMinutes(5L));
|
||||
}
|
||||
|
||||
public String getCaptcha(String uuid) {
|
||||
var key = buildCacheKey(uuid);
|
||||
var captcha = redisClient.get(key, String.class);
|
||||
redisClient.delete(key);
|
||||
return captcha;
|
||||
}
|
||||
|
||||
private String buildCacheKey(String uuid) {
|
||||
return "captcha::" + uuid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.onixbyte.helix.manager;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.Department;
|
||||
import com.onixbyte.helix.mapper.DepartmentMapper;
|
||||
import com.onixbyte.helix.repository.DepartmentRepository;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Component
|
||||
public class DepartmentManager {
|
||||
|
||||
private final DepartmentMapper departmentMapper;
|
||||
private final DepartmentRepository departmentRepository;
|
||||
|
||||
public DepartmentManager(DepartmentMapper departmentMapper, DepartmentRepository departmentRepository) {
|
||||
this.departmentMapper = departmentMapper;
|
||||
this.departmentRepository = departmentRepository;
|
||||
}
|
||||
|
||||
public Page<Department> selectAll(Pageable pageable) {
|
||||
return departmentRepository.findAll(pageable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.onixbyte.helix.manager;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.Menu;
|
||||
import com.onixbyte.helix.mapper.MenuMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class MenuManager {
|
||||
private static final Logger log = LoggerFactory.getLogger(MenuManager.class);
|
||||
private final MenuMapper menuMapper;
|
||||
|
||||
public MenuManager(MenuMapper menuMapper) {
|
||||
this.menuMapper = menuMapper;
|
||||
}
|
||||
|
||||
public List<Menu> selectActiveMenusByUserId(Long userId) {
|
||||
return menuMapper.selectActiveMenusByUserId(userId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.onixbyte.helix.manager;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.Position;
|
||||
import com.onixbyte.helix.mapper.PositionMapper;
|
||||
import com.onixbyte.helix.repository.PositionRepository;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class PositionManager {
|
||||
|
||||
private final PositionMapper positionMapper;
|
||||
private final PositionRepository positionRepository;
|
||||
|
||||
public PositionManager(PositionMapper positionMapper, PositionRepository positionRepository) {
|
||||
this.positionMapper = positionMapper;
|
||||
this.positionRepository = positionRepository;
|
||||
}
|
||||
|
||||
public Page<Position> selectAll(Pageable pageable) {
|
||||
return positionRepository.findAll(pageable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.onixbyte.helix.manager;
|
||||
|
||||
import com.onixbyte.helix.domain.database.query.wrapper.QueryRoleWrapper;
|
||||
import com.onixbyte.helix.domain.entity.Role;
|
||||
import com.onixbyte.helix.exception.BizException;
|
||||
import com.onixbyte.helix.mapper.RoleMapper;
|
||||
import com.onixbyte.helix.repository.RoleRepository;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class RoleManager {
|
||||
|
||||
private final RoleMapper roleMapper;
|
||||
private final RoleRepository roleRepository;
|
||||
|
||||
public RoleManager(RoleMapper roleMapper, RoleRepository roleRepository) {
|
||||
this.roleMapper = roleMapper;
|
||||
this.roleRepository = roleRepository;
|
||||
}
|
||||
|
||||
public void validateRoles(List<Long> roleIds) {
|
||||
if (!roleMapper.areRolesExisted(roleIds)) {
|
||||
throw new BizException(HttpStatus.BAD_REQUEST, "Role does not exist in database.");
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<Role> getRole(Role example) {
|
||||
return roleRepository.findOne(Example.of(example));
|
||||
}
|
||||
|
||||
public Page<Role> selectAll(Pageable pageable, QueryRoleWrapper wrapper) {
|
||||
var records = roleMapper.selectAll(pageable, wrapper);
|
||||
var total = roleMapper.count(wrapper);
|
||||
|
||||
return new PageImpl<>(records, pageable, total);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.onixbyte.helix.manager;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.Setting;
|
||||
import com.onixbyte.helix.repository.SettingRepository;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class SettingManager {
|
||||
|
||||
private final SettingRepository settingRepository;
|
||||
|
||||
public SettingManager(SettingRepository settingRepository) {
|
||||
this.settingRepository = settingRepository;
|
||||
}
|
||||
|
||||
@Cacheable(cacheNames = "setting", key = "#name")
|
||||
public Setting getSettingByName(String name) {
|
||||
return settingRepository.getSettingByName(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.onixbyte.helix.manager;
|
||||
|
||||
import com.onixbyte.helix.common.regex.Patterns;
|
||||
import com.onixbyte.helix.constant.CacheName;
|
||||
import com.onixbyte.helix.domain.database.query.wrapper.QueryUserWrapper;
|
||||
import com.onixbyte.helix.domain.entity.User;
|
||||
import com.onixbyte.helix.domain.web.response.UserDetailResponse;
|
||||
import com.onixbyte.helix.exception.BizException;
|
||||
import com.onixbyte.helix.mapper.UserMapper;
|
||||
import com.onixbyte.helix.repository.UserRepository;
|
||||
import com.onixbyte.region.Region;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.cache.annotation.CachePut;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class UserManager {
|
||||
|
||||
private final UserMapper userMapper;
|
||||
private final UserRepository userRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public UserManager(UserMapper userMapper, UserRepository userRepository, PasswordEncoder passwordEncoder) {
|
||||
this.userMapper = userMapper;
|
||||
this.userRepository = userRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user by username, and cache this user by username.
|
||||
*
|
||||
* @param username username
|
||||
* @return user
|
||||
*/
|
||||
@Cacheable(cacheNames = CacheName.USER, key = "#username", unless = "#result == null")
|
||||
public User selectByUsername(String username) {
|
||||
return userRepository.findOne(Example.of(User.builder()
|
||||
.username(username)
|
||||
.build()))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query paginated users.
|
||||
*
|
||||
* @param pageable page request
|
||||
* @return page result
|
||||
*/
|
||||
public Page<UserDetailResponse> selectUserDetailsPage(Pageable pageable, QueryUserWrapper wrapper) {
|
||||
var result = userMapper.selectListWithDetails(pageable, wrapper);
|
||||
var total = userMapper.count(wrapper);
|
||||
return new PageImpl<>(result, pageable, total);
|
||||
}
|
||||
|
||||
@CachePut(cacheNames = CacheName.USER, key = "#user.username")
|
||||
public User save(User user) {
|
||||
return userRepository.save(user);
|
||||
}
|
||||
|
||||
public UserDetailResponse queryUserDetailByUserId(Long userId) {
|
||||
return userMapper.selectWithDetailByUserId(userId);
|
||||
}
|
||||
|
||||
public void deleteById(Long userId) {
|
||||
userRepository.deleteById(userId);
|
||||
}
|
||||
|
||||
@CachePut(cacheNames = CacheName.USER, key = "#result.username", unless = "#result == null")
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
public User updateUser(User user) {
|
||||
var userToUpdate = userRepository.findById(user.getId())
|
||||
.orElseThrow(() -> new BizException(HttpStatus.BAD_REQUEST, "找不到 ID 为" + user.getId() + "的用户信息"));
|
||||
|
||||
Optional.ofNullable(user.getPassword())
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.map(passwordEncoder::encode)
|
||||
.ifPresent(userToUpdate::setPassword);
|
||||
|
||||
Optional.ofNullable(user.getFullName())
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.ifPresent(userToUpdate::setFullName);
|
||||
|
||||
Optional.ofNullable(user.getEmail())
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.filter((email) -> Patterns.EMAIL.asPredicate().test(email))
|
||||
.ifPresent(userToUpdate::setEmail);
|
||||
|
||||
Optional.ofNullable(user.getRegionAbbreviation())
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.filter(Region::isValidAbbreviation)
|
||||
.ifPresent(userToUpdate::setRegionAbbreviation);
|
||||
|
||||
Optional.ofNullable(user.getPhoneNumber())
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.ifPresent(userToUpdate::setPhoneNumber);
|
||||
|
||||
Optional.ofNullable(user.getAvatarUrl())
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.filter((avatarUrl) -> Patterns.IMAGE_URL.asPredicate().test(avatarUrl) || Patterns.GRAVATAR_IMAGE_URL.asPredicate().test(avatarUrl))
|
||||
.ifPresent(userToUpdate::setAvatarUrl);
|
||||
|
||||
Optional.ofNullable(user.getStatus())
|
||||
.ifPresent(userToUpdate::setStatus);
|
||||
|
||||
Optional.ofNullable(user.getDepartmentId())
|
||||
.ifPresent(userToUpdate::setDepartmentId);
|
||||
|
||||
Optional.ofNullable(user.getPositionId())
|
||||
.ifPresent(userToUpdate::setPositionId);
|
||||
|
||||
return userToUpdate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.onixbyte.helix.manager;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.UserRole;
|
||||
import com.onixbyte.helix.exception.BizException;
|
||||
import com.onixbyte.helix.mapper.UserRoleMapper;
|
||||
import com.onixbyte.helix.repository.UserRoleRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class UserRoleManager {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(UserRoleManager.class);
|
||||
private final UserRoleMapper userRoleMapper;
|
||||
private final UserRoleRepository userRoleRepository;
|
||||
|
||||
public UserRoleManager(UserRoleMapper userRoleMapper, UserRoleRepository userRoleRepository) {
|
||||
this.userRoleMapper = userRoleMapper;
|
||||
this.userRoleRepository = userRoleRepository;
|
||||
}
|
||||
|
||||
public List<UserRole> saveBatch(List<UserRole> userRoles) {
|
||||
return userRoleRepository.saveAll(userRoles);
|
||||
}
|
||||
|
||||
public void deleteByUserId(Long userId) {
|
||||
var affectedRows = userRoleRepository.deleteByUserId(userId);
|
||||
log.info("用户 {} 的角色关联被全部移除(共 {} 条)。", userId, affectedRows);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Business logic management and orchestration package for the Helix application.
|
||||
* <p>
|
||||
* This package is designed to contain manager classes that orchestrate complex business operations,
|
||||
* coordinate between multiple services, and handle cross-cutting concerns that span multiple
|
||||
* domain boundaries.
|
||||
* <p>
|
||||
* <strong>Manager Pattern:</strong> Managers in this package serve as facades or coordinators that
|
||||
* encapsulate complex business workflows, typically involving multiple services, repositories,
|
||||
* or external systems. They provide a higher-level abstraction over individual service components.
|
||||
* <p>
|
||||
* <strong>Intended Contents:</strong>
|
||||
* <ul>
|
||||
* <li>
|
||||
* <strong>Workflow Managers:</strong> Orchestrate multi-step business processes
|
||||
* </li>
|
||||
* <li>
|
||||
* <strong>Integration Managers:</strong> Coordinate interactions with external systems
|
||||
* </li>
|
||||
* <li>
|
||||
* <strong>Transaction Managers:</strong> Handle complex transactional scenarios
|
||||
* </li>
|
||||
* <li>
|
||||
* <strong>Cache Managers:</strong> Manage caching strategies and cache invalidation
|
||||
* </li>
|
||||
* <li>
|
||||
* <strong>Event Managers:</strong> Coordinate event publishing and handling
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* <strong>Design Guidelines:</strong>
|
||||
* <ul>
|
||||
* <li>
|
||||
* <strong>Single Responsibility:</strong> Each manager should focus on a specific business domain
|
||||
* or cross-cutting concern
|
||||
* </li>
|
||||
* <li>
|
||||
* <strong>Service Coordination:</strong> Managers should delegate to services rather than
|
||||
* implementing business logic directly
|
||||
* </li>
|
||||
* <li>
|
||||
* <strong>Transaction Boundaries:</strong> Define clear transactional boundaries for
|
||||
* complex operations
|
||||
* </li>
|
||||
* <li>
|
||||
* <strong>Error Handling:</strong> Provide comprehensive error handling and rollback mechanisms
|
||||
* for complex workflows
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Managers typically sit between the controller layer and the service layer, providing a
|
||||
* coordination point for complex operations that require multiple service interactions or
|
||||
* cross-cutting concerns.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @since 1.0.0
|
||||
* @see com.onixbyte.helix.service
|
||||
* @see org.springframework.transaction.annotation.Transactional
|
||||
*/
|
||||
package com.onixbyte.helix.manager;
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.onixbyte.helix.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* Asset related database operations.
|
||||
*
|
||||
* @author zihluwang
|
||||
*/
|
||||
@Mapper
|
||||
public interface AssetMapper {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.onixbyte.helix.mapper;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.Authority;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface AuthorityMapper {
|
||||
|
||||
/**
|
||||
* Select authorities that is granted to the specific user.
|
||||
*
|
||||
* @param userId user ID
|
||||
* @return authorities
|
||||
*/
|
||||
List<Authority> selectByUserId(@Param("userId") Long userId);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.onixbyte.helix.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface DepartmentMapper {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.onixbyte.helix.mapper;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.Menu;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface MenuMapper {
|
||||
List<Menu> selectActiveMenusByUserId(@Param("userId") Long userId);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.onixbyte.helix.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface PositionMapper {
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.onixbyte.helix.mapper;
|
||||
|
||||
import com.onixbyte.helix.domain.database.query.wrapper.QueryRoleWrapper;
|
||||
import com.onixbyte.helix.domain.entity.Role;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface RoleMapper {
|
||||
|
||||
boolean areRolesExisted(@Param("roleIds") List<Long> roleIds);
|
||||
|
||||
List<Role> selectAll(
|
||||
@Param("pageable") Pageable page,
|
||||
@Param("wrapper") QueryRoleWrapper wrapper
|
||||
);
|
||||
|
||||
Integer count(@Param("wrapper") QueryRoleWrapper wrapper);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.onixbyte.helix.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface SettingMapper {
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.onixbyte.helix.mapper;
|
||||
|
||||
import com.onixbyte.helix.domain.database.query.wrapper.QueryUserWrapper;
|
||||
import com.onixbyte.helix.domain.web.response.UserDetailResponse;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface UserMapper {
|
||||
|
||||
int count(@Param("wrapper") QueryUserWrapper request);
|
||||
|
||||
List<UserDetailResponse> selectListWithDetails(
|
||||
@Param("pageable") Pageable pageable,
|
||||
@Param("wrapper") QueryUserWrapper wrapper
|
||||
);
|
||||
|
||||
UserDetailResponse selectWithDetailByUserId(@Param("id") Long userId);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.onixbyte.helix.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface UserRoleMapper {
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Data processing and transformation components for the Helix application.
|
||||
* <p>
|
||||
* This package contains processors that handle data transformation, validation, and business
|
||||
* logic processing. Processors in this package follow the Chain of Responsibility pattern and
|
||||
* Strategy pattern to provide flexible and extensible data processing capabilities.
|
||||
* <p>
|
||||
* <strong>Processing Architecture:</strong> The Helix application uses a processor-based
|
||||
* architecture for complex data transformations and business rule applications. Processors can be
|
||||
* chained together to create sophisticated data processing pipelines.
|
||||
* <p>
|
||||
* <strong>Intended Contents:</strong>
|
||||
* <ul>
|
||||
* <li><strong>Data Processors:</strong> Transform and validate incoming data</li>
|
||||
* <li><strong>Business Rule Processors:</strong> Apply business logic and rules</li>
|
||||
* <li><strong>Validation Processors:</strong> Perform complex validation operations</li>
|
||||
* <li><strong>Pipeline Processors:</strong> Orchestrate multiple processing steps</li>
|
||||
* <li><strong>Event Processors:</strong> Handle domain events and notifications</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* <strong>Design Principles:</strong>
|
||||
* <ul>
|
||||
* <li>
|
||||
* <strong>Single Responsibility:</strong> Each processor handles one specific type of processing
|
||||
* or transformation
|
||||
* </li>
|
||||
* <li>
|
||||
* <strong>Composability:</strong> Processors can be combined to create complex
|
||||
* processing workflows
|
||||
* </li>
|
||||
* <li>
|
||||
* <strong>Immutability:</strong> Processors should not modify input data directly; instead,
|
||||
* they should return processed results
|
||||
* </li>
|
||||
* <li>
|
||||
* <strong>Error Handling:</strong> Processors should handle errors gracefully and provide
|
||||
* meaningful error messages
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* <strong>Common Patterns:</strong>
|
||||
* <ul>
|
||||
* <li>
|
||||
* <strong>Chain of Responsibility:</strong> Link processors together for sequential processing
|
||||
* </li>
|
||||
* <li><strong>Strategy Pattern:</strong> Allow runtime selection of processing algorithms</li>
|
||||
* <li><strong>Template Method:</strong> Define processing skeleton with customisable steps</li>
|
||||
* <li><strong>Observer Pattern:</strong> Notify interested parties of processing events</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* All processors in this package should be stateless and thread-safe to support concurrent
|
||||
* processing scenarios.
|
||||
*
|
||||
* @author zihluwang
|
||||
* @since 1.0.0
|
||||
* @see com.onixbyte.helix.service
|
||||
* @see com.onixbyte.helix.domain.entity
|
||||
*/
|
||||
package com.onixbyte.helix.processor;
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.onixbyte.helix.properties;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
|
||||
@ConfigurationProperties(prefix = "app.common")
|
||||
public record ApplicationProperties(
|
||||
@DefaultValue("default@helix.onixbyte.dev") String defaultEmail
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.onixbyte.helix.properties;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
|
||||
/**
|
||||
* Configuration properties for S3-compatible file storage services.
|
||||
* <p>
|
||||
* This class encapsulates the essential settings required to connect to and utilise S3-based file
|
||||
* storage providers. It supports custom endpoints, regions, and credentials, allowing integration
|
||||
* with AWS S3 or compatible services.<br>
|
||||
* <p>
|
||||
* The configuration is intentionally minimal, with sensible defaults for common providers. Use this
|
||||
* class to enable S3 storage, specify connection details, and select the provider as appropriate.
|
||||
* <p>
|
||||
* Example usage in <code>application.yml</code>:
|
||||
* <pre>{@code
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param endpoint endpoint URL for the S3 service
|
||||
* @param publicHost public host for access file anonymously
|
||||
* @param pathStyle whether to enable path style for this s3 service
|
||||
* @param bucket indicates which bucket is the target
|
||||
* @param region region in which the S3 bucket is located
|
||||
* @param accessKeyId access key ID used for authentication with the S3 provider
|
||||
* @param secretAccessKey secret access key used for authentication with the S3 provider
|
||||
* @author zihluwang
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "app.asset")
|
||||
public record AssetProperties(
|
||||
String endpoint,
|
||||
String publicHost,
|
||||
@DefaultValue("false") boolean pathStyle,
|
||||
String bucket,
|
||||
String region,
|
||||
String accessKeyId,
|
||||
String secretAccessKey
|
||||
) {
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user