feat: 初始提交
This commit is contained in:
+149
@@ -0,0 +1,149 @@
|
|||||||
|
### macOS
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
*~
|
||||||
|
|
||||||
|
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||||
|
.fuse_hidden*
|
||||||
|
|
||||||
|
# Metadata left by Dolphin file manager, which comes with KDE Plasma
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Linux trash folder which might appear on any partition or disk
|
||||||
|
.Trash-*
|
||||||
|
|
||||||
|
# .nfs files are created when an open file is removed but is still being accessed
|
||||||
|
.nfs*
|
||||||
|
|
||||||
|
# Log files created by default by the nohup command
|
||||||
|
nohup.out
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
Thumbs.db:encryptable
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
### JetBrains IDE
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, GoLand, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based HTTP Client
|
||||||
|
http-client.private.env.json
|
||||||
|
|
||||||
|
### Gradle
|
||||||
|
.gradle
|
||||||
|
**/build/
|
||||||
|
!**/src/**/build/
|
||||||
|
|
||||||
|
!gradle/wrapper/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!gradle/wrapper/gradle-wrapper.properties
|
||||||
|
gradle-app.setting
|
||||||
|
.gradletasknamecache
|
||||||
|
|
||||||
|
# Eclipse Gradle plugin generated files
|
||||||
|
# Eclipse Core
|
||||||
|
.project
|
||||||
|
# JDT-specific (Eclipse Java Development Tools)
|
||||||
|
.classpath
|
||||||
|
|
||||||
|
### Server
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
*.jar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
replay_pid*
|
||||||
|
|
||||||
|
# Config
|
||||||
|
config/*.yml
|
||||||
|
config/*.yaml
|
||||||
|
config/*.properties
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
test/
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("org.springframework.boot") version "3.5.4"
|
||||||
|
id("io.spring.dependency-management") version "1.1.7"
|
||||||
|
}
|
||||||
|
|
||||||
|
val artefactVersion: String by project
|
||||||
|
|
||||||
|
group = "com.onixbyte.helix"
|
||||||
|
version = artefactVersion
|
||||||
|
|
||||||
|
tasks.withType<JavaCompile> {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.jspecify.core)
|
||||||
|
implementation(platform(libs.aws.sdk.bom))
|
||||||
|
implementation(libs.aws.sdk.s3)
|
||||||
|
implementation(libs.commons.io)
|
||||||
|
implementation(libs.commons.lang)
|
||||||
|
implementation(libs.commons.collections)
|
||||||
|
implementation(libs.hypersistence.core)
|
||||||
|
implementation(platform(libs.onixbyte.versionCatalogue))
|
||||||
|
implementation(libs.onixbyte.tuple)
|
||||||
|
implementation(libs.onixbyte.commonToolbox)
|
||||||
|
implementation(libs.onixbyte.identityGenerator)
|
||||||
|
implementation(libs.onixbyte.captcha)
|
||||||
|
implementation(libs.onixbyte.regions)
|
||||||
|
implementation(libs.jwt.core)
|
||||||
|
implementation(libs.spring.boot.configurationProcessor)
|
||||||
|
implementation(libs.spring.boot.starter.web)
|
||||||
|
implementation(libs.spring.boot.starter.webFlux)
|
||||||
|
implementation(libs.spring.boot.starter.validation)
|
||||||
|
implementation(libs.spring.boot.starter.redis)
|
||||||
|
implementation(libs.spring.boot.starter.cache)
|
||||||
|
implementation(libs.spring.boot.starter.security)
|
||||||
|
implementation(libs.spring.boot.starter.jpa)
|
||||||
|
implementation(libs.mybatis.starter.core)
|
||||||
|
implementation(libs.jackson.jsr310)
|
||||||
|
testImplementation(libs.spring.boot.starter.test)
|
||||||
|
testImplementation(libs.reactor.test)
|
||||||
|
testImplementation(libs.spring.security.test)
|
||||||
|
testImplementation(libs.mybatis.starter.test)
|
||||||
|
runtimeOnly(libs.postgres.driver)
|
||||||
|
testRuntimeOnly(libs.h2.database)
|
||||||
|
testRuntimeOnly(libs.junit.launcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
spring:
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
# Redis 主机
|
||||||
|
host: ${REDIS_HOST:-localhost}
|
||||||
|
# Redis 端口
|
||||||
|
port: ${REDIS_PORT:-6379}
|
||||||
|
# Redis 数据库索引
|
||||||
|
database: ${REDIS_DATABASE_INDEX:-0}
|
||||||
|
# Redis 数据库密码
|
||||||
|
password: ${REDIS_PASSWORD:-}
|
||||||
|
datasource:
|
||||||
|
# 数据库连接池类型
|
||||||
|
type: com.zaxxer.hikari.HikariDataSource
|
||||||
|
# 数据库驱动类
|
||||||
|
driver-class-name: org.postgresql.Driver
|
||||||
|
# 数据库 URL
|
||||||
|
url: jdbc:postgresql://${PG_HOST:-localhost}:${PG_PORT:-5432}/${PG_DATABASE:-helix}
|
||||||
|
# 数据库用户
|
||||||
|
username: ${PG_USER}
|
||||||
|
# 数据库密码
|
||||||
|
password: ${PG_PASSWORD}
|
||||||
|
|
||||||
|
app:
|
||||||
|
asset:
|
||||||
|
# 是否开启 S3 文件存储服务
|
||||||
|
enabled: true
|
||||||
|
# S3 服务端点(若使用非 AWS 提供的 S3 兼容 API,请添加该配置)
|
||||||
|
# endpoint: https://endpoint.s3.service
|
||||||
|
# S3 服务区域(详情请见 S3 服务提供商)
|
||||||
|
# region: apac
|
||||||
|
# 公开域名
|
||||||
|
public-host: https://s3.my.app
|
||||||
|
# 是否开启 Path Style
|
||||||
|
path-style: false
|
||||||
|
# 存储桶名称
|
||||||
|
bucket: dev
|
||||||
|
# S3 访问密钥 ID
|
||||||
|
access-key-id: ${S3_ACCESS_KEY_ID}
|
||||||
|
# S3 访问密钥机密
|
||||||
|
secret-access-key: ${S3_SECRET_ACCESS_KEY}
|
||||||
|
# 鉴权配置
|
||||||
|
authentication:
|
||||||
|
# Microsoft Entra ID 鉴权配置
|
||||||
|
msal:
|
||||||
|
# Microsoft Entra ID 提供的客户端 ID
|
||||||
|
client-id: ${MSAL_CLIENT_ID}
|
||||||
|
# Microsoft Entra ID 提供的租户 ID
|
||||||
|
tenant-id: ${MSAL_TENANT_ID}
|
||||||
|
# 令牌配置
|
||||||
|
jwt:
|
||||||
|
# 令牌签发人
|
||||||
|
issuer: Helix Server
|
||||||
|
# 令牌机密
|
||||||
|
secret: ${TOKEN_SECRET:-1234567890abcdefghijklmnopqrstuv}
|
||||||
|
# 令牌有效期(Ref java.time.Duration)
|
||||||
|
valid-time: PT2H
|
||||||
|
# 跨域配置
|
||||||
|
cors:
|
||||||
|
# 是否允许身份验证
|
||||||
|
allow-credentials: true
|
||||||
|
# 是否允许私有网络
|
||||||
|
allow-private-network: true
|
||||||
|
# 允许的请求头列表
|
||||||
|
allowed-headers: Content-Type
|
||||||
|
# 允许的请求方法列表(Ref org.springframework.http.HttpMethod)
|
||||||
|
# 2025.11.6注
|
||||||
|
# 由于 Spring 解析问题,在此处使用小写的情况下会导致在请求头中存在 Origin 时出现 Invalid CORS Request 的问题,请务必使用大写
|
||||||
|
allowed-methods:
|
||||||
|
- GET
|
||||||
|
- POST
|
||||||
|
- PUT
|
||||||
|
- PATCH
|
||||||
|
- DELETE
|
||||||
|
# 允许的来源域名
|
||||||
|
allowed-origins: '*'
|
||||||
|
# 要对外暴露的响应头列表
|
||||||
|
exposed-headers: X-Authorisation
|
||||||
|
# 跨域缓存时长
|
||||||
|
max-age: PT2H
|
||||||
|
# Captcha 配置
|
||||||
|
captcha:
|
||||||
|
# Captcha 长度
|
||||||
|
length: 6
|
||||||
@@ -0,0 +1,324 @@
|
|||||||
|
/**
|
||||||
|
* IMPORTANT NOTE ON DATABASE CREATION:
|
||||||
|
* * If you intend to create the database using a user other than the one specified
|
||||||
|
* for the application (e.g., 'system_admin' creating a database for 'app_user'),
|
||||||
|
* please ensure this SQL file is executed by the **corresponding database user**.
|
||||||
|
* * This is crucial for correctly setting up ownership and default privileges.
|
||||||
|
* * Example: CREATE DATABASE my_database OWNER app_user;
|
||||||
|
* * Ensure all necessary user roles and permissions are in place **prior** to
|
||||||
|
* running this initialisation script.
|
||||||
|
*/
|
||||||
|
|
||||||
|
--- Type Definitions ---
|
||||||
|
DROP TYPE IF EXISTS USER_STATUS CASCADE;
|
||||||
|
DROP TYPE IF EXISTS STATUS CASCADE;
|
||||||
|
DROP TYPE IF EXISTS IDENTITY_PROVIDER CASCADE;
|
||||||
|
DROP TYPE IF EXISTS SETTING_TYPE CASCADE;
|
||||||
|
|
||||||
|
CREATE TYPE USER_STATUS AS ENUM ('ACTIVE', 'INACTIVE', 'LOCKED');
|
||||||
|
CREATE TYPE STATUS AS ENUM ('ACTIVE', 'INACTIVE');
|
||||||
|
CREATE TYPE IDENTITY_PROVIDER AS ENUM ('LOCAL', 'OIDC', 'MICROSOFT_ENTRA_ID', 'GOOGLE_OIDC', 'SAML');
|
||||||
|
CREATE TYPE SETTING_TYPE AS ENUM ('STRING', 'BOOLEAN', 'INT');
|
||||||
|
|
||||||
|
--- Departments Table ---
|
||||||
|
DROP TABLE IF EXISTS departments CASCADE;
|
||||||
|
CREATE TABLE departments
|
||||||
|
(
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
name VARCHAR(128) NOT NULL UNIQUE,
|
||||||
|
parent_id BIGINT NULL REFERENCES departments (id),
|
||||||
|
sort INT NOT NULL DEFAULT NULL,
|
||||||
|
status STATUS NOT NULL DEFAULT 'ACTIVE'::STATUS,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX departments_name_index ON departments (name);
|
||||||
|
CREATE INDEX departments_parent_id_index ON departments (parent_id);
|
||||||
|
|
||||||
|
--- Departments Data Insertion ---
|
||||||
|
INSERT INTO departments (id, name, parent_id, sort, status)
|
||||||
|
VALUES (1, 'Company HQ', NULL, 1, 'ACTIVE'::STATUS),
|
||||||
|
(2, 'Human Resources', 1, 1, 'ACTIVE'::STATUS),
|
||||||
|
(3, 'Finance', 1, 2, 'ACTIVE'::STATUS),
|
||||||
|
(4, 'Technology', 1, 3, 'ACTIVE'::STATUS),
|
||||||
|
(5, 'IT Support', 4, 1, 'ACTIVE'::STATUS),
|
||||||
|
(6, 'Software Development', 4, 2, 'ACTIVE'::STATUS),
|
||||||
|
(7, 'Operations', 1, 4, 'INACTIVE'::STATUS);
|
||||||
|
|
||||||
|
--- Positions Table ---
|
||||||
|
DROP TABLE IF EXISTS positions CASCADE;
|
||||||
|
CREATE TABLE positions
|
||||||
|
(
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
name VARCHAR(128) NOT NULL UNIQUE,
|
||||||
|
code VARCHAR(64) NULL UNIQUE,
|
||||||
|
description TEXT,
|
||||||
|
sort INT NOT NULL DEFAULT 0,
|
||||||
|
status STATUS NOT NULL DEFAULT 'ACTIVE',
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX positions_name_index ON positions (name);
|
||||||
|
CREATE INDEX positions_code_index ON positions (code);
|
||||||
|
CREATE INDEX positions_name_code_index ON positions (name, code);
|
||||||
|
|
||||||
|
--- Positions Data Insertion ---
|
||||||
|
INSERT INTO positions (id, name, code, description, sort, status)
|
||||||
|
VALUES (1, 'HR Manager', 'HR-MGR',
|
||||||
|
'Responsible for overseeing recruitment, employee relations, and staff wellbeing.', 1,
|
||||||
|
'ACTIVE'),
|
||||||
|
(2, 'Finance Officer', 'FIN-OFC',
|
||||||
|
'Handles accounts, prepares financial statements, and ensures compliance with regulations.',
|
||||||
|
2, 'ACTIVE'),
|
||||||
|
(3, 'IT Support Specialist', 'IT-SPT',
|
||||||
|
'Provides technical assistance, manages helpdesk queries, and maintains computer systems.',
|
||||||
|
3, 'ACTIVE'),
|
||||||
|
(4, 'Software Engineer', 'SWE-ENG',
|
||||||
|
'Develops and maintains in-house applications, ensuring code quality and system reliability.',
|
||||||
|
4, 'ACTIVE'),
|
||||||
|
(5, 'Operations Coordinator', 'OPS-CRD',
|
||||||
|
'Assists with day-to-day logistics, procurement, and office organisation.', 5, 'INACTIVE');
|
||||||
|
|
||||||
|
--- Users Table ---
|
||||||
|
DROP TABLE IF EXISTS users CASCADE;
|
||||||
|
CREATE TABLE users
|
||||||
|
(
|
||||||
|
id BIGINT PRIMARY KEY,
|
||||||
|
username VARCHAR(64) UNIQUE NOT NULL,
|
||||||
|
password VARCHAR(255),
|
||||||
|
full_name VARCHAR(128) NOT NULL,
|
||||||
|
email VARCHAR(128) UNIQUE,
|
||||||
|
region_code VARCHAR(10),
|
||||||
|
phone_number VARCHAR(32),
|
||||||
|
avatar_url TEXT,
|
||||||
|
status USER_STATUS NOT NULL DEFAULT 'ACTIVE',
|
||||||
|
department_id BIGINT REFERENCES departments (id),
|
||||||
|
position_id BIGINT REFERENCES positions (id),
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX users_username_index ON users (username);
|
||||||
|
|
||||||
|
--- Users Table Indexes ---
|
||||||
|
CREATE UNIQUE INDEX uidx_users_region_abbreviation_phone_number
|
||||||
|
ON users (region_abbreviation, phone_number);
|
||||||
|
|
||||||
|
--- Users Data Insertion ---
|
||||||
|
-- NOTE: All phone numbers are generated by ChatGPT, they should not be connected any real person.
|
||||||
|
INSERT INTO users(id, username, password, full_name, email, region_abbreviation, phone_number, avatar_url,
|
||||||
|
department_id, position_id, created_at, updated_at)
|
||||||
|
VALUES (1, 'helix', null, 'Helix Admin', 'admin@helix.onixbyte.dev', 'GB', '7000000000',
|
||||||
|
'https://gravatar.com/avatar/6ef4c4033f6aa8e43d06bd5e462a6173cc2a960633473721a6f1289cd1b5146f',
|
||||||
|
1, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
|
(2, 'johndoe', null, 'John Doe', 'johndoe@helix.onixbyte.dev', 'GB', '7000000001',
|
||||||
|
'https://gravatar.com/avatar/41bcebddd573747d1bd35ef7fae72ebefd6b47f077d42442a2510d35b0c2db92',
|
||||||
|
6, 4, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
|
||||||
|
|
||||||
|
--- User Identities Table ---
|
||||||
|
DROP TABLE IF EXISTS user_identities CASCADE;
|
||||||
|
CREATE TABLE user_identities
|
||||||
|
(
|
||||||
|
user_id BIGINT NOT NULL REFERENCES users (id),
|
||||||
|
provider IDENTITY_PROVIDER NOT NULL,
|
||||||
|
external_id VARCHAR(255) NOT NULL,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (user_id, provider, external_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
--- Roles Table ---
|
||||||
|
DROP TABLE IF EXISTS roles CASCADE;
|
||||||
|
CREATE TABLE roles
|
||||||
|
(
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
name VARCHAR(128) NOT NULL UNIQUE,
|
||||||
|
code VARCHAR(64) NOT NULL UNIQUE,
|
||||||
|
sort INTEGER NOT NULL,
|
||||||
|
default_value BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
description TEXT,
|
||||||
|
status STATUS NOT NULL DEFAULT 'ACTIVE',
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX roles_name_index ON roles (name);
|
||||||
|
CREATE INDEX roles_code_index ON roles (code);
|
||||||
|
CREATE INDEX roles_name_code_index ON roles (name, code);
|
||||||
|
|
||||||
|
--- Roles Data Insertion ---
|
||||||
|
INSERT INTO roles (name, code, sort, default_value, description, status, created_at, updated_at)
|
||||||
|
VALUES ('Admin', 'admin', 1, FALSE, 'Administrator of this system.', 'ACTIVE'::STATUS,
|
||||||
|
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
|
('Normal User', 'user', 2, TRUE, 'Normal user of this system.', 'ACTIVE'::STATUS,
|
||||||
|
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
|
||||||
|
|
||||||
|
--- User Roles Table ---
|
||||||
|
DROP TABLE IF EXISTS user_roles CASCADE;
|
||||||
|
CREATE TABLE user_roles
|
||||||
|
(
|
||||||
|
user_id BIGINT NOT NULL
|
||||||
|
CONSTRAINT user_roles_users_id_fk REFERENCES users,
|
||||||
|
role_id BIGINT NOT NULL
|
||||||
|
CONSTRAINT user_roles_roles_id_fk REFERENCES roles,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
CONSTRAINT user_roles_pk PRIMARY KEY (user_id, role_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
--- User Roles Data Insertion ---
|
||||||
|
INSERT INTO user_roles
|
||||||
|
VALUES (1, 1),
|
||||||
|
(2, 2);
|
||||||
|
|
||||||
|
--- Authorities Table ---
|
||||||
|
DROP TABLE IF EXISTS authorities CASCADE;
|
||||||
|
CREATE TABLE authorities
|
||||||
|
(
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
code VARCHAR(128) NOT NULL UNIQUE,
|
||||||
|
name VARCHAR(128) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
status STATUS NOT NULL DEFAULT 'ACTIVE'::STATUS,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX authorities_code_index ON authorities (code);
|
||||||
|
|
||||||
|
--- Authorities Data Insertion ---
|
||||||
|
INSERT INTO authorities(code, name, description, status, created_at, updated_at)
|
||||||
|
VALUES ('system:dashboard:read', 'Read Dashboard', 'Read dashboard.', 'ACTIVE'::STATUS,
|
||||||
|
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
|
('system:user:read', 'Read User', 'Read user.', 'ACTIVE'::STATUS, CURRENT_TIMESTAMP,
|
||||||
|
CURRENT_TIMESTAMP),
|
||||||
|
('system:user_detail:read', 'Read User', 'Read user detail.', 'ACTIVE'::STATUS,
|
||||||
|
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
|
('system:user:write', 'Write User', 'Write user, such as add, edit or delete.',
|
||||||
|
'ACTIVE'::STATUS, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
|
('system:department:read', 'Read Department', 'Read departments', 'ACTIVE'::STATUS,
|
||||||
|
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
|
('system:department:write', 'Write Department', 'Write departments.', 'ACTIVE'::STATUS,
|
||||||
|
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
|
('system:role:read', 'Read Roles', 'Read roles.', 'ACTIVE'::STATUS, CURRENT_TIMESTAMP,
|
||||||
|
CURRENT_TIMESTAMP),
|
||||||
|
('system:role:write', 'Write Roles', 'Write roles.', 'ACTIVE'::STATUS,
|
||||||
|
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
|
('system:authority:read', 'Read Authorities', 'Read authorities.', 'ACTIVE'::STATUS,
|
||||||
|
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
|
('system:authority:write', 'Write Authorities', 'Write authorities.',
|
||||||
|
'ACTIVE'::STATUS, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
|
('system:audit_log:read', 'Read Audit Logs', 'Read audit logs.', 'ACTIVE'::STATUS,
|
||||||
|
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
|
('system:sso:write', 'Manage SSO',
|
||||||
|
'Manage SSO configurations (such as Microsoft Entra ID, etc.).', 'ACTIVE'::STATUS,
|
||||||
|
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||||
|
('system:setting:write', 'Write System Settings', 'Write system settings.',
|
||||||
|
'ACTIVE'::STATUS, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
|
||||||
|
|
||||||
|
--- Role Authorities Table ---
|
||||||
|
DROP TABLE IF EXISTS role_authorities CASCADE;
|
||||||
|
CREATE TABLE role_authorities
|
||||||
|
(
|
||||||
|
role_id BIGINT NOT NULL REFERENCES roles (id),
|
||||||
|
authority_id BIGINT NOT NULL REFERENCES authorities (id),
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (role_id, authority_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
--- Role Authorities Data Insertion ---
|
||||||
|
INSERT INTO role_authorities
|
||||||
|
VALUES (1, 1, CURRENT_TIMESTAMP),
|
||||||
|
(1, 2, CURRENT_TIMESTAMP),
|
||||||
|
(1, 3, CURRENT_TIMESTAMP),
|
||||||
|
(1, 4, CURRENT_TIMESTAMP),
|
||||||
|
(1, 5, CURRENT_TIMESTAMP),
|
||||||
|
(1, 6, CURRENT_TIMESTAMP),
|
||||||
|
(1, 7, CURRENT_TIMESTAMP),
|
||||||
|
(1, 8, CURRENT_TIMESTAMP),
|
||||||
|
(1, 9, CURRENT_TIMESTAMP),
|
||||||
|
(1, 10, CURRENT_TIMESTAMP),
|
||||||
|
(1, 11, CURRENT_TIMESTAMP),
|
||||||
|
(1, 12, CURRENT_TIMESTAMP),
|
||||||
|
(1, 13, CURRENT_TIMESTAMP),
|
||||||
|
(2, 1, CURRENT_TIMESTAMP),
|
||||||
|
(2, 2, CURRENT_TIMESTAMP),
|
||||||
|
(2, 3, CURRENT_TIMESTAMP),
|
||||||
|
(2, 5, CURRENT_TIMESTAMP),
|
||||||
|
(2, 7, CURRENT_TIMESTAMP),
|
||||||
|
(2, 9, CURRENT_TIMESTAMP),
|
||||||
|
(2, 11, CURRENT_TIMESTAMP);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS assets;
|
||||||
|
CREATE TABLE assets
|
||||||
|
(
|
||||||
|
id BIGSERIAL NOT NULL PRIMARY KEY,
|
||||||
|
key VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
upload_by BIGINT NOT NULL REFERENCES users (id),
|
||||||
|
upload_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX assets_key_index ON assets (key);
|
||||||
|
|
||||||
|
COMMENT ON TABLE assets IS 'Stores metadata for files or other digital assets within the system.';
|
||||||
|
COMMENT ON COLUMN assets.id IS 'The unique identifier for the asset, automatically generated by the database.';
|
||||||
|
COMMENT ON COLUMN assets.key IS 'The unique key or path of the asset within the storage system.';
|
||||||
|
COMMENT ON COLUMN assets.upload_by IS 'The unique ID of the user who uploaded this asset, referencing the ID in the users table.';
|
||||||
|
COMMENT ON COLUMN assets.upload_time IS 'The timestamp indicating when the asset was uploaded to the system.';
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS settings;
|
||||||
|
CREATE TABLE settings
|
||||||
|
(
|
||||||
|
id BIGSERIAL NOT NULL PRIMARY KEY,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
description VARCHAR(255) NULL,
|
||||||
|
type SETTING_TYPE NOT NULL,
|
||||||
|
value VARCHAR(255) NULL,
|
||||||
|
default_value VARCHAR(255) NOT NULL,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX settings_name_index ON settings (name);
|
||||||
|
|
||||||
|
COMMENT ON TABLE settings IS 'Hot-deployable application settings.';
|
||||||
|
COMMENT ON COLUMN settings.id IS 'Setting unique identifier.';
|
||||||
|
COMMENT ON COLUMN settings.name IS 'Setting name.';
|
||||||
|
COMMENT ON COLUMN settings.description IS 'Setting description.';
|
||||||
|
COMMENT ON COLUMN settings.type IS 'The type of the value.';
|
||||||
|
COMMENT ON COLUMN settings.value IS 'Setting current value.';
|
||||||
|
COMMENT ON COLUMN settings.default_value IS 'Setting default value.';
|
||||||
|
|
||||||
|
INSERT INTO settings(name, description, type, value, default_value)
|
||||||
|
VALUES ('captcha-setting::enabled', 'Whether captcha is enabled.', 'BOOLEAN'::SETTING_TYPE, 'true',
|
||||||
|
'false'),
|
||||||
|
('auth-setting::register-enabled', 'Whether register is enabled', 'BOOLEAN'::SETTING_TYPE,
|
||||||
|
'true', 'false');
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS menus;
|
||||||
|
CREATE TABLE menus
|
||||||
|
(
|
||||||
|
id BIGSERIAL NOT NULL PRIMARY KEY,
|
||||||
|
name VARCHAR(50) NOT NULL,
|
||||||
|
parent_id BIGINT NULL DEFAULT NULL,
|
||||||
|
code VARCHAR(255) NOT NULL,
|
||||||
|
sort INTEGER NOT NULL DEFAULT 0,
|
||||||
|
path VARCHAR(255) NULL DEFAULT NULL,
|
||||||
|
is_external_link BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
is_visible BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
status STATUS NOT NULL DEFAULT 'ACTIVE'::STATUS,
|
||||||
|
authority_code VARCHAR(128) NULL DEFAULT NULL,
|
||||||
|
icon VARCHAR(128) NULL DEFAULT NULL,
|
||||||
|
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
update_time TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX menus_code_uindex ON menus (code);
|
||||||
|
|
||||||
|
INSERT INTO menus(id, name, parent_id, code, sort, path, is_external_link, is_visible, status,
|
||||||
|
authority_code, icon, create_time, update_time)
|
||||||
|
VALUES (1, '系统管理', NULL, 'system-manage', 99, NULL, FALSE, TRUE, 'ACTIVE'::STATUS, NULL, NULL,
|
||||||
|
NOW(), NOW()),
|
||||||
|
(2, '用户管理', 1, 'user-manage', 1, '/users', FALSE, TRUE, 'ACTIVE'::STATUS,
|
||||||
|
'system:user:write', NULL, NOW(), NOW());
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
artefactVersion=0.0.1
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
[versions]
|
||||||
|
jspecifyVersion = "1.0.0"
|
||||||
|
javaJwtVersion = "4.5.0"
|
||||||
|
postgresDriverVersion = "42.7.7"
|
||||||
|
h2Version = "2.2.224"
|
||||||
|
springSecurityVersion = "6.5.2"
|
||||||
|
springBootVersion = "3.5.4"
|
||||||
|
reactorVersion = "3.7.8"
|
||||||
|
junitPlatformVersion = "1.12.2"
|
||||||
|
onixbyteVersion = "3.2.0"
|
||||||
|
onixbyteCaptcha = "1.1.0"
|
||||||
|
onixbyteRegions = "2025.12.0"
|
||||||
|
awsSdkVersion = "2.25.48"
|
||||||
|
commonsIoVersion = "2.16.1"
|
||||||
|
commonsCollections = "4.5.0"
|
||||||
|
commonsLangVersion = "3.20.0"
|
||||||
|
mybatisVersion = "3.0.5"
|
||||||
|
jacksonVersion = "2.19.2"
|
||||||
|
hypersistenceVersion = "3.14.0"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
jwt-core = { group = "com.auth0", name = "java-jwt", version.ref = "javaJwtVersion" }
|
||||||
|
postgres-driver = { group = "org.postgresql", name = "postgresql", version.ref = "postgresDriverVersion" }
|
||||||
|
h2-database = { group = "com.h2database", name = "h2", version.ref = "h2Version" }
|
||||||
|
spring-boot-configurationProcessor = { group = "org.springframework.boot", name = "spring-boot-configuration-processor", version.ref = "springBootVersion" }
|
||||||
|
spring-boot-starter-web = { group = "org.springframework.boot", name = "spring-boot-starter-web", version.ref = "springBootVersion" }
|
||||||
|
spring-boot-starter-webFlux = { group = "org.springframework.boot", name = "spring-boot-starter-webflux", version.ref = "springBootVersion" }
|
||||||
|
spring-boot-starter-validation = { group = "org.springframework.boot", name = "spring-boot-starter-validation", version.ref = "springBootVersion" }
|
||||||
|
spring-boot-starter-redis = { group = "org.springframework.boot", name = "spring-boot-starter-data-redis", version.ref = "springBootVersion" }
|
||||||
|
spring-boot-starter-cache = { group = "org.springframework.boot", name = "spring-boot-starter-cache", version.ref = "springBootVersion" }
|
||||||
|
spring-boot-starter-security = { group = "org.springframework.boot", name = "spring-boot-starter-security", version.ref = "springBootVersion" }
|
||||||
|
spring-boot-starter-test = { group = "org.springframework.boot", name = "spring-boot-starter-test", version.ref = "springBootVersion" }
|
||||||
|
spring-security-test = { group = "org.springframework.security", name = "spring-security-test", version.ref = "springSecurityVersion" }
|
||||||
|
reactor-test = { group = "io.projectreactor", name = "reactor-test", version.ref = "reactorVersion" }
|
||||||
|
junit-launcher = { group = "org.junit.platform", name = "junit-platform-launcher", version.ref = "junitPlatformVersion" }
|
||||||
|
onixbyte-versionCatalogue = { group = "com.onixbyte", name = "version-catalogue", version.ref = "onixbyteVersion" }
|
||||||
|
onixbyte-tuple = { group = "com.onixbyte", name = "tuple", version.ref = "onixbyteVersion" }
|
||||||
|
onixbyte-commonToolbox = { group = "com.onixbyte", name = "common-toolbox", version.ref = "onixbyteVersion" }
|
||||||
|
onixbyte-identityGenerator = { group = "com.onixbyte", name = "identity-generator", version.ref = "onixbyteVersion" }
|
||||||
|
onixbyte-captcha = { group = "com.onixbyte", name = "captcha", version.ref = "onixbyteCaptcha" }
|
||||||
|
onixbyte-regions = { group = "com.onixbyte", name = "regions4j", version.ref = "onixbyteRegions" }
|
||||||
|
aws-sdk-bom = { group = "software.amazon.awssdk", name = "bom", version.ref = "awsSdkVersion" }
|
||||||
|
aws-sdk-s3 = { group = "software.amazon.awssdk", name = "s3" }
|
||||||
|
commons-io = { group = "commons-io", name = "commons-io", version.ref = "commonsIoVersion" }
|
||||||
|
commons-collections = { group = "org.apache.commons", name = "commons-collections4", version.ref = "commonsCollections" }
|
||||||
|
mybatis-starter-core = { group = "org.mybatis.spring.boot", name = "mybatis-spring-boot-starter", version.ref = "mybatisVersion" }
|
||||||
|
mybatis-starter-test = { group = "org.mybatis.spring.boot", name = "mybatis-spring-boot-starter-test", version.ref = "mybatisVersion" }
|
||||||
|
jackson-jsr310 = { group = "com.fasterxml.jackson.datatype", name = "jackson-datatype-jsr310", version.ref = "jacksonVersion" }
|
||||||
|
spring-boot-starter-jpa = { group = "org.springframework.boot", name = "spring-boot-starter-data-jpa" }
|
||||||
|
hypersistence-core = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-63", version.ref = "hypersistenceVersion" }
|
||||||
|
commons-lang = { group = "org.apache.commons", name = "commons-lang3", version.ref = "commonsLangVersion" }
|
||||||
|
jspecify-core = { group = "org.jspecify", name = "jspecify", version.ref = "jspecifyVersion" }
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
Vendored
+94
@@ -0,0 +1,94 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%"=="" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
rootProject.name = "helix-server"
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user