Compare commits
58 Commits
main
..
dedf95865e
| Author | SHA1 | Date | |
|---|---|---|---|
| dedf95865e | |||
| 9d2fc024ea | |||
| 6f29904349 | |||
| 08c18fea90 | |||
| 776ddd28c1 | |||
| a3596ad086 | |||
| e1ad5cdfd8 | |||
| 712a675325 | |||
| 8b343af4e8 | |||
| 69e3f84bec | |||
| aebb693ee7 | |||
| 7b9849c311 | |||
| d33f4f2dbf | |||
| 484a9f4a71 | |||
|
7c9f9c35f9
|
|||
| bfa0690065 | |||
| abbcb6bf5c | |||
| bd597fbc64 | |||
| 14e53740b0 | |||
| 7ce241cc16 | |||
| 0562c8548b | |||
| e83f1358e3 | |||
| 2de2cb029d | |||
| d2b086ce17 | |||
| e5ae5f61b5 | |||
| d9bd1a1b0e | |||
| 9b48869f3f | |||
| 64be3a79ea | |||
| e9c6732888 | |||
| 891b7f9280 | |||
| 3095e34164 | |||
| d6944957c8 | |||
| ba2ed6d22c | |||
|
f6096c5ab7
|
|||
|
4dc9ed341d
|
|||
|
c13520bd8b
|
|||
|
a8c349bd75
|
|||
|
a526ac795d
|
|||
|
b9fee3787a
|
|||
|
0a10c64278
|
|||
| 8d148f88d0 | |||
| 2db2b594f6 | |||
| a5da09cf09 | |||
| 9b8d276e0b | |||
| 382a6c177f | |||
| d25c754bf0 | |||
| 4ca7791e26 | |||
| 5406d810a6 | |||
| b24a6dc809 | |||
| d2d0ccef1c | |||
| c74a67cdc6 | |||
| 659d123f2b | |||
| f62e26d441 | |||
| 05b6bbca79 | |||
| 670e54aefb | |||
|
aa19c91465
|
|||
| 3f1c320a48 | |||
| 826f51926b |
+152
@@ -0,0 +1,152 @@
|
|||||||
|
### 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
|
||||||
|
|
||||||
|
### 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/
|
||||||
|
|
||||||
|
### Gradle
|
||||||
|
.gradle
|
||||||
|
**/build/
|
||||||
|
!**/src/**/build/
|
||||||
|
|
||||||
|
!gradle/
|
||||||
|
!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
|
||||||
|
|
||||||
|
api/
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
FROM amazoncorretto:21 AS build
|
||||||
|
|
||||||
|
ARG ARTEFACT_VERSION='1.0.0'
|
||||||
|
WORKDIR /home/app
|
||||||
|
|
||||||
|
COPY gradlew .
|
||||||
|
COPY gradle gradle
|
||||||
|
COPY build.gradle.kts .
|
||||||
|
COPY settings.gradle.kts .
|
||||||
|
COPY gradle/libs.versions.toml gradle/libs.versions.toml
|
||||||
|
|
||||||
|
RUN chmod +x gradlew && ./gradlew dependencies --no-daemon
|
||||||
|
|
||||||
|
COPY src src
|
||||||
|
RUN ./gradlew bootJar --no-daemon -PartefactVersion=${ARTEFACT_VERSION}
|
||||||
|
|
||||||
|
FROM amazoncorretto:21-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=build /home/app/build/libs/helix-server-${ARTEFACT_VERSION}.jar app.jar
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
|
||||||
|
|
||||||
|
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
|
||||||
+10
-6
@@ -1,7 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
java
|
||||||
id("org.springframework.boot") version "3.5.4"
|
alias(libs.plugins.spring.boot)
|
||||||
id("io.spring.dependency-management") version "1.1.7"
|
alias(libs.plugins.spring.dependency.management)
|
||||||
}
|
}
|
||||||
|
|
||||||
val artefactVersion: String by project
|
val artefactVersion: String by project
|
||||||
@@ -14,10 +14,10 @@ tasks.withType<JavaCompile> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_21
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_21
|
||||||
toolchain {
|
toolchain {
|
||||||
languageVersion = JavaLanguageVersion.of(17)
|
languageVersion = JavaLanguageVersion.of(21)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,11 +32,13 @@ dependencies {
|
|||||||
implementation(platform(libs.onixbyte.versionCatalogue))
|
implementation(platform(libs.onixbyte.versionCatalogue))
|
||||||
implementation(libs.onixbyte.tuple)
|
implementation(libs.onixbyte.tuple)
|
||||||
implementation(libs.onixbyte.commonToolbox)
|
implementation(libs.onixbyte.commonToolbox)
|
||||||
|
implementation(libs.onixbyte.mathToolbox)
|
||||||
implementation(libs.onixbyte.identityGenerator)
|
implementation(libs.onixbyte.identityGenerator)
|
||||||
implementation(libs.onixbyte.captcha)
|
implementation(libs.onixbyte.captcha)
|
||||||
implementation(libs.onixbyte.regions)
|
implementation(libs.onixbyte.regions)
|
||||||
implementation(libs.jwt.core)
|
implementation(libs.jwt.core)
|
||||||
implementation(libs.spring.boot.configurationProcessor)
|
implementation(libs.spring.boot.configurationProcessor)
|
||||||
|
implementation(libs.spring.boot.actuator)
|
||||||
implementation(libs.spring.boot.starter.web)
|
implementation(libs.spring.boot.starter.web)
|
||||||
implementation(libs.spring.boot.starter.webFlux)
|
implementation(libs.spring.boot.starter.webFlux)
|
||||||
implementation(libs.spring.boot.starter.validation)
|
implementation(libs.spring.boot.starter.validation)
|
||||||
@@ -45,6 +47,8 @@ dependencies {
|
|||||||
implementation(libs.spring.boot.starter.security)
|
implementation(libs.spring.boot.starter.security)
|
||||||
implementation(libs.spring.boot.starter.jpa)
|
implementation(libs.spring.boot.starter.jpa)
|
||||||
implementation(libs.mybatis.starter.core)
|
implementation(libs.mybatis.starter.core)
|
||||||
|
implementation(libs.flyway.core)
|
||||||
|
implementation(libs.flyway.postgresql)
|
||||||
implementation(libs.jackson.jsr310)
|
implementation(libs.jackson.jsr310)
|
||||||
testImplementation(libs.spring.boot.starter.test)
|
testImplementation(libs.spring.boot.starter.test)
|
||||||
testImplementation(libs.reactor.test)
|
testImplementation(libs.reactor.test)
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ app:
|
|||||||
# 是否开启 S3 文件存储服务
|
# 是否开启 S3 文件存储服务
|
||||||
enabled: true
|
enabled: true
|
||||||
# S3 服务端点(若使用非 AWS 提供的 S3 兼容 API,请添加该配置)
|
# S3 服务端点(若使用非 AWS 提供的 S3 兼容 API,请添加该配置)
|
||||||
# endpoint: https://endpoint.s3.service
|
# endpoint: https://endpoint.s3.service
|
||||||
# S3 服务区域(详情请见 S3 服务提供商)
|
# S3 服务区域(详情请见 S3 服务提供商)
|
||||||
# region: apac
|
# region: apac
|
||||||
# 公开域名
|
# 公开域名
|
||||||
public-host: https://s3.my.app
|
public-host: https://s3.my.app
|
||||||
# 是否开启 Path Style
|
# 是否开启 Path Style
|
||||||
@@ -64,8 +64,8 @@ app:
|
|||||||
# 允许的请求头列表
|
# 允许的请求头列表
|
||||||
allowed-headers: Content-Type
|
allowed-headers: Content-Type
|
||||||
# 允许的请求方法列表(Ref org.springframework.http.HttpMethod)
|
# 允许的请求方法列表(Ref org.springframework.http.HttpMethod)
|
||||||
# 2025.11.6注
|
# Spring HttpMethod 并非 enum class,因此在此处使用小写请求方式会导致在请求头中存在 Origin 时出现 Invalid
|
||||||
# 由于 Spring 解析问题,在此处使用小写的情况下会导致在请求头中存在 Origin 时出现 Invalid CORS Request 的问题,请务必使用大写
|
# CORS Request 的问题,请务必使用大写
|
||||||
allowed-methods:
|
allowed-methods:
|
||||||
- GET
|
- GET
|
||||||
- POST
|
- POST
|
||||||
|
|||||||
@@ -1,324 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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());
|
|
||||||
+46
-27
@@ -1,13 +1,13 @@
|
|||||||
[versions]
|
[versions]
|
||||||
jspecifyVersion = "1.0.0"
|
jspecifyVersion = "1.0.0"
|
||||||
javaJwtVersion = "4.5.0"
|
javaJwtVersion = "4.5.1"
|
||||||
postgresDriverVersion = "42.7.7"
|
postgresDriverVersion = "42.7.9"
|
||||||
h2Version = "2.2.224"
|
h2Version = "2.2.224"
|
||||||
springSecurityVersion = "6.5.2"
|
|
||||||
springBootVersion = "3.5.4"
|
springBootVersion = "3.5.4"
|
||||||
|
springSecurityVersion = "6.5.2"
|
||||||
reactorVersion = "3.7.8"
|
reactorVersion = "3.7.8"
|
||||||
junitPlatformVersion = "1.12.2"
|
junitPlatformVersion = "1.12.2"
|
||||||
onixbyteVersion = "3.2.0"
|
onixbyteVersion = "3.3.0"
|
||||||
onixbyteCaptcha = "1.1.0"
|
onixbyteCaptcha = "1.1.0"
|
||||||
onixbyteRegions = "2025.12.0"
|
onixbyteRegions = "2025.12.0"
|
||||||
awsSdkVersion = "2.25.48"
|
awsSdkVersion = "2.25.48"
|
||||||
@@ -17,39 +17,58 @@ commonsLangVersion = "3.20.0"
|
|||||||
mybatisVersion = "3.0.5"
|
mybatisVersion = "3.0.5"
|
||||||
jacksonVersion = "2.19.2"
|
jacksonVersion = "2.19.2"
|
||||||
hypersistenceVersion = "3.14.0"
|
hypersistenceVersion = "3.14.0"
|
||||||
|
springDependencyManagementVersion = "1.1.7"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
jwt-core = { group = "com.auth0", name = "java-jwt", version.ref = "javaJwtVersion" }
|
# General Utilities
|
||||||
|
jspecify-core = { group = "org.jspecify", name = "jspecify", version.ref = "jspecifyVersion" }
|
||||||
|
commons-io = { group = "commons-io", name = "commons-io", version.ref = "commonsIoVersion" }
|
||||||
|
commons-collections = { group = "org.apache.commons", name = "commons-collections4", version.ref = "commonsCollections" }
|
||||||
|
commons-lang = { group = "org.apache.commons", name = "commons-lang3", version.ref = "commonsLangVersion" }
|
||||||
|
jackson-jsr310 = { group = "com.fasterxml.jackson.datatype", name = "jackson-datatype-jsr310", version.ref = "jacksonVersion" }
|
||||||
|
|
||||||
|
# Onixbyte Ecosystem
|
||||||
|
onixbyte-versionCatalogue = { group = "com.onixbyte", name = "version-catalogue", version.ref = "onixbyteVersion" }
|
||||||
|
onixbyte-tuple = { group = "com.onixbyte", name = "tuple" }
|
||||||
|
onixbyte-commonToolbox = { group = "com.onixbyte", name = "common-toolbox" }
|
||||||
|
onixbyte-mathToolbox = { group = "com.onixbyte", name = "math-toolbox" }
|
||||||
|
onixbyte-identityGenerator = { group = "com.onixbyte", name = "identity-generator" }
|
||||||
|
onixbyte-captcha = { group = "com.onixbyte", name = "captcha", version.ref = "onixbyteCaptcha" }
|
||||||
|
onixbyte-regions = { group = "com.onixbyte", name = "regions4j", version.ref = "onixbyteRegions" }
|
||||||
|
|
||||||
|
# Persistence & Database
|
||||||
|
mybatis-starter-core = { group = "org.mybatis.spring.boot", name = "mybatis-spring-boot-starter", version.ref = "mybatisVersion" }
|
||||||
|
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" }
|
||||||
postgres-driver = { group = "org.postgresql", name = "postgresql", version.ref = "postgresDriverVersion" }
|
postgres-driver = { group = "org.postgresql", name = "postgresql", version.ref = "postgresDriverVersion" }
|
||||||
h2-database = { group = "com.h2database", name = "h2", version.ref = "h2Version" }
|
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-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" }
|
flyway-core = { group = "org.flywaydb", name = "flyway-core" }
|
||||||
|
flyway-postgresql = { group = "org.flywaydb", name = "flyway-database-postgresql" }
|
||||||
|
|
||||||
|
# Spring Boot Core & Web
|
||||||
|
spring-boot-starter-web = { group = "org.springframework.boot", name = "spring-boot-starter-web" }
|
||||||
|
spring-boot-starter-webFlux = { group = "org.springframework.boot", name = "spring-boot-starter-webflux" }
|
||||||
|
spring-boot-starter-validation = { group = "org.springframework.boot", name = "spring-boot-starter-validation" }
|
||||||
|
spring-boot-starter-cache = { group = "org.springframework.boot", name = "spring-boot-starter-cache" }
|
||||||
|
spring-boot-configurationProcessor = { group = "org.springframework.boot", name = "spring-boot-configuration-processor" }
|
||||||
|
spring-boot-actuator = { group = "org.springframework.boot", name = "spring-boot-starter-actuator" }
|
||||||
|
|
||||||
|
# Security & Auth
|
||||||
spring-boot-starter-security = { group = "org.springframework.boot", name = "spring-boot-starter-security", version.ref = "springBootVersion" }
|
spring-boot-starter-security = { group = "org.springframework.boot", name = "spring-boot-starter-security", version.ref = "springBootVersion" }
|
||||||
|
jwt-core = { group = "com.auth0", name = "java-jwt", version.ref = "javaJwtVersion" }
|
||||||
|
|
||||||
|
# Cloud Services
|
||||||
|
aws-sdk-bom = { group = "software.amazon.awssdk", name = "bom", version.ref = "awsSdkVersion" }
|
||||||
|
aws-sdk-s3 = { group = "software.amazon.awssdk", name = "s3" }
|
||||||
|
|
||||||
|
# Testing
|
||||||
spring-boot-starter-test = { group = "org.springframework.boot", name = "spring-boot-starter-test", 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" }
|
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" }
|
reactor-test = { group = "io.projectreactor", name = "reactor-test", version.ref = "reactorVersion" }
|
||||||
junit-launcher = { group = "org.junit.platform", name = "junit-platform-launcher", version.ref = "junitPlatformVersion" }
|
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" }
|
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]
|
[plugins]
|
||||||
|
spring-boot = { id = "org.springframework.boot", version.ref = "springBootVersion" }
|
||||||
|
spring-dependency-management = { id = "io.spring.dependency-management", version.ref = "springDependencyManagementVersion" }
|
||||||
|
|||||||
@@ -1,31 +1,20 @@
|
|||||||
package com.onixbyte.helix;
|
package com.onixbyte.helix;
|
||||||
|
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cache.annotation.EnableCaching;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Application entrance.
|
* Application entrance.
|
||||||
*
|
*
|
||||||
* @author zihluwang
|
* @author zihluwang
|
||||||
* @see SpringBootApplication
|
* @see SpringBootApplication
|
||||||
* @see EnableCaching
|
|
||||||
* @see SpringApplication
|
* @see SpringApplication
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@EnableCaching
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class HelixApplication {
|
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) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(HelixApplication.class, args);
|
SpringApplication.run(HelixApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package com.onixbyte.helix.client;
|
package com.onixbyte.helix.client;
|
||||||
|
|
||||||
import com.auth0.jwt.JWT;
|
import com.auth0.jwt.JWT;
|
||||||
|
import com.auth0.jwt.JWTVerifier;
|
||||||
import com.auth0.jwt.algorithms.Algorithm;
|
import com.auth0.jwt.algorithms.Algorithm;
|
||||||
|
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||||
import com.onixbyte.helix.domain.entity.User;
|
import com.onixbyte.helix.domain.entity.User;
|
||||||
import com.onixbyte.helix.properties.TokenProperties;
|
import com.onixbyte.helix.properties.TokenProperties;
|
||||||
import com.onixbyte.helix.utils.DateTimeUtil;
|
import com.onixbyte.helix.utils.DateTimeUtil;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -23,6 +26,7 @@ public class TokenClient {
|
|||||||
|
|
||||||
private final Algorithm algorithm;
|
private final Algorithm algorithm;
|
||||||
private final TokenProperties tokenProperties;
|
private final TokenProperties tokenProperties;
|
||||||
|
private final JWTVerifier verifier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new TokenClient with the necessary algorithm and token properties.
|
* Constructs a new TokenClient with the necessary algorithm and token properties.
|
||||||
@@ -31,9 +35,11 @@ public class TokenClient {
|
|||||||
* @param tokenProperties the configuration properties for the token, such as issuer and
|
* @param tokenProperties the configuration properties for the token, such as issuer and
|
||||||
* validity period
|
* validity period
|
||||||
*/
|
*/
|
||||||
public TokenClient(Algorithm algorithm, TokenProperties tokenProperties) {
|
@Autowired
|
||||||
|
public TokenClient(Algorithm algorithm, TokenProperties tokenProperties, JWTVerifier verifier) {
|
||||||
this.algorithm = algorithm;
|
this.algorithm = algorithm;
|
||||||
this.tokenProperties = tokenProperties;
|
this.tokenProperties = tokenProperties;
|
||||||
|
this.verifier = verifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,4 +60,17 @@ public class TokenClient {
|
|||||||
.withExpiresAt(DateTimeUtil.asInstant(expiresAt))
|
.withExpiresAt(DateTimeUtil.asInstant(expiresAt))
|
||||||
.sign(algorithm);
|
.sign(algorithm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify and decode token.
|
||||||
|
*
|
||||||
|
* @param token a JWT token
|
||||||
|
* @return information included in the given token
|
||||||
|
* @throws com.auth0.jwt.exceptions.JWTVerificationException if the token is invalid, such as
|
||||||
|
* expired, or not signed by
|
||||||
|
* specific server
|
||||||
|
*/
|
||||||
|
public DecodedJWT verifyToken(String token) {
|
||||||
|
return verifier.verify(token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.onixbyte.helix.config;
|
package com.onixbyte.helix.config;
|
||||||
|
|
||||||
import com.onixbyte.helix.properties.ApplicationProperties;
|
import com.onixbyte.helix.properties.ApplicationProperties;
|
||||||
|
import com.onixbyte.helix.properties.AuthenticationProperties;
|
||||||
import com.onixbyte.helix.properties.MsalProperties;
|
import com.onixbyte.helix.properties.MsalProperties;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -18,11 +19,15 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
* application context.
|
* application context.
|
||||||
*
|
*
|
||||||
* @author zihluwang
|
* @author zihluwang
|
||||||
* @since 1.0.0
|
|
||||||
* @see MsalProperties
|
* @see MsalProperties
|
||||||
* @see EnableConfigurationProperties
|
* @see EnableConfigurationProperties
|
||||||
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableConfigurationProperties({MsalProperties.class, ApplicationProperties.class})
|
@EnableConfigurationProperties({
|
||||||
|
MsalProperties.class,
|
||||||
|
AuthenticationProperties.class,
|
||||||
|
ApplicationProperties.class
|
||||||
|
})
|
||||||
public class AuthenticationConfig {
|
public class AuthenticationConfig {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.onixbyte.helix.config;
|
package com.onixbyte.helix.config;
|
||||||
|
|
||||||
import com.onixbyte.helix.extension.redis.serializer.JacksonSerialiser;
|
import com.onixbyte.helix.extension.redis.serializer.JacksonSerialiser;
|
||||||
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||||
@@ -34,6 +35,7 @@ import java.time.Duration;
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@EnableCaching
|
||||||
public class CacheConfig {
|
public class CacheConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.onixbyte.helix.config;
|
package com.onixbyte.helix.config;
|
||||||
|
|
||||||
import com.onixbyte.identitygenerator.IdentityGenerator;
|
import com.onixbyte.identitygenerator.IdentityGenerator;
|
||||||
import com.onixbyte.identitygenerator.impl.SequentialUuidGenerator;
|
|
||||||
import com.onixbyte.identitygenerator.impl.SnowflakeIdentityGenerator;
|
import com.onixbyte.identitygenerator.impl.SnowflakeIdentityGenerator;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -28,24 +27,6 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class GuidConfig {
|
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
|
@Bean
|
||||||
public IdentityGenerator<Long> userIdentityGenerator() {
|
public IdentityGenerator<Long> userIdentityGenerator() {
|
||||||
return new SnowflakeIdentityGenerator(0x0, 0x0);
|
return new SnowflakeIdentityGenerator(0x0, 0x0);
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024-2026 OnixByte
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.onixbyte.helix.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.LocaleResolver;
|
||||||
|
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class I18nConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public LocaleResolver localeResolver() {
|
||||||
|
var slr = new AcceptHeaderLocaleResolver();
|
||||||
|
slr.setDefaultLocale(Locale.UK);
|
||||||
|
return slr;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.onixbyte.helix.config;
|
package com.onixbyte.helix.config;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
|
||||||
import com.onixbyte.helix.common.jackson.JacksonModules;
|
import com.onixbyte.helix.common.jackson.JacksonModules;
|
||||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.onixbyte.helix.config;
|
package com.onixbyte.helix.config;
|
||||||
|
|
||||||
|
import com.auth0.jwt.JWT;
|
||||||
|
import com.auth0.jwt.JWTVerifier;
|
||||||
import com.auth0.jwt.algorithms.Algorithm;
|
import com.auth0.jwt.algorithms.Algorithm;
|
||||||
import com.onixbyte.helix.filter.TokenAuthenticationFilter;
|
import com.onixbyte.helix.filter.TokenAuthenticationFilter;
|
||||||
import com.onixbyte.helix.properties.CorsProperties;
|
import com.onixbyte.helix.properties.CorsProperties;
|
||||||
@@ -206,4 +208,11 @@ public class SecurityConfig {
|
|||||||
public Algorithm algorithm(TokenProperties properties) {
|
public Algorithm algorithm(TokenProperties properties) {
|
||||||
return Algorithm.HMAC256(properties.secret());
|
return Algorithm.HMAC256(properties.secret());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JWTVerifier verifier(Algorithm algorithm, TokenProperties tokenProperties) {
|
||||||
|
return JWT.require(algorithm)
|
||||||
|
.withIssuer(tokenProperties.issuer())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024-2026 OnixByte
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.onixbyte.helix.config;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.MessageSource;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class ValidationConfig {
|
||||||
|
|
||||||
|
private final MessageSource messageSource;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public ValidationConfig(MessageSource messageSource) {
|
||||||
|
this.messageSource = messageSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public LocalValidatorFactoryBean getValidator() {
|
||||||
|
var factoryBean = new LocalValidatorFactoryBean();
|
||||||
|
factoryBean.setValidationMessageSource(messageSource);
|
||||||
|
return factoryBean;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
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 {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,23 @@
|
|||||||
package com.onixbyte.helix.controller;
|
package com.onixbyte.helix.controller;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.AssetPrefix;
|
import com.onixbyte.helix.shared.AssetPrefix;
|
||||||
import com.onixbyte.helix.domain.web.response.FileUploadResponse;
|
import com.onixbyte.helix.domain.web.response.FileUploadResponse;
|
||||||
import com.onixbyte.helix.exception.BizException;
|
import com.onixbyte.helix.exception.BizException;
|
||||||
import com.onixbyte.helix.service.AssetService;
|
import com.onixbyte.helix.service.AssetService;
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* REST controller for file storage operations. Provides endpoints for uploading, downloading, and
|
* This controller provides entry points that manipulates assets.
|
||||||
* deleting assets using the configured storage service.
|
|
||||||
*
|
*
|
||||||
* @author zihluwang
|
* @author zihluwang
|
||||||
* @since 1.0.0
|
* @author siujamo
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/assets")
|
@RequestMapping("/assets")
|
||||||
@@ -26,11 +27,7 @@ public class AssetController {
|
|||||||
|
|
||||||
private final AssetService assetService;
|
private final AssetService assetService;
|
||||||
|
|
||||||
/**
|
@Autowired
|
||||||
* Constructs a new FileController with the specified file service.
|
|
||||||
*
|
|
||||||
* @param assetService the file service to use for file operations
|
|
||||||
*/
|
|
||||||
public AssetController(AssetService assetService) {
|
public AssetController(AssetService assetService) {
|
||||||
this.assetService = assetService;
|
this.assetService = assetService;
|
||||||
}
|
}
|
||||||
@@ -47,7 +44,7 @@ public class AssetController {
|
|||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
if (file.isEmpty()) {
|
if (file.isEmpty()) {
|
||||||
throw new BizException(HttpStatus.BAD_REQUEST, "File cannot be empty.");
|
throw new BizException(HttpStatus.BAD_REQUEST, MessageName.ASSET_NOT_EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileUrl = assetService.uploadFile(AssetPrefix.UPLOADS, file);
|
var fileUrl = assetService.uploadFile(AssetPrefix.UPLOADS, file);
|
||||||
@@ -60,10 +57,13 @@ public class AssetController {
|
|||||||
file.getSize(),
|
file.getSize(),
|
||||||
fileUrl
|
fileUrl
|
||||||
));
|
));
|
||||||
|
} catch (BizException ex) {
|
||||||
|
throw ex;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("File upload failed: {}", e.getMessage(), e);
|
log.error("File upload failed: {}", e.getMessage(), e);
|
||||||
throw new BizException(HttpStatus.INTERNAL_SERVER_ERROR,
|
throw new BizException(HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
"Failed upload file: " + e.getMessage());
|
MessageName.ASSET_UPLOAD_FAILED,
|
||||||
|
e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,43 @@
|
|||||||
package com.onixbyte.helix.controller;
|
package com.onixbyte.helix.controller;
|
||||||
|
|
||||||
import com.onixbyte.helix.domain.web.request.UsernamePasswordLoginRequest;
|
import com.onixbyte.helix.domain.web.request.LoginRequest;
|
||||||
import com.onixbyte.helix.domain.web.response.LoginSuccessResponse;
|
import com.onixbyte.helix.domain.web.response.UserDetailResponse;
|
||||||
import com.onixbyte.helix.service.AuthService;
|
import com.onixbyte.helix.service.AuthService;
|
||||||
import org.slf4j.Logger;
|
import com.onixbyte.helix.service.TokenService;
|
||||||
import org.slf4j.LoggerFactory;
|
import com.onixbyte.helix.service.UserService;
|
||||||
|
import com.onixbyte.helix.shared.TokenConstant;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This controller provides entry points making user authorised.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
* @author siujamo
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/auth")
|
@RequestMapping("/auth")
|
||||||
public class AuthController {
|
public class AuthController {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(AuthController.class);
|
|
||||||
private final AuthService authService;
|
private final AuthService authService;
|
||||||
|
private final TokenService tokenService;
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
public AuthController(AuthService authService) {
|
@Autowired
|
||||||
|
public AuthController(
|
||||||
|
AuthService authService,
|
||||||
|
TokenService tokenService,
|
||||||
|
UserService userService
|
||||||
|
) {
|
||||||
this.authService = authService;
|
this.authService = authService;
|
||||||
|
this.tokenService = tokenService;
|
||||||
|
this.userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,14 +47,39 @@ public class AuthController {
|
|||||||
* @return detailed user info and authentication token
|
* @return detailed user info and authentication token
|
||||||
*/
|
*/
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
public LoginSuccessResponse loginWithUsernameAndPassword(
|
public ResponseEntity<UserDetailResponse> loginWithUsernameAndPassword(
|
||||||
@Validated @RequestBody UsernamePasswordLoginRequest request
|
@Validated @RequestBody LoginRequest request
|
||||||
) {
|
) {
|
||||||
return authService.login(request);
|
var user = authService.login(request);
|
||||||
|
var token = tokenService.generateToken(user);
|
||||||
|
|
||||||
|
var cookie = authService.buildCookie(TokenConstant.TOKEN_NAME, token);
|
||||||
|
|
||||||
|
return ResponseEntity.status(HttpStatus.OK)
|
||||||
|
.header(HttpHeaders.SET_COOKIE, cookie.toString())
|
||||||
|
.body(userService.getDetail(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether the registration function is enabled.
|
||||||
|
*
|
||||||
|
* @return {@code true} if registration function is enabled, otherwise {@code false}
|
||||||
|
*/
|
||||||
@GetMapping("/register-enabled")
|
@GetMapping("/register-enabled")
|
||||||
public boolean getRegisterEnabled() {
|
public boolean getRegisterEnabled() {
|
||||||
return authService.getRegisterEnabled();
|
return authService.getRegisterEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform log out.
|
||||||
|
*
|
||||||
|
* @return a response that remove the authentication from cookie
|
||||||
|
*/
|
||||||
|
@GetMapping("/logout")
|
||||||
|
public ResponseEntity<Void> logout() {
|
||||||
|
var cookie = authService.buildCookie(TokenConstant.TOKEN_NAME, "", Duration.ZERO);
|
||||||
|
return ResponseEntity.status(HttpStatus.OK)
|
||||||
|
.header(HttpHeaders.SET_COOKIE, cookie.toString())
|
||||||
|
.body(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package com.onixbyte.helix.controller;
|
||||||
|
|
||||||
|
import com.onixbyte.helix.domain.entity.Authority;
|
||||||
|
import com.onixbyte.helix.domain.web.request.AuthorityRequest;
|
||||||
|
import com.onixbyte.helix.domain.web.request.QueryAuthorityRequest;
|
||||||
|
import com.onixbyte.helix.domain.web.response.ActionResponse;
|
||||||
|
import com.onixbyte.helix.service.AuthorityService;
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
|
import com.onixbyte.helix.utils.MessageUtil;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This controller provides entry points for manipulate authorities.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
* @author siujamo
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/authorities")
|
||||||
|
public class AuthorityController {
|
||||||
|
|
||||||
|
private final AuthorityService authorityService;
|
||||||
|
private final MessageUtil messageUtil;
|
||||||
|
|
||||||
|
public AuthorityController(
|
||||||
|
AuthorityService authorityService,
|
||||||
|
MessageUtil messageUtil
|
||||||
|
) {
|
||||||
|
this.authorityService = authorityService;
|
||||||
|
this.messageUtil = messageUtil;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get authorities by page.
|
||||||
|
*
|
||||||
|
* @param pageNum current page num
|
||||||
|
* @param pageSize page size
|
||||||
|
* @param request query parameters
|
||||||
|
* @return a page contains authority data of the specified page
|
||||||
|
*/
|
||||||
|
@GetMapping
|
||||||
|
public Page<Authority> getAuthorities(
|
||||||
|
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||||
|
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
||||||
|
@Validated @ModelAttribute QueryAuthorityRequest request
|
||||||
|
) {
|
||||||
|
var pageRequest = PageRequest.of(pageNum - 1, pageSize);
|
||||||
|
return authorityService.getAuthorities(pageRequest, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an authority.
|
||||||
|
*
|
||||||
|
* @param request authority specs
|
||||||
|
* @return created authority
|
||||||
|
*/
|
||||||
|
@PostMapping
|
||||||
|
public Authority addAuthority(@Validated @RequestBody AuthorityRequest request) {
|
||||||
|
return authorityService.addAuthority(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit an authority.
|
||||||
|
*
|
||||||
|
* @param request authority specs
|
||||||
|
* @return edited authority
|
||||||
|
*/
|
||||||
|
@PutMapping("/{id:\\d+}")
|
||||||
|
public Authority editAuthority(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@Validated @RequestBody AuthorityRequest request
|
||||||
|
) {
|
||||||
|
return authorityService.editAuthority(id, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{authorityId:\\d+}")
|
||||||
|
public ActionResponse deleteAuthority(@PathVariable Long authorityId) {
|
||||||
|
var name = authorityService.deleteAuthority(authorityId);
|
||||||
|
return ActionResponse.success(messageUtil.getMessage(MessageName.AUTHORITY_DELETED, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.onixbyte.helix.controller;
|
|||||||
|
|
||||||
import com.onixbyte.helix.domain.web.response.CaptchaResponse;
|
import com.onixbyte.helix.domain.web.response.CaptchaResponse;
|
||||||
import com.onixbyte.helix.service.CaptchaService;
|
import com.onixbyte.helix.service.CaptchaService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -9,16 +10,28 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This controller provides entry points to get captcha images.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
* @author siujamo
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/captcha")
|
@RequestMapping("/captcha")
|
||||||
public class CaptchaController {
|
public class CaptchaController {
|
||||||
|
|
||||||
private final CaptchaService captchaService;
|
private final CaptchaService captchaService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
public CaptchaController(CaptchaService captchaService) {
|
public CaptchaController(CaptchaService captchaService) {
|
||||||
this.captchaService = captchaService;
|
this.captchaService = captchaService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get captcha image and captcha uuid.
|
||||||
|
*
|
||||||
|
* @return captcha response, contains the uuid of the captcha and image BASE64
|
||||||
|
*/
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<CaptchaResponse> getCaptcha() {
|
public ResponseEntity<CaptchaResponse> getCaptcha() {
|
||||||
var captchaTuple = captchaService.buildCaptcha();
|
var captchaTuple = captchaService.buildCaptcha();
|
||||||
|
|||||||
@@ -1,20 +1,28 @@
|
|||||||
package com.onixbyte.helix.controller;
|
package com.onixbyte.helix.controller;
|
||||||
|
|
||||||
import com.onixbyte.helix.domain.entity.Department;
|
import com.onixbyte.helix.domain.entity.Department;
|
||||||
import com.onixbyte.helix.domain.model.TreeNode;
|
import com.onixbyte.helix.domain.common.TreeNode;
|
||||||
|
import com.onixbyte.helix.domain.web.request.DepartmentRequest;
|
||||||
import com.onixbyte.helix.service.DepartmentService;
|
import com.onixbyte.helix.service.DepartmentService;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This controller provides entry points that manipulates departments.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
* @author siujamo
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/departments")
|
@RequestMapping("/departments")
|
||||||
public class DepartmentController {
|
public class DepartmentController {
|
||||||
|
|
||||||
private final DepartmentService departmentService;
|
private final DepartmentService departmentService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
public DepartmentController(DepartmentService departmentService) {
|
public DepartmentController(DepartmentService departmentService) {
|
||||||
this.departmentService = departmentService;
|
this.departmentService = departmentService;
|
||||||
}
|
}
|
||||||
@@ -28,4 +36,17 @@ public class DepartmentController {
|
|||||||
public List<Department> getDepartments() {
|
public List<Department> getDepartments() {
|
||||||
return departmentService.getDepartments();
|
return departmentService.getDepartments();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public Department addDepartment(@Validated @RequestBody DepartmentRequest request) {
|
||||||
|
return departmentService.addDepartment(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id:\\d+}")
|
||||||
|
public Department editDepartment(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@Validated @RequestBody DepartmentRequest request
|
||||||
|
) {
|
||||||
|
return departmentService.editDepartment(id, request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ package com.onixbyte.helix.controller;
|
|||||||
|
|
||||||
import com.onixbyte.helix.domain.web.response.BizExceptionResponse;
|
import com.onixbyte.helix.domain.web.response.BizExceptionResponse;
|
||||||
import com.onixbyte.helix.exception.BizException;
|
import com.onixbyte.helix.exception.BizException;
|
||||||
|
import com.onixbyte.helix.utils.MessageUtil;
|
||||||
import jakarta.validation.ConstraintViolationException;
|
import jakarta.validation.ConstraintViolationException;
|
||||||
|
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
@@ -13,22 +16,10 @@ import java.time.LocalDateTime;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global exception handler for the Helix application.
|
* This controller advise will catch some business exception which produced when performing actions.
|
||||||
* <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
|
* @author zihluwang
|
||||||
|
* @author siujamo
|
||||||
* @see BizException
|
* @see BizException
|
||||||
* @see BizExceptionResponse
|
* @see BizExceptionResponse
|
||||||
* @see RestControllerAdvice
|
* @see RestControllerAdvice
|
||||||
@@ -37,51 +28,25 @@ import java.util.stream.Collectors;
|
|||||||
@RestControllerAdvice
|
@RestControllerAdvice
|
||||||
public class ExceptionController {
|
public class ExceptionController {
|
||||||
|
|
||||||
|
private final MessageUtil messageUtil;
|
||||||
|
|
||||||
|
public ExceptionController(MessageUtil messageUtil) {
|
||||||
|
this.messageUtil = messageUtil;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
@ExceptionHandler(BizException.class)
|
||||||
public ResponseEntity<BizExceptionResponse> handleBizException(BizException ex) {
|
public ResponseEntity<BizExceptionResponse> handleBizException(BizException ex) {
|
||||||
|
var message = ex.getMessageCode() == null
|
||||||
|
? ex.getMessage()
|
||||||
|
: messageUtil.getMessage(ex.getMessageCode(), ex.getMessageArgs());
|
||||||
|
|
||||||
return ResponseEntity.status(ex.getStatus())
|
return ResponseEntity.status(ex.getStatus())
|
||||||
.body(new BizExceptionResponse(
|
.body(new BizExceptionResponse(
|
||||||
LocalDateTime.now(),
|
LocalDateTime.now(),
|
||||||
ex.getMessage())
|
message)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
@ExceptionHandler(ConstraintViolationException.class)
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
public BizExceptionResponse handleConstraintViolation(ConstraintViolationException ex) {
|
public BizExceptionResponse handleConstraintViolation(ConstraintViolationException ex) {
|
||||||
@@ -94,4 +59,19 @@ public class ExceptionController {
|
|||||||
errorMessage
|
errorMessage
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
|
public BizExceptionResponse handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
|
||||||
|
var errorMessage = ex.getBindingResult()
|
||||||
|
.getFieldErrors()
|
||||||
|
.stream()
|
||||||
|
.map(DefaultMessageSourceResolvable::getDefaultMessage)
|
||||||
|
.collect(Collectors.joining("; "));
|
||||||
|
|
||||||
|
return new BizExceptionResponse(
|
||||||
|
LocalDateTime.now(),
|
||||||
|
errorMessage
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,37 @@
|
|||||||
package com.onixbyte.helix.controller;
|
package com.onixbyte.helix.controller;
|
||||||
|
|
||||||
import com.onixbyte.helix.domain.entity.Menu;
|
import com.onixbyte.helix.domain.entity.Menu;
|
||||||
import com.onixbyte.helix.domain.model.TreeNode;
|
import com.onixbyte.helix.domain.common.TreeNode;
|
||||||
import com.onixbyte.helix.service.MenuService;
|
import com.onixbyte.helix.service.MenuService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This controller provides entry points to manipulate menus.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
* @author siujamo
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/menus")
|
@RequestMapping("/menus")
|
||||||
public class MenuController {
|
public class MenuController {
|
||||||
|
|
||||||
private final MenuService menuService;
|
private final MenuService menuService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
public MenuController(MenuService menuService) {
|
public MenuController(MenuService menuService) {
|
||||||
this.menuService = menuService;
|
this.menuService = menuService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get menu tree.
|
||||||
|
*
|
||||||
|
* @return available menu tree for the current user
|
||||||
|
*/
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public List<TreeNode<Menu>> getMenuTree() {
|
public List<TreeNode<Menu>> getMenuTree() {
|
||||||
return menuService.getMenuTree();
|
return menuService.getMenuTree();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.onixbyte.helix.controller;
|
|||||||
|
|
||||||
import com.onixbyte.helix.domain.entity.Position;
|
import com.onixbyte.helix.domain.entity.Position;
|
||||||
import com.onixbyte.helix.service.PositionService;
|
import com.onixbyte.helix.service.PositionService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
@@ -10,16 +11,30 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This controller provides entry points to manipulate positions.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
* @author siujamo
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/positions")
|
@RequestMapping("/positions")
|
||||||
public class PositionController {
|
public class PositionController {
|
||||||
|
|
||||||
private final PositionService positionService;
|
private final PositionService positionService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
public PositionController(PositionService positionService) {
|
public PositionController(PositionService positionService) {
|
||||||
this.positionService = positionService;
|
this.positionService = positionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get position data paginated.
|
||||||
|
*
|
||||||
|
* @param pageNum current page num
|
||||||
|
* @param pageSize page size
|
||||||
|
* @return paginated position data
|
||||||
|
*/
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public Page<Position> getPositions(
|
public Page<Position> getPositions(
|
||||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||||
|
|||||||
@@ -2,21 +2,35 @@ package com.onixbyte.helix.controller;
|
|||||||
|
|
||||||
import com.onixbyte.helix.domain.entity.Role;
|
import com.onixbyte.helix.domain.entity.Role;
|
||||||
import com.onixbyte.helix.domain.web.request.QueryRoleRequest;
|
import com.onixbyte.helix.domain.web.request.QueryRoleRequest;
|
||||||
|
import com.onixbyte.helix.domain.web.request.RoleRequest;
|
||||||
|
import com.onixbyte.helix.domain.web.response.ActionResponse;
|
||||||
import com.onixbyte.helix.service.RoleService;
|
import com.onixbyte.helix.service.RoleService;
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
|
import com.onixbyte.helix.utils.MessageUtil;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This controller provides entry points to manipulate roles.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
* @author siujamo
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/roles")
|
@RequestMapping("/roles")
|
||||||
public class RoleController {
|
public class RoleController {
|
||||||
|
|
||||||
private final RoleService roleService;
|
private final RoleService roleService;
|
||||||
|
private final MessageUtil messageUtil;
|
||||||
|
|
||||||
public RoleController(RoleService roleService) {
|
@Autowired
|
||||||
|
public RoleController(RoleService roleService, MessageUtil messageUtil) {
|
||||||
this.roleService = roleService;
|
this.roleService = roleService;
|
||||||
|
this.messageUtil = messageUtil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@@ -28,4 +42,23 @@ public class RoleController {
|
|||||||
var pageRequest = PageRequest.of(pageNum - 1, pageSize, Sort.by(Sort.Order.asc("id")));
|
var pageRequest = PageRequest.of(pageNum - 1, pageSize, Sort.by(Sort.Order.asc("id")));
|
||||||
return roleService.getRoles(pageRequest, request);
|
return roleService.getRoles(pageRequest, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public Role addRole(@Validated @RequestBody RoleRequest request) {
|
||||||
|
return roleService.addRole(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id:\\d+}")
|
||||||
|
public Role editRole(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@Validated @RequestBody RoleRequest request
|
||||||
|
) {
|
||||||
|
return roleService.editRole(id, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id:\\d+}")
|
||||||
|
public ActionResponse deleteRole(@PathVariable Long id) {
|
||||||
|
var name = roleService.deleteRole(id);
|
||||||
|
return ActionResponse.success(messageUtil.getMessage(MessageName.ROLE_DELETED, name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package com.onixbyte.helix.controller;
|
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.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This controller provides entry points to manipulate settings.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
* @author siujamo
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping
|
@RequestMapping
|
||||||
public class SettingController {
|
public class SettingController {
|
||||||
|
|||||||
@@ -1,28 +1,39 @@
|
|||||||
package com.onixbyte.helix.controller;
|
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.AddUserRequest;
|
||||||
|
import com.onixbyte.helix.domain.web.request.EditUserRequest;
|
||||||
import com.onixbyte.helix.domain.web.request.QueryUserRequest;
|
import com.onixbyte.helix.domain.web.request.QueryUserRequest;
|
||||||
import com.onixbyte.helix.domain.web.request.ResetPasswordRequest;
|
import com.onixbyte.helix.domain.web.request.ResetPasswordRequest;
|
||||||
import com.onixbyte.helix.domain.web.request.UpdateUserRequest;
|
import com.onixbyte.helix.domain.web.response.ActionResponse;
|
||||||
import com.onixbyte.helix.domain.web.response.UserDetailResponse;
|
import com.onixbyte.helix.domain.web.response.UserDetailResponse;
|
||||||
import com.onixbyte.helix.service.UserService;
|
import com.onixbyte.helix.service.UserService;
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
|
import com.onixbyte.helix.utils.MessageUtil;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This controller provides entry points to manipulate users.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
* @author siujamo
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/users")
|
@RequestMapping("/users")
|
||||||
public class UserController {
|
public class UserController {
|
||||||
|
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
private final MessageUtil messageUtil;
|
||||||
|
|
||||||
public UserController(UserService userService) {
|
@Autowired
|
||||||
|
public UserController(UserService userService, MessageUtil messageUtil) {
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
|
this.messageUtil = messageUtil;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,30 +66,59 @@ public class UserController {
|
|||||||
return userService.getUserDetailByUserId(userId);
|
return userService.getUserDetailByUserId(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new user.
|
||||||
|
*
|
||||||
|
* @param request user to be added
|
||||||
|
* @return added user
|
||||||
|
*/
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@PreAuthorize("hasAnyAuthority('system:user:write')")
|
@PreAuthorize("hasAnyAuthority('system:user:write')")
|
||||||
public ResponseEntity<Void> addUser(@Validated @RequestBody AddUserRequest request) {
|
public UserDetailResponse addUser(@Validated @RequestBody AddUserRequest request) {
|
||||||
userService.addUser(request);
|
return userService.addUser(request);
|
||||||
return ResponseEntity.ok(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping
|
/**
|
||||||
public ResponseEntity<Void> editUser(@Validated @RequestBody UpdateUserRequest request) {
|
* Edit a user.
|
||||||
userService.updateUser(request);
|
*
|
||||||
return ResponseEntity.ok(null);
|
* @param id user ID
|
||||||
|
* @param request user to be edited
|
||||||
|
* @return edited user
|
||||||
|
*/
|
||||||
|
@PutMapping("/{id:\\d+}")
|
||||||
|
public UserDetailResponse editUser(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@Validated @RequestBody EditUserRequest request
|
||||||
|
) {
|
||||||
|
return userService.updateUser(id, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset user's password.
|
||||||
|
*
|
||||||
|
* @param request reset password request, contains ID of the user and new password
|
||||||
|
* @return action response
|
||||||
|
*/
|
||||||
@PreAuthorize("hasAnyAuthority('system:user:reset-password')")
|
@PreAuthorize("hasAnyAuthority('system:user:reset-password')")
|
||||||
@PatchMapping("/reset-password")
|
@PatchMapping("/reset-password/{id:\\d+}")
|
||||||
public ResponseEntity<Void> resetPassword(@Validated @RequestBody ResetPasswordRequest request) {
|
public ActionResponse resetPassword(
|
||||||
userService.resetPassword(request);
|
@PathVariable Long id,
|
||||||
return ResponseEntity.ok(null);
|
@Validated @RequestBody ResetPasswordRequest request
|
||||||
|
) {
|
||||||
|
userService.resetPassword(id, request);
|
||||||
|
return ActionResponse.success(messageUtil.getMessage(MessageName.USER_PASSWORD_RESET_SUCCESS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a user.
|
||||||
|
*
|
||||||
|
* @param userId ID of the user to be deleted
|
||||||
|
* @return action response
|
||||||
|
*/
|
||||||
@PreAuthorize("hasAnyAuthority('system:user:write')")
|
@PreAuthorize("hasAnyAuthority('system:user:write')")
|
||||||
@DeleteMapping("/{userId:\\d+}")
|
@DeleteMapping("/{userId:\\d+}")
|
||||||
public ResponseEntity<Void> deleteUser(@PathVariable Long userId) {
|
public ActionResponse deleteUser(@PathVariable Long userId) {
|
||||||
userService.deleteUser(userId);
|
userService.deleteUser(userId);
|
||||||
return ResponseEntity.ok(null);
|
return ActionResponse.success(messageUtil.getMessage(MessageName.USER_DELETED, userId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.onixbyte.helix.domain.model;
|
package com.onixbyte.helix.domain.common;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package com.onixbyte.helix.domain.database.query.wrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper class for authority query criteria.
|
||||||
|
* <p>
|
||||||
|
* This class encapsulates query parameters used for filtering and searching authority entities.
|
||||||
|
* It is used in conjunction with repository methods to provide flexible querying capabilities.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
*/
|
||||||
|
public class QueryAuthorityWrapper {
|
||||||
|
}
|
||||||
+9
-3
@@ -1,7 +1,15 @@
|
|||||||
package com.onixbyte.helix.domain.database.query.wrapper;
|
package com.onixbyte.helix.domain.database.query.wrapper;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.Status;
|
import com.onixbyte.helix.enumeration.Status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper class for role query criteria.
|
||||||
|
* <p>
|
||||||
|
* This class encapsulates query parameters used for filtering and searching role entities. It is
|
||||||
|
* used in conjunction with mapper methods to provide flexible querying capabilities.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
*/
|
||||||
public class QueryRoleWrapper {
|
public class QueryRoleWrapper {
|
||||||
private String name;
|
private String name;
|
||||||
private String code;
|
private String code;
|
||||||
@@ -39,6 +47,4 @@ public class QueryRoleWrapper {
|
|||||||
public void setStatus(Status status) {
|
public void setStatus(Status status) {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-1
@@ -1,9 +1,17 @@
|
|||||||
package com.onixbyte.helix.domain.database.query.wrapper;
|
package com.onixbyte.helix.domain.database.query.wrapper;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.UserStatus;
|
import com.onixbyte.helix.enumeration.UserStatus;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper class for user query criteria.
|
||||||
|
* <p>
|
||||||
|
* This class encapsulates query parameters used for filtering and searching user entities. It is
|
||||||
|
* used in conjunction with mapper methods to provide flexible querying capabilities.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
*/
|
||||||
public class QueryUserWrapper {
|
public class QueryUserWrapper {
|
||||||
private Long departmentId;
|
private Long departmentId;
|
||||||
private String username;
|
private String username;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import java.time.LocalDateTime;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "assets")
|
@Table(name = "asset")
|
||||||
public class Asset {
|
public class Asset {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package com.onixbyte.helix.domain.entity;
|
package com.onixbyte.helix.domain.entity;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.Status;
|
import com.onixbyte.helix.enumeration.Status;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import org.hibernate.annotations.JdbcType;
|
import org.hibernate.annotations.JdbcType;
|
||||||
import org.hibernate.annotations.Type;
|
|
||||||
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ import java.util.Objects;
|
|||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "authorities")
|
@Table(name = "authority")
|
||||||
public class Authority {
|
public class Authority {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -262,4 +262,16 @@ public class Authority {
|
|||||||
public GrantedAuthority asGrantedAuthority() {
|
public GrantedAuthority asGrantedAuthority() {
|
||||||
return this::getCode;
|
return this::getCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
var createTime = LocalDateTime.now();
|
||||||
|
this.createdAt = createTime;
|
||||||
|
this.updatedAt = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
protected void onUpdate() {
|
||||||
|
this.updatedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.onixbyte.helix.domain.entity;
|
package com.onixbyte.helix.domain.entity;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.Status;
|
import com.onixbyte.helix.enumeration.Status;
|
||||||
import com.onixbyte.helix.domain.common.Treeable;
|
import com.onixbyte.helix.domain.common.Treeable;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import org.hibernate.annotations.JdbcType;
|
import org.hibernate.annotations.JdbcType;
|
||||||
@@ -23,7 +23,7 @@ import java.util.Objects;
|
|||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "departments")
|
@Table(name = "department")
|
||||||
public class Department implements Treeable<Long> {
|
public class Department implements Treeable<Long> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -287,4 +287,16 @@ public class Department implements Treeable<Long> {
|
|||||||
return new Department(id, name, parentId, sort, status, createdAt, updatedAt);
|
return new Department(id, name, parentId, sort, status, createdAt, updatedAt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
var createTime = LocalDateTime.now();
|
||||||
|
this.createdAt = createTime;
|
||||||
|
this.updatedAt = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
protected void onUpdate() {
|
||||||
|
this.updatedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.onixbyte.helix.domain.entity;
|
package com.onixbyte.helix.domain.entity;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.Status;
|
import com.onixbyte.helix.enumeration.Status;
|
||||||
import com.onixbyte.helix.domain.common.Treeable;
|
import com.onixbyte.helix.domain.common.Treeable;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import org.hibernate.annotations.JdbcType;
|
import org.hibernate.annotations.JdbcType;
|
||||||
@@ -10,7 +10,7 @@ import java.time.LocalDateTime;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "menus")
|
@Table(name = "menu")
|
||||||
public class Menu implements Treeable<Long> {
|
public class Menu implements Treeable<Long> {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@@ -224,4 +224,16 @@ public class Menu implements Treeable<Long> {
|
|||||||
", updatedAt=" + updatedAt +
|
", updatedAt=" + updatedAt +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
var createTime = LocalDateTime.now();
|
||||||
|
this.createdAt = createTime;
|
||||||
|
this.updatedAt = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
protected void onUpdate() {
|
||||||
|
this.updatedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.onixbyte.helix.domain.entity;
|
package com.onixbyte.helix.domain.entity;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.Status;
|
import com.onixbyte.helix.enumeration.Status;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import org.hibernate.annotations.JdbcType;
|
import org.hibernate.annotations.JdbcType;
|
||||||
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
||||||
@@ -21,7 +21,7 @@ import java.util.Objects;
|
|||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "positions")
|
@Table(name = "position")
|
||||||
public class Position {
|
public class Position {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -281,4 +281,16 @@ public class Position {
|
|||||||
return new Position(id, name, code, description, sort, status, createdAt, updatedAt);
|
return new Position(id, name, code, description, sort, status, createdAt, updatedAt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
var createTime = LocalDateTime.now();
|
||||||
|
this.createdAt = createTime;
|
||||||
|
this.updatedAt = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
protected void onUpdate() {
|
||||||
|
this.updatedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.onixbyte.helix.domain.entity;
|
package com.onixbyte.helix.domain.entity;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.Status;
|
import com.onixbyte.helix.enumeration.Status;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import org.hibernate.annotations.JdbcType;
|
import org.hibernate.annotations.JdbcType;
|
||||||
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
||||||
@@ -20,7 +20,7 @@ import java.util.Objects;
|
|||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "roles")
|
@Table(name = "role")
|
||||||
public class Role {
|
public class Role {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -305,4 +305,17 @@ public class Role {
|
|||||||
return new Role(id, name, code, sort, defaultValue, description, status, createdAt, updatedAt);
|
return new Role(id, name, code, sort, defaultValue, description, status, createdAt, updatedAt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
private void onCreate() {
|
||||||
|
var createTime = LocalDateTime.now();
|
||||||
|
this.createdAt = createTime;
|
||||||
|
this.updatedAt = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
private void onUpdate() {
|
||||||
|
this.updatedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import java.util.Objects;
|
|||||||
* auditing field.
|
* auditing field.
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "role_authorities")
|
@Table(name = "role_authority")
|
||||||
public class RoleAuthority {
|
public class RoleAuthority {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,4 +122,9 @@ public class RoleAuthority {
|
|||||||
return new RoleAuthority(roleId, authorityId, createdAt);
|
return new RoleAuthority(roleId, authorityId, createdAt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
this.createdAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.onixbyte.helix.domain.entity;
|
package com.onixbyte.helix.domain.entity;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.SettingType;
|
import com.onixbyte.helix.enumeration.SettingType;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import org.hibernate.annotations.JdbcType;
|
import org.hibernate.annotations.JdbcType;
|
||||||
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
||||||
@@ -14,7 +14,7 @@ import java.util.Objects;
|
|||||||
* This entity allows for dynamic configuration changes without application restarts.
|
* This entity allows for dynamic configuration changes without application restarts.
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "settings")
|
@Table(name = "setting")
|
||||||
public class Setting {
|
public class Setting {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,17 +76,6 @@ public class Setting {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private LocalDateTime updatedAt;
|
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() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,4 +255,16 @@ public class Setting {
|
|||||||
return new Setting(id, name, description, type, value, defaultValue, createdAt, updatedAt);
|
return new Setting(id, name, description, type, value, defaultValue, createdAt, updatedAt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
var createTime = LocalDateTime.now();
|
||||||
|
this.createdAt = createTime;
|
||||||
|
this.updatedAt = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
protected void onUpdate() {
|
||||||
|
this.updatedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.onixbyte.helix.domain.entity;
|
package com.onixbyte.helix.domain.entity;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.UserStatus;
|
import com.onixbyte.helix.enumeration.UserStatus;
|
||||||
import jakarta.persistence.*; // 导入 Jakarta Persistence API
|
import jakarta.persistence.*; // 导入 Jakarta Persistence API
|
||||||
import org.hibernate.annotations.JdbcType;
|
import org.hibernate.annotations.JdbcType;
|
||||||
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
||||||
@@ -22,7 +22,7 @@ import java.util.Objects;
|
|||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(
|
@Table(
|
||||||
name = "users",
|
name = "\"user\"",
|
||||||
uniqueConstraints = {
|
uniqueConstraints = {
|
||||||
@UniqueConstraint(name = "uidx_users_username", columnNames = {"username"}),
|
@UniqueConstraint(name = "uidx_users_username", columnNames = {"username"}),
|
||||||
@UniqueConstraint(name = "uidx_users_email", columnNames = {"email"}),
|
@UniqueConstraint(name = "uidx_users_email", columnNames = {"email"}),
|
||||||
@@ -48,12 +48,6 @@ public class User {
|
|||||||
@Column(nullable = false, length = 64)
|
@Column(nullable = false, length = 64)
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
/**
|
|
||||||
* The encrypted password for user authentication.
|
|
||||||
*/
|
|
||||||
@Column
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The user's complete full name.
|
* The user's complete full name.
|
||||||
*/
|
*/
|
||||||
@@ -121,37 +115,12 @@ public class User {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private LocalDateTime updatedAt;
|
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() {
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
public User(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.id = id;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.password = password;
|
|
||||||
this.fullName = fullName;
|
this.fullName = fullName;
|
||||||
this.email = email;
|
this.email = email;
|
||||||
this.regionAbbreviation = regionAbbreviation;
|
this.regionAbbreviation = regionAbbreviation;
|
||||||
@@ -200,24 +169,6 @@ public class User {
|
|||||||
this.username = 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() {
|
public String getFullName() {
|
||||||
return fullName;
|
return fullName;
|
||||||
}
|
}
|
||||||
@@ -361,7 +312,6 @@ public class User {
|
|||||||
public static class UserBuilder {
|
public static class UserBuilder {
|
||||||
private Long id;
|
private Long id;
|
||||||
private String username;
|
private String username;
|
||||||
private String password;
|
|
||||||
private String fullName;
|
private String fullName;
|
||||||
private String email;
|
private String email;
|
||||||
private String regionAbbreviation;
|
private String regionAbbreviation;
|
||||||
@@ -386,11 +336,6 @@ public class User {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserBuilder password(String password) {
|
|
||||||
this.password = password;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserBuilder fullName(String fullName) {
|
public UserBuilder fullName(String fullName) {
|
||||||
this.fullName = fullName;
|
this.fullName = fullName;
|
||||||
return this;
|
return this;
|
||||||
@@ -447,7 +392,19 @@ public class User {
|
|||||||
* @return a new User instance
|
* @return a new User instance
|
||||||
*/
|
*/
|
||||||
public User build() {
|
public User build() {
|
||||||
return new User(id, username, password, fullName, email, regionAbbreviation, phoneNumber, avatarUrl, status, departmentId, positionId, createdAt, updatedAt);
|
return new User(id, username, fullName, email, regionAbbreviation, phoneNumber, avatarUrl, status, departmentId, positionId, createdAt, updatedAt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
var createTime = LocalDateTime.now();
|
||||||
|
this.createdAt = createTime;
|
||||||
|
this.updatedAt = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
protected void onUpdate() {
|
||||||
|
this.updatedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+37
-45
@@ -1,7 +1,7 @@
|
|||||||
package com.onixbyte.helix.domain.entity;
|
package com.onixbyte.helix.domain.entity;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.IdentityProvider;
|
import com.onixbyte.helix.enumeration.CredentialProvider;
|
||||||
import com.onixbyte.helix.domain.entity.embeddable.UserIdentityId;
|
import com.onixbyte.helix.domain.entity.embeddable.UserCredentialId;
|
||||||
import jakarta.persistence.*; // 导入 Jakarta Persistence API
|
import jakarta.persistence.*; // 导入 Jakarta Persistence API
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -19,14 +19,14 @@ import java.util.Objects;
|
|||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "user_identities")
|
@Table(name = "user_credential")
|
||||||
public class UserIdentity {
|
public class UserCredential {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The composite primary key for the entity, composed of userId, provider, and externalId.
|
* The composite primary key for the entity, composed of userId, provider, and externalId.
|
||||||
*/
|
*/
|
||||||
@EmbeddedId
|
@EmbeddedId
|
||||||
private UserIdentityId id;
|
private UserCredentialId id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The timestamp when this identity mapping was created.
|
* The timestamp when this identity mapping was created.
|
||||||
@@ -40,26 +40,6 @@ public class UserIdentity {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private LocalDateTime updatedAt;
|
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.
|
* Gets the identifier of the internal user account from the composite primary key.
|
||||||
* @return the user ID
|
* @return the user ID
|
||||||
@@ -73,7 +53,7 @@ public class UserIdentity {
|
|||||||
* @param userId the user ID
|
* @param userId the user ID
|
||||||
*/
|
*/
|
||||||
public void setUserId(Long userId) {
|
public void setUserId(Long userId) {
|
||||||
if (this.id == null) this.id = new UserIdentityId();
|
if (this.id == null) this.id = new UserCredentialId();
|
||||||
this.id.setUserId(userId);
|
this.id.setUserId(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +61,7 @@ public class UserIdentity {
|
|||||||
* Gets the external identity provider from the composite primary key.
|
* Gets the external identity provider from the composite primary key.
|
||||||
* @return the provider
|
* @return the provider
|
||||||
*/
|
*/
|
||||||
public IdentityProvider getProvider() {
|
public CredentialProvider getProvider() {
|
||||||
return this.id != null ? this.id.getProvider() : null;
|
return this.id != null ? this.id.getProvider() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,8 +69,8 @@ public class UserIdentity {
|
|||||||
* Sets the external identity provider within the composite primary key.
|
* Sets the external identity provider within the composite primary key.
|
||||||
* @param provider the provider
|
* @param provider the provider
|
||||||
*/
|
*/
|
||||||
public void setProvider(IdentityProvider provider) {
|
public void setProvider(CredentialProvider provider) {
|
||||||
if (this.id == null) this.id = new UserIdentityId();
|
if (this.id == null) this.id = new UserCredentialId();
|
||||||
this.id.setProvider(provider);
|
this.id.setProvider(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,17 +78,17 @@ public class UserIdentity {
|
|||||||
* Gets the unique identifier from the external provider from the composite primary key.
|
* Gets the unique identifier from the external provider from the composite primary key.
|
||||||
* @return the external ID
|
* @return the external ID
|
||||||
*/
|
*/
|
||||||
public String getExternalId() {
|
public String getCredential() {
|
||||||
return this.id != null ? this.id.getExternalId() : null;
|
return this.id != null ? this.id.getCredential() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the unique identifier from the external provider within the composite primary key.
|
* Sets the unique identifier from the external provider within the composite primary key.
|
||||||
* @param externalId the external ID
|
* @param credential the external ID
|
||||||
*/
|
*/
|
||||||
public void setExternalId(String externalId) {
|
public void setCredential(String credential) {
|
||||||
if (this.id == null) this.id = new UserIdentityId();
|
if (this.id == null) this.id = new UserCredentialId();
|
||||||
this.id.setExternalId(externalId);
|
this.id.setCredential(credential);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDateTime getCreatedAt() {
|
public LocalDateTime getCreatedAt() {
|
||||||
@@ -129,12 +109,12 @@ public class UserIdentity {
|
|||||||
|
|
||||||
// --- Constructors (Adjusted for EmbeddedId) ---
|
// --- Constructors (Adjusted for EmbeddedId) ---
|
||||||
|
|
||||||
public UserIdentity() {
|
public UserCredential() {
|
||||||
this.id = new UserIdentityId(); // Initialize ID object for safety
|
this.id = new UserCredentialId(); // Initialize ID object for safety
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserIdentity(Long userId, IdentityProvider provider, String externalId, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
public UserCredential(Long userId, CredentialProvider provider, String externalId, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||||
this.id = new UserIdentityId(userId, provider, externalId);
|
this.id = new UserCredentialId(userId, provider, externalId);
|
||||||
this.createdAt = createdAt;
|
this.createdAt = createdAt;
|
||||||
this.updatedAt = updatedAt;
|
this.updatedAt = updatedAt;
|
||||||
}
|
}
|
||||||
@@ -145,7 +125,7 @@ public class UserIdentity {
|
|||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
UserIdentity that = (UserIdentity) o;
|
UserCredential that = (UserCredential) o;
|
||||||
return Objects.equals(id, that.id); // Entity equality based on primary key
|
return Objects.equals(id, that.id); // Entity equality based on primary key
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +139,7 @@ public class UserIdentity {
|
|||||||
return "UserIdentity{" +
|
return "UserIdentity{" +
|
||||||
"userId=" + getUserId() +
|
"userId=" + getUserId() +
|
||||||
", provider=" + getProvider() +
|
", provider=" + getProvider() +
|
||||||
", externalId='" + getExternalId() + '\'' +
|
", externalId='" + getCredential() + '\'' +
|
||||||
", createdAt=" + createdAt +
|
", createdAt=" + createdAt +
|
||||||
", updatedAt=" + updatedAt +
|
", updatedAt=" + updatedAt +
|
||||||
'}';
|
'}';
|
||||||
@@ -182,7 +162,7 @@ public class UserIdentity {
|
|||||||
*/
|
*/
|
||||||
public static class UserIdentityBuilder {
|
public static class UserIdentityBuilder {
|
||||||
private Long userId;
|
private Long userId;
|
||||||
private IdentityProvider provider;
|
private CredentialProvider provider;
|
||||||
private String externalId;
|
private String externalId;
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
@@ -195,7 +175,7 @@ public class UserIdentity {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserIdentityBuilder provider(IdentityProvider provider) {
|
public UserIdentityBuilder provider(CredentialProvider provider) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -220,8 +200,20 @@ public class UserIdentity {
|
|||||||
*
|
*
|
||||||
* @return a new UserIdentity instance
|
* @return a new UserIdentity instance
|
||||||
*/
|
*/
|
||||||
public UserIdentity build() {
|
public UserCredential build() {
|
||||||
return new UserIdentity(userId, provider, externalId, createdAt, updatedAt);
|
return new UserCredential(userId, provider, externalId, createdAt, updatedAt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
var createTime = LocalDateTime.now();
|
||||||
|
this.createdAt = createTime;
|
||||||
|
this.updatedAt = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
protected void onUpdate() {
|
||||||
|
this.updatedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ import java.util.Objects;
|
|||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "user_roles")
|
@Table(name = "user_role")
|
||||||
public class UserRole {
|
public class UserRole {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,17 +37,6 @@ public class UserRole {
|
|||||||
@Column(nullable = false, updatable = false)
|
@Column(nullable = false, updatable = false)
|
||||||
private LocalDateTime createdAt;
|
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.
|
* Gets the identifier of the role from the composite primary key.
|
||||||
*
|
*
|
||||||
@@ -173,4 +162,9 @@ public class UserRole {
|
|||||||
return new UserRole(roleId, userId, createdAt);
|
return new UserRole(roleId, userId, createdAt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
this.createdAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+16
-17
@@ -1,9 +1,8 @@
|
|||||||
package com.onixbyte.helix.domain.entity.embeddable;
|
package com.onixbyte.helix.domain.entity.embeddable;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.IdentityProvider;
|
import com.onixbyte.helix.enumeration.CredentialProvider;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Embeddable;
|
import jakarta.persistence.Embeddable;
|
||||||
import jakarta.persistence.EnumType;
|
|
||||||
import jakarta.persistence.Enumerated;
|
import jakarta.persistence.Enumerated;
|
||||||
import org.hibernate.annotations.JdbcType;
|
import org.hibernate.annotations.JdbcType;
|
||||||
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
|
||||||
@@ -19,7 +18,7 @@ import java.util.Objects;
|
|||||||
* from that provider.
|
* from that provider.
|
||||||
*/
|
*/
|
||||||
@Embeddable
|
@Embeddable
|
||||||
public class UserIdentityId implements Serializable {
|
public class UserCredentialId implements Serializable {
|
||||||
|
|
||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
@@ -38,23 +37,23 @@ public class UserIdentityId implements Serializable {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
@Enumerated
|
@Enumerated
|
||||||
@JdbcType(PostgreSQLEnumJdbcType.class)
|
@JdbcType(PostgreSQLEnumJdbcType.class)
|
||||||
private IdentityProvider provider;
|
private CredentialProvider provider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The unique identifier from the external provider, corresponding to the 'external_id' column.
|
* The unique identifier from the external provider, corresponding to the 'external_id' column.
|
||||||
*/
|
*/
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String externalId;
|
private String credential;
|
||||||
|
|
||||||
// --- Constructors ---
|
// --- Constructors ---
|
||||||
|
|
||||||
public UserIdentityId() {
|
public UserCredentialId() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserIdentityId(Long userId, IdentityProvider provider, String externalId) {
|
public UserCredentialId(Long userId, CredentialProvider provider, String credential) {
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.externalId = externalId;
|
this.credential = credential;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Getters and Setters (Omitted for brevity, but should exist) ---
|
// --- Getters and Setters (Omitted for brevity, but should exist) ---
|
||||||
@@ -66,32 +65,32 @@ public class UserIdentityId implements Serializable {
|
|||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IdentityProvider getProvider() {
|
public CredentialProvider getProvider() {
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProvider(IdentityProvider provider) {
|
public void setProvider(CredentialProvider provider) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getExternalId() {
|
public String getCredential() {
|
||||||
return externalId;
|
return credential;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExternalId(String externalId) {
|
public void setCredential(String credential) {
|
||||||
this.externalId = externalId;
|
this.credential = credential;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- equals and hashCode (REQUIRED for composite keys) ---
|
// --- equals and hashCode (REQUIRED for composite keys) ---
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
UserIdentityId that = (UserIdentityId) o;
|
UserCredentialId that = (UserCredentialId) o;
|
||||||
return Objects.equals(userId, that.userId) && provider == that.provider && Objects.equals(externalId, that.externalId);
|
return Objects.equals(userId, that.userId) && provider == that.provider && Objects.equals(credential, that.credential);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(userId, provider, externalId);
|
return Objects.hash(userId, provider, credential);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.onixbyte.helix.domain.view;
|
package com.onixbyte.helix.domain.view;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.Status;
|
import com.onixbyte.helix.enumeration.Status;
|
||||||
import com.onixbyte.helix.domain.entity.Authority;
|
import com.onixbyte.helix.domain.entity.Authority;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.onixbyte.helix.domain.view;
|
package com.onixbyte.helix.domain.view;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.Status;
|
import com.onixbyte.helix.enumeration.Status;
|
||||||
import com.onixbyte.helix.domain.entity.Department;
|
import com.onixbyte.helix.domain.entity.Department;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -209,7 +209,6 @@ public class DepartmentView {
|
|||||||
private Long id;
|
private Long id;
|
||||||
private String name;
|
private String name;
|
||||||
private Long parentId;
|
private Long parentId;
|
||||||
private String treePath;
|
|
||||||
private Integer sort;
|
private Integer sort;
|
||||||
private Status status;
|
private Status status;
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
@@ -233,11 +232,6 @@ public class DepartmentView {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DepartmentViewBuilder treePath(String treePath) {
|
|
||||||
this.treePath = treePath;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DepartmentViewBuilder sort(Integer sort) {
|
public DepartmentViewBuilder sort(Integer sort) {
|
||||||
this.sort = sort;
|
this.sort = sort;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.onixbyte.helix.domain.view;
|
package com.onixbyte.helix.domain.view;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.Status;
|
import com.onixbyte.helix.enumeration.Status;
|
||||||
import com.onixbyte.helix.domain.entity.Position;
|
import com.onixbyte.helix.domain.entity.Position;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.onixbyte.helix.domain.view;
|
package com.onixbyte.helix.domain.view;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.Status;
|
import com.onixbyte.helix.enumeration.Status;
|
||||||
import com.onixbyte.helix.domain.entity.Role;
|
import com.onixbyte.helix.domain.entity.Role;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.onixbyte.helix.domain.view;
|
package com.onixbyte.helix.domain.view;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.IdentityProvider;
|
import com.onixbyte.helix.enumeration.CredentialProvider;
|
||||||
import com.onixbyte.helix.domain.entity.UserIdentity;
|
import com.onixbyte.helix.domain.entity.UserCredential;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -26,7 +26,7 @@ public class UserIdentityView {
|
|||||||
/**
|
/**
|
||||||
* The external identity provider.
|
* The external identity provider.
|
||||||
*/
|
*/
|
||||||
private IdentityProvider provider;
|
private CredentialProvider provider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The unique identifier from the external provider.
|
* The unique identifier from the external provider.
|
||||||
@@ -52,8 +52,8 @@ public class UserIdentityView {
|
|||||||
/**
|
/**
|
||||||
* Constructor with all fields.
|
* Constructor with all fields.
|
||||||
*/
|
*/
|
||||||
public UserIdentityView(Long userId, IdentityProvider provider, String externalId,
|
public UserIdentityView(Long userId, CredentialProvider provider, String externalId,
|
||||||
LocalDateTime createdAt, LocalDateTime updatedAt) {
|
LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.externalId = externalId;
|
this.externalId = externalId;
|
||||||
@@ -64,19 +64,19 @@ public class UserIdentityView {
|
|||||||
/**
|
/**
|
||||||
* Creates a UserIdentityView from a UserIdentity entity.
|
* Creates a UserIdentityView from a UserIdentity entity.
|
||||||
*
|
*
|
||||||
* @param userIdentity the UserIdentity entity
|
* @param userCredential the UserIdentity entity
|
||||||
* @return the UserIdentityView object
|
* @return the UserIdentityView object
|
||||||
*/
|
*/
|
||||||
public static UserIdentityView fromEntity(UserIdentity userIdentity) {
|
public static UserIdentityView fromEntity(UserCredential userCredential) {
|
||||||
if (userIdentity == null) {
|
if (userCredential == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new UserIdentityView(
|
return new UserIdentityView(
|
||||||
userIdentity.getUserId(),
|
userCredential.getUserId(),
|
||||||
userIdentity.getProvider(),
|
userCredential.getProvider(),
|
||||||
userIdentity.getExternalId(),
|
userCredential.getCredential(),
|
||||||
userIdentity.getCreatedAt(),
|
userCredential.getCreatedAt(),
|
||||||
userIdentity.getUpdatedAt()
|
userCredential.getUpdatedAt()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,11 +88,11 @@ public class UserIdentityView {
|
|||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IdentityProvider getProvider() {
|
public CredentialProvider getProvider() {
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProvider(IdentityProvider provider) {
|
public void setProvider(CredentialProvider provider) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ public class UserIdentityView {
|
|||||||
*/
|
*/
|
||||||
public static class UserIdentityViewBuilder {
|
public static class UserIdentityViewBuilder {
|
||||||
private Long userId;
|
private Long userId;
|
||||||
private IdentityProvider provider;
|
private CredentialProvider provider;
|
||||||
private String externalId;
|
private String externalId;
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
@@ -175,7 +175,7 @@ public class UserIdentityView {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserIdentityViewBuilder provider(IdentityProvider provider) {
|
public UserIdentityViewBuilder provider(CredentialProvider provider) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.onixbyte.helix.domain.view;
|
package com.onixbyte.helix.domain.view;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.UserStatus;
|
import com.onixbyte.helix.enumeration.UserStatus;
|
||||||
import com.onixbyte.helix.domain.entity.User;
|
import com.onixbyte.helix.domain.entity.User;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package com.onixbyte.helix.domain.web.request;
|
package com.onixbyte.helix.domain.web.request;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.UserStatus;
|
import com.onixbyte.helix.enumeration.UserStatus;
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public record AddUserRequest(
|
public record AddUserRequest(
|
||||||
@NotBlank(message = "Username cannot be empty.")
|
@NotBlank(message = "{" + MessageName.REQUEST_ADD_USER_USERNAME_NOT_EMPTY + "}")
|
||||||
String username,
|
String username,
|
||||||
@NotBlank(message = "Password cannot be empty.")
|
@NotBlank(message = "{" + MessageName.REQUEST_ADD_USER_PASSWORD_NOT_EMPTY + "}")
|
||||||
String password,
|
String password,
|
||||||
@NotBlank(message = "Full name cannot be empty.")
|
@NotBlank(message = "{" + MessageName.REQUEST_ADD_USER_FULL_NAME_NOT_EMPTY + "}")
|
||||||
String fullName,
|
String fullName,
|
||||||
String email,
|
String email,
|
||||||
String regionAbbreviation,
|
String regionAbbreviation,
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.onixbyte.helix.domain.web.request;
|
||||||
|
|
||||||
|
import com.onixbyte.helix.enumeration.Status;
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
|
import com.onixbyte.helix.validation.group.OnCreate;
|
||||||
|
import com.onixbyte.helix.validation.group.OnUpdate;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Null;
|
||||||
|
|
||||||
|
public record AuthorityRequest(
|
||||||
|
@Null(groups = {OnUpdate.class}, message = "{" + MessageName.REQUEST_AUTHORITY_CODE_NOT_EDITABLE + "}")
|
||||||
|
@NotNull(groups = {OnCreate.class}, message = "{" + MessageName.REQUEST_AUTHORITY_CODE_NOT_NULL + "}")
|
||||||
|
String code,
|
||||||
|
@NotNull(message = "{" + MessageName.REQUEST_AUTHORITY_NAME_NOT_NULL + "}")
|
||||||
|
@NotBlank(message = "{" + MessageName.REQUEST_AUTHORITY_NAME_NOT_NULL + "}")
|
||||||
|
String name,
|
||||||
|
String description,
|
||||||
|
Status status
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024-2026 OnixByte
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.onixbyte.helix.domain.web.request;
|
||||||
|
|
||||||
|
import com.onixbyte.helix.enumeration.Status;
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public record DepartmentRequest(
|
||||||
|
@NotNull(message = "{" + MessageName.REQUEST_DEPARTMENT_NAME_NOT_NULL + "}")
|
||||||
|
@NotBlank(message = "{" + MessageName.REQUEST_DEPARTMENT_NAME_NOT_NULL + "}")
|
||||||
|
String name,
|
||||||
|
Long parentId,
|
||||||
|
Integer sort,
|
||||||
|
Status status
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.onixbyte.helix.domain.web.request;
|
||||||
|
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
|
||||||
|
public record EditRoleRequest(
|
||||||
|
@NotNull(message = "{" + MessageName.REQUEST_EDIT_ROLE_ID_NOT_NULL + "}")
|
||||||
|
Long id,
|
||||||
|
String name,
|
||||||
|
String code,
|
||||||
|
Integer sort,
|
||||||
|
Boolean defaultValue,
|
||||||
|
String description,
|
||||||
|
@Pattern(
|
||||||
|
regexp = "^(ACTIVE|INACTIVE)?$",
|
||||||
|
message = "{" + MessageName.REQUEST_EDIT_ROLE_STATUS_INVALID + "}")
|
||||||
|
String status
|
||||||
|
) {
|
||||||
|
}
|
||||||
+5
-6
@@ -1,14 +1,13 @@
|
|||||||
package com.onixbyte.helix.domain.web.request;
|
package com.onixbyte.helix.domain.web.request;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.UserStatus;
|
import com.onixbyte.helix.enumeration.UserStatus;
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import jakarta.validation.constraints.Positive;
|
import jakarta.validation.constraints.Positive;
|
||||||
|
|
||||||
import java.util.List;
|
public record EditUserRequest(
|
||||||
|
@NotNull(message = "{" + MessageName.REQUEST_EDIT_USER_ID_NOT_NULL + "}")
|
||||||
public record UpdateUserRequest(
|
@Positive(message = "{" + MessageName.REQUEST_EDIT_USER_ID_POSITIVE + "}")
|
||||||
@NotNull(message = "User ID cannot be null")
|
|
||||||
@Positive(message = "User ID must be positive")
|
|
||||||
Long id,
|
Long id,
|
||||||
String fullName,
|
String fullName,
|
||||||
String email,
|
String email,
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.onixbyte.helix.domain.web.request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login data.
|
||||||
|
*
|
||||||
|
* @param username username
|
||||||
|
* @param password password
|
||||||
|
* @param uuid captcha uuid
|
||||||
|
* @param captcha captcha code
|
||||||
|
*/
|
||||||
|
public record LoginRequest(
|
||||||
|
String username,
|
||||||
|
String password,
|
||||||
|
String uuid,
|
||||||
|
String captcha
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.onixbyte.helix.domain.web.request;
|
||||||
|
|
||||||
|
public record QueryAuthorityRequest() {
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.onixbyte.helix.domain.web.request;
|
package com.onixbyte.helix.domain.web.request;
|
||||||
|
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
import jakarta.validation.constraints.Pattern;
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
|
||||||
public record QueryRoleRequest(
|
public record QueryRoleRequest(
|
||||||
@@ -7,7 +8,7 @@ public record QueryRoleRequest(
|
|||||||
String code,
|
String code,
|
||||||
@Pattern(
|
@Pattern(
|
||||||
regexp = "^(ACTIVE|INACTIVE)?$",
|
regexp = "^(ACTIVE|INACTIVE)?$",
|
||||||
message = "状态仅可以是 ACTIVE、INACTIVE 其中之一")
|
message = "{" + MessageName.REQUEST_QUERY_ROLE_STATUS_INVALID + "}")
|
||||||
String status
|
String status
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.onixbyte.helix.domain.web.request;
|
package com.onixbyte.helix.domain.web.request;
|
||||||
|
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
import jakarta.validation.constraints.Pattern;
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -11,7 +12,7 @@ public record QueryUserRequest(
|
|||||||
String phoneNumber,
|
String phoneNumber,
|
||||||
@Pattern(
|
@Pattern(
|
||||||
regexp = "^(ACTIVE|INACTIVE|LOCKED)?$",
|
regexp = "^(ACTIVE|INACTIVE|LOCKED)?$",
|
||||||
message = "状态仅可以是 ACTIVE、INACTIVE 或 LOCKED 其中之一")
|
message = "{" + MessageName.REQUEST_QUERY_USER_STATUS_INVALID + "}")
|
||||||
String status,
|
String status,
|
||||||
LocalDateTime createdAtStart,
|
LocalDateTime createdAtStart,
|
||||||
LocalDateTime createdAtEnd
|
LocalDateTime createdAtEnd
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package com.onixbyte.helix.domain.web.request;
|
package com.onixbyte.helix.domain.web.request;
|
||||||
|
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
public record ResetPasswordRequest(
|
public record ResetPasswordRequest(
|
||||||
@NotNull(message = "用户 ID 不能为空") Long id,
|
@NotBlank(message = "{" + MessageName.REQUEST_RESET_PASSWORD_NOT_EMPTY + "}") String password
|
||||||
@NotBlank(message = "密码不能为空") String password
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.onixbyte.helix.domain.web.request;
|
||||||
|
|
||||||
|
import com.onixbyte.helix.enumeration.Status;
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
|
||||||
|
public record RoleRequest(
|
||||||
|
@NotBlank(message = "{" + MessageName.REQUEST_ROLE_NAME_NOT_EMPTY + "}")
|
||||||
|
String name,
|
||||||
|
@NotBlank(message = "{" + MessageName.REQUEST_ROLE_CODE_NOT_EMPTY + "}")
|
||||||
|
String code,
|
||||||
|
@NotNull(message = "{" + MessageName.REQUEST_ROLE_SORT_NOT_NULL + "}")
|
||||||
|
Integer sort,
|
||||||
|
Boolean defaultValue,
|
||||||
|
String description,
|
||||||
|
Status status
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package com.onixbyte.helix.domain.web.request;
|
|
||||||
|
|
||||||
public record UsernamePasswordLoginRequest(
|
|
||||||
String username,
|
|
||||||
String password,
|
|
||||||
String uuid,
|
|
||||||
String captcha
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.onixbyte.helix.domain.web.response;
|
||||||
|
|
||||||
|
public record ActionResponse(
|
||||||
|
String message,
|
||||||
|
boolean success
|
||||||
|
) {
|
||||||
|
|
||||||
|
public static ActionResponse success(String message) {
|
||||||
|
return new ActionResponse(message, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ActionResponse failed(String message) {
|
||||||
|
return new ActionResponse(message, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,4 @@ public record LoginSuccessResponse(
|
|||||||
String accessToken,
|
String accessToken,
|
||||||
User user
|
User user
|
||||||
) {
|
) {
|
||||||
|
|
||||||
public LoginSuccessResponse {
|
|
||||||
user.setPassword(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.onixbyte.helix.domain.web.response;
|
package com.onixbyte.helix.domain.web.response;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.UserStatus;
|
import com.onixbyte.helix.domain.entity.User;
|
||||||
|
import com.onixbyte.helix.enumeration.UserStatus;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@@ -175,6 +176,22 @@ public class UserDetailResponse {
|
|||||||
private UserDetailResponseBuilder() {
|
private UserDetailResponseBuilder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserDetailResponseBuilder user(User user) {
|
||||||
|
this.id = String.valueOf(user.getId());
|
||||||
|
this.username = user.getUsername();
|
||||||
|
this.fullName = user.getFullName();
|
||||||
|
this.email = user.getEmail();
|
||||||
|
this.regionAbbreviation = user.getRegionAbbreviation();
|
||||||
|
this.phoneNumber = user.getPhoneNumber();
|
||||||
|
this.avatarUrl = user.getAvatarUrl();
|
||||||
|
this.status = user.getStatus();
|
||||||
|
this.departmentId = user.getDepartmentId();
|
||||||
|
this.positionId = user.getPositionId();
|
||||||
|
this.createdAt = user.getCreatedAt();
|
||||||
|
this.updatedAt = user.getUpdatedAt();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public UserDetailResponseBuilder id(String id) {
|
public UserDetailResponseBuilder id(String id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024-2026 OnixByte
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.onixbyte.helix.enumeration;
|
||||||
|
|
||||||
|
public enum ApplicationMode {
|
||||||
|
|
||||||
|
DEVELOPMENT,
|
||||||
|
PRODUCTION,
|
||||||
|
;
|
||||||
|
}
|
||||||
+24
-2
@@ -1,4 +1,26 @@
|
|||||||
package com.onixbyte.helix.constant;
|
/*
|
||||||
|
* Copyright (c) 2024-2026 OnixByte
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.onixbyte.helix.enumeration;
|
||||||
|
|
||||||
import com.onixbyte.helix.config.AuthenticationConfig;
|
import com.onixbyte.helix.config.AuthenticationConfig;
|
||||||
|
|
||||||
@@ -18,7 +40,7 @@ import com.onixbyte.helix.config.AuthenticationConfig;
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
* @see AuthenticationConfig
|
* @see AuthenticationConfig
|
||||||
*/
|
*/
|
||||||
public enum IdentityProvider {
|
public enum CredentialProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local identity provider using the application's internal user database.
|
* Local identity provider using the application's internal user database.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.onixbyte.helix.constant;
|
package com.onixbyte.helix.enumeration;
|
||||||
|
|
||||||
public enum SettingType {
|
public enum SettingType {
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.onixbyte.helix.constant;
|
package com.onixbyte.helix.enumeration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumeration representing general status states for system entities.
|
* Enumeration representing general status states for system entities.
|
||||||
+1
-2
@@ -1,4 +1,4 @@
|
|||||||
package com.onixbyte.helix.constant;
|
package com.onixbyte.helix.enumeration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumeration representing the various states of user accounts within the system.
|
* Enumeration representing the various states of user accounts within the system.
|
||||||
@@ -14,7 +14,6 @@ package com.onixbyte.helix.constant;
|
|||||||
*
|
*
|
||||||
* @author zihluwang
|
* @author zihluwang
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
* @see com.onixbyte.helix.config.SecurityConfiguration
|
|
||||||
*/
|
*/
|
||||||
public enum UserStatus {
|
public enum UserStatus {
|
||||||
|
|
||||||
@@ -41,26 +41,19 @@ public class BizException extends RuntimeException {
|
|||||||
* REST API endpoints.
|
* REST API endpoints.
|
||||||
*/
|
*/
|
||||||
private final HttpStatus status;
|
private final HttpStatus status;
|
||||||
|
private final String messageCode;
|
||||||
|
private final Object[] messageArgs;
|
||||||
|
|
||||||
/**
|
public BizException(String messageCode, Object... messageArgs) {
|
||||||
* 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;
|
this.status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||||
|
this.messageCode = messageCode;
|
||||||
|
this.messageArgs = messageArgs == null ? new Object[0] : messageArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public BizException(HttpStatus status, String messageCode, Object... messageArgs) {
|
||||||
* 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;
|
this.status = status;
|
||||||
|
this.messageCode = messageCode;
|
||||||
|
this.messageArgs = messageArgs == null ? new Object[0] : messageArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,4 +64,12 @@ public class BizException extends RuntimeException {
|
|||||||
public HttpStatus getStatus() {
|
public HttpStatus getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getMessageCode() {
|
||||||
|
return messageCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[] getMessageArgs() {
|
||||||
|
return messageArgs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package com.onixbyte.helix.filter;
|
package com.onixbyte.helix.filter;
|
||||||
|
|
||||||
import com.auth0.jwt.JWT;
|
|
||||||
import com.auth0.jwt.algorithms.Algorithm;
|
|
||||||
import com.auth0.jwt.exceptions.JWTVerificationException;
|
import com.auth0.jwt.exceptions.JWTVerificationException;
|
||||||
|
import com.onixbyte.helix.client.TokenClient;
|
||||||
import com.onixbyte.helix.manager.AuthorityManager;
|
import com.onixbyte.helix.manager.AuthorityManager;
|
||||||
import com.onixbyte.helix.manager.UserManager;
|
import com.onixbyte.helix.manager.UserManager;
|
||||||
import com.onixbyte.helix.security.authentication.UsernamePasswordAuthentication;
|
import com.onixbyte.helix.security.authentication.UsernamePasswordAuthentication;
|
||||||
|
import com.onixbyte.helix.shared.TokenConstant;
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.Cookie;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.jspecify.annotations.NonNull;
|
import org.jspecify.annotations.NonNull;
|
||||||
@@ -17,23 +18,29 @@ import org.springframework.security.core.GrantedAuthority;
|
|||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
import org.springframework.web.util.WebUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
private final static Logger log = LoggerFactory.getLogger(TokenAuthenticationFilter.class);
|
private final static Logger log = LoggerFactory.getLogger(TokenAuthenticationFilter.class);
|
||||||
|
|
||||||
private final Algorithm algorithm;
|
|
||||||
private final UserManager userManager;
|
private final UserManager userManager;
|
||||||
private final AuthorityManager authorityManager;
|
private final AuthorityManager authorityManager;
|
||||||
|
private final TokenClient tokenClient;
|
||||||
|
|
||||||
public TokenAuthenticationFilter(Algorithm algorithm, UserManager userManager, AuthorityManager authorityManager) {
|
public TokenAuthenticationFilter(
|
||||||
this.algorithm = algorithm;
|
UserManager userManager,
|
||||||
|
AuthorityManager authorityManager,
|
||||||
|
TokenClient tokenClient
|
||||||
|
) {
|
||||||
this.userManager = userManager;
|
this.userManager = userManager;
|
||||||
this.authorityManager = authorityManager;
|
this.authorityManager = authorityManager;
|
||||||
|
this.tokenClient = tokenClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -42,24 +49,16 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
@NonNull HttpServletResponse response,
|
@NonNull HttpServletResponse response,
|
||||||
@NonNull FilterChain filterChain
|
@NonNull FilterChain filterChain
|
||||||
) throws ServletException, IOException {
|
) throws ServletException, IOException {
|
||||||
var token = request.getHeader("Authorization");
|
var token = Optional.ofNullable(WebUtils.getCookie(request, TokenConstant.TOKEN_NAME))
|
||||||
|
.map(Cookie::getValue)
|
||||||
|
.orElse(null);
|
||||||
if (Objects.isNull(token) || token.isBlank()) {
|
if (Objects.isNull(token) || token.isBlank()) {
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!token.startsWith("Bearer ")) {
|
|
||||||
filterChain.doFilter(request, response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
token = token.substring(7);
|
|
||||||
var verifier = JWT.require(algorithm)
|
|
||||||
.withIssuer("Helix Server")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var decodedToken = verifier.verify(token);
|
var decodedToken = tokenClient.verifyToken(token);
|
||||||
var username = decodedToken.getSubject();
|
var username = decodedToken.getSubject();
|
||||||
|
|
||||||
var user = userManager.selectByUsername(username);
|
var user = userManager.selectByUsername(username);
|
||||||
@@ -68,8 +67,6 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
.map((authority) -> (GrantedAuthority) authority::getCode)
|
.map((authority) -> (GrantedAuthority) authority::getCode)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
user.setPassword(null);
|
|
||||||
|
|
||||||
var authentication = UsernamePasswordAuthentication.authenticated(user, authorities);
|
var authentication = UsernamePasswordAuthentication.authenticated(user, authorities);
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
|
|||||||
@@ -1,18 +1,44 @@
|
|||||||
package com.onixbyte.helix.manager;
|
package com.onixbyte.helix.manager;
|
||||||
|
|
||||||
|
import com.onixbyte.helix.enumeration.ApplicationMode;
|
||||||
import com.onixbyte.helix.properties.ApplicationProperties;
|
import com.onixbyte.helix.properties.ApplicationProperties;
|
||||||
|
import com.onixbyte.helix.properties.AuthenticationProperties;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class ApplicationManager {
|
public class ApplicationManager {
|
||||||
|
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
private final AuthenticationProperties authenticationProperties;
|
||||||
|
|
||||||
public ApplicationManager(ApplicationProperties applicationProperties) {
|
@Autowired
|
||||||
|
public ApplicationManager(ApplicationProperties applicationProperties, AuthenticationProperties authenticationProperties) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
|
this.authenticationProperties = authenticationProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDefaultEmail() {
|
public String getDefaultEmail() {
|
||||||
return applicationProperties.defaultEmail();
|
return applicationProperties.defaultEmail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getExternalHost() {
|
||||||
|
return applicationProperties.externalHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSslEnabled() {
|
||||||
|
return Optional.ofNullable(authenticationProperties.sslEnabled())
|
||||||
|
.orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSecureCookieEnabled() {
|
||||||
|
return Optional.ofNullable(authenticationProperties.secureCookieEnabled())
|
||||||
|
.orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationMode getApplicationMode() {
|
||||||
|
return applicationProperties.mode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,21 @@
|
|||||||
package com.onixbyte.helix.manager;
|
package com.onixbyte.helix.manager;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.CacheName;
|
import com.onixbyte.helix.shared.CacheName;
|
||||||
import com.onixbyte.helix.domain.entity.Asset;
|
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 com.onixbyte.helix.repository.AssetRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.cache.annotation.CacheEvict;
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
import org.springframework.cache.annotation.CachePut;
|
import org.springframework.cache.annotation.CachePut;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class AssetManager {
|
public class AssetManager {
|
||||||
|
|
||||||
private final AssetMapper assetMapper;
|
|
||||||
private final AssetRepository assetRepository;
|
private final AssetRepository assetRepository;
|
||||||
|
|
||||||
public AssetManager(AssetMapper assetMapper, AssetRepository assetRepository) {
|
@Autowired
|
||||||
this.assetMapper = assetMapper;
|
public AssetManager(AssetRepository assetRepository) {
|
||||||
this.assetRepository = assetRepository;
|
this.assetRepository = assetRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,92 @@
|
|||||||
package com.onixbyte.helix.manager;
|
package com.onixbyte.helix.manager;
|
||||||
|
|
||||||
import com.onixbyte.helix.constant.CacheName;
|
import com.onixbyte.helix.domain.database.query.wrapper.QueryAuthorityWrapper;
|
||||||
import com.onixbyte.helix.domain.entity.Authority;
|
import com.onixbyte.helix.domain.entity.Authority;
|
||||||
|
import com.onixbyte.helix.domain.web.request.AuthorityRequest;
|
||||||
|
import com.onixbyte.helix.enumeration.Status;
|
||||||
|
import com.onixbyte.helix.exception.BizException;
|
||||||
import com.onixbyte.helix.mapper.AuthorityMapper;
|
import com.onixbyte.helix.mapper.AuthorityMapper;
|
||||||
|
import com.onixbyte.helix.repository.AuthorityRepository;
|
||||||
|
import com.onixbyte.helix.shared.CacheName;
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
|
import org.springframework.data.domain.Example;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class AuthorityManager {
|
public class AuthorityManager {
|
||||||
|
|
||||||
private final AuthorityMapper authorityMapper;
|
private final AuthorityMapper authorityMapper;
|
||||||
|
private final AuthorityRepository authorityRepository;
|
||||||
|
|
||||||
public AuthorityManager(AuthorityMapper authorityMapper) {
|
@Autowired
|
||||||
|
public AuthorityManager(AuthorityMapper authorityMapper, AuthorityRepository authorityRepository) {
|
||||||
this.authorityMapper = authorityMapper;
|
this.authorityMapper = authorityMapper;
|
||||||
|
this.authorityRepository = authorityRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Cacheable(cacheNames = CacheName.AUTHORITIES_OF_USER, key = "#userId")
|
@Cacheable(cacheNames = CacheName.AUTHORITIES_OF_USER, key = "#userId")
|
||||||
public List<Authority> queryByUserId(Long userId) {
|
public List<Authority> queryByUserId(Long userId) {
|
||||||
return authorityMapper.selectByUserId(userId);
|
return authorityMapper.selectByUserId(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Page<Authority> selectAll(Pageable pageable, QueryAuthorityWrapper wrapper) {
|
||||||
|
return authorityRepository.findAll(pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean existsByCode(Authority authority) {
|
||||||
|
return authorityRepository.exists(Example.of(Authority
|
||||||
|
.builder()
|
||||||
|
.code(authority.getCode())
|
||||||
|
.build()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Authority save(Authority authority) {
|
||||||
|
return authorityRepository.save(authority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fully updates an existing authority by ID.
|
||||||
|
* <p>
|
||||||
|
* The method loads the target authority, replaces mutable fields ({@code name},
|
||||||
|
* {@code description}, {@code status}), and refreshes {@code updatedAt} to the current time.
|
||||||
|
* The update runs within a transactional context.
|
||||||
|
*
|
||||||
|
* @param id the ID of the authority to update
|
||||||
|
* @param authority the source data carrying new field values
|
||||||
|
* @return the supplied {@link Authority} object
|
||||||
|
* @throws BizException if the target authority does not exist
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public Authority fullUpdateById(Long id, Authority authority) {
|
||||||
|
var updatedAt = LocalDateTime.now();
|
||||||
|
|
||||||
|
var authorityToUpdate = authorityRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new BizException(HttpStatus.NOT_FOUND, MessageName.AUTHORITY_NOT_FOUND, id));
|
||||||
|
|
||||||
|
authorityToUpdate.setName(authority.getName());
|
||||||
|
authorityToUpdate.setDescription(authority.getDescription());
|
||||||
|
authorityToUpdate.setStatus(authority.getStatus());
|
||||||
|
authorityToUpdate.setUpdatedAt(updatedAt);
|
||||||
|
|
||||||
|
return authority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String findAuthorityNameById(Long authorityId) {
|
||||||
|
return authorityRepository.findAuthorityNameById(authorityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteById(Long authorityId) {
|
||||||
|
authorityRepository.deleteById(authorityId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
package com.onixbyte.helix.manager;
|
package com.onixbyte.helix.manager;
|
||||||
|
|
||||||
import com.onixbyte.helix.client.RedisClient;
|
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.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 org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class CaptchaManager {
|
public class CaptchaManager {
|
||||||
|
|||||||
@@ -1,26 +1,77 @@
|
|||||||
package com.onixbyte.helix.manager;
|
package com.onixbyte.helix.manager;
|
||||||
|
|
||||||
import com.onixbyte.helix.domain.entity.Department;
|
import com.onixbyte.helix.domain.entity.Department;
|
||||||
import com.onixbyte.helix.mapper.DepartmentMapper;
|
import com.onixbyte.helix.exception.BizException;
|
||||||
import com.onixbyte.helix.repository.DepartmentRepository;
|
import com.onixbyte.helix.repository.DepartmentRepository;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.*;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class DepartmentManager {
|
public class DepartmentManager {
|
||||||
|
|
||||||
private final DepartmentMapper departmentMapper;
|
|
||||||
private final DepartmentRepository departmentRepository;
|
private final DepartmentRepository departmentRepository;
|
||||||
|
|
||||||
public DepartmentManager(DepartmentMapper departmentMapper, DepartmentRepository departmentRepository) {
|
@Autowired
|
||||||
this.departmentMapper = departmentMapper;
|
public DepartmentManager(DepartmentRepository departmentRepository) {
|
||||||
this.departmentRepository = departmentRepository;
|
this.departmentRepository = departmentRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<Department> selectAll(Pageable pageable) {
|
public Page<Department> selectAll(Pageable pageable) {
|
||||||
return departmentRepository.findAll(pageable);
|
return departmentRepository.findAll(pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Department selectById(Long id) {
|
||||||
|
return departmentRepository.findById(id).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getNextSort(Long parentId) {
|
||||||
|
return Optional.ofNullable(departmentRepository.findMaxSort(parentId)).orElse(0) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Department save(Department department) {
|
||||||
|
return departmentRepository.save(department);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fully updates an existing department by ID.
|
||||||
|
* <p>
|
||||||
|
* The method loads the target department, replaces mutable fields ({@code name},
|
||||||
|
* {@code parentId}, {@code sort}, {@code status}), and refreshes {@code updatedAt} to the
|
||||||
|
* current time.
|
||||||
|
*
|
||||||
|
* @param id the ID of the department to update
|
||||||
|
* @param department the source data carrying new field values
|
||||||
|
* @return the managed and updated {@link Department} entity
|
||||||
|
* @throws BizException if the target department does not exist
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public Department fullUpdateById(Long id, Department department) {
|
||||||
|
var updatedAt = LocalDateTime.now();
|
||||||
|
|
||||||
|
var departmentToEdit = departmentRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new BizException(
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
"Department (ID: %d) to be edited not found.".formatted(department.getId()))
|
||||||
|
);
|
||||||
|
|
||||||
|
departmentToEdit.setName(department.getName());
|
||||||
|
departmentToEdit.setParentId(department.getParentId());
|
||||||
|
departmentToEdit.setSort(department.getSort());
|
||||||
|
departmentToEdit.setStatus(department.getStatus());
|
||||||
|
departmentToEdit.setUpdatedAt(updatedAt);
|
||||||
|
return departmentToEdit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean existsByName(String name) {
|
||||||
|
return departmentRepository.existsByName(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ package com.onixbyte.helix.manager;
|
|||||||
|
|
||||||
import com.onixbyte.helix.domain.entity.Menu;
|
import com.onixbyte.helix.domain.entity.Menu;
|
||||||
import com.onixbyte.helix.mapper.MenuMapper;
|
import com.onixbyte.helix.mapper.MenuMapper;
|
||||||
import org.slf4j.Logger;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class MenuManager {
|
public class MenuManager {
|
||||||
private static final Logger log = LoggerFactory.getLogger(MenuManager.class);
|
|
||||||
private final MenuMapper menuMapper;
|
private final MenuMapper menuMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
public MenuManager(MenuMapper menuMapper) {
|
public MenuManager(MenuMapper menuMapper) {
|
||||||
this.menuMapper = menuMapper;
|
this.menuMapper = menuMapper;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,27 @@
|
|||||||
package com.onixbyte.helix.manager;
|
package com.onixbyte.helix.manager;
|
||||||
|
|
||||||
import com.onixbyte.helix.domain.entity.Position;
|
import com.onixbyte.helix.domain.entity.Position;
|
||||||
import com.onixbyte.helix.mapper.PositionMapper;
|
|
||||||
import com.onixbyte.helix.repository.PositionRepository;
|
import com.onixbyte.helix.repository.PositionRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageImpl;
|
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class PositionManager {
|
public class PositionManager {
|
||||||
|
|
||||||
private final PositionMapper positionMapper;
|
|
||||||
private final PositionRepository positionRepository;
|
private final PositionRepository positionRepository;
|
||||||
|
|
||||||
public PositionManager(PositionMapper positionMapper, PositionRepository positionRepository) {
|
@Autowired
|
||||||
this.positionMapper = positionMapper;
|
public PositionManager(PositionRepository positionRepository) {
|
||||||
this.positionRepository = positionRepository;
|
this.positionRepository = positionRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<Position> selectAll(Pageable pageable) {
|
public Page<Position> selectAll(Pageable pageable) {
|
||||||
return positionRepository.findAll(pageable);
|
return positionRepository.findAll(pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Position selectById(Long id) {
|
||||||
|
return positionRepository.findById(id).orElse(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.onixbyte.helix.manager;
|
||||||
|
|
||||||
|
import com.onixbyte.helix.mapper.RoleAuthorityMapper;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class RoleAuthorityManager {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(RoleAuthorityManager.class);
|
||||||
|
private final RoleAuthorityMapper roleAuthorityMapper;
|
||||||
|
|
||||||
|
public RoleAuthorityManager(RoleAuthorityMapper roleAuthorityMapper) {
|
||||||
|
this.roleAuthorityMapper = roleAuthorityMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteByRoleId(Long roleId) {
|
||||||
|
var affectedRows = roleAuthorityMapper.deleteByRoleId(roleId);
|
||||||
|
log.info("A total of {} authorities linked to Role ID: {} have been successfully cleared.",
|
||||||
|
affectedRows, roleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteByAuthorityId(Long authorityId) {
|
||||||
|
var affectedRows = roleAuthorityMapper.deleteByAuthorityId(authorityId);
|
||||||
|
log.info("The binding between {} authorities and the role has been cleared.", affectedRows);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,13 +5,15 @@ import com.onixbyte.helix.domain.entity.Role;
|
|||||||
import com.onixbyte.helix.exception.BizException;
|
import com.onixbyte.helix.exception.BizException;
|
||||||
import com.onixbyte.helix.mapper.RoleMapper;
|
import com.onixbyte.helix.mapper.RoleMapper;
|
||||||
import com.onixbyte.helix.repository.RoleRepository;
|
import com.onixbyte.helix.repository.RoleRepository;
|
||||||
import org.springframework.data.domain.Example;
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
import org.springframework.data.domain.Page;
|
import com.onixbyte.helix.utils.MessageUtil;
|
||||||
import org.springframework.data.domain.PageImpl;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.*;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@@ -20,20 +22,23 @@ public class RoleManager {
|
|||||||
|
|
||||||
private final RoleMapper roleMapper;
|
private final RoleMapper roleMapper;
|
||||||
private final RoleRepository roleRepository;
|
private final RoleRepository roleRepository;
|
||||||
|
private final MessageUtil messageUtil;
|
||||||
|
|
||||||
public RoleManager(RoleMapper roleMapper, RoleRepository roleRepository) {
|
@Autowired
|
||||||
|
public RoleManager(RoleMapper roleMapper, RoleRepository roleRepository, MessageUtil messageUtil) {
|
||||||
this.roleMapper = roleMapper;
|
this.roleMapper = roleMapper;
|
||||||
this.roleRepository = roleRepository;
|
this.roleRepository = roleRepository;
|
||||||
|
this.messageUtil = messageUtil;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateRoles(List<Long> roleIds) {
|
public void validateRoles(List<Long> roleIds) {
|
||||||
if (!roleMapper.areRolesExisted(roleIds)) {
|
if (!roleMapper.areRolesExisted(roleIds)) {
|
||||||
throw new BizException(HttpStatus.BAD_REQUEST, "Role does not exist in database.");
|
throw new BizException(HttpStatus.BAD_REQUEST, MessageName.ROLE_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Role> getRole(Role example) {
|
public List<Role> getRoles(Role example) {
|
||||||
return roleRepository.findOne(Example.of(example));
|
return roleRepository.findAll(Example.of(example), Sort.by(Sort.Order.asc("id")));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<Role> selectAll(Pageable pageable, QueryRoleWrapper wrapper) {
|
public Page<Role> selectAll(Pageable pageable, QueryRoleWrapper wrapper) {
|
||||||
@@ -42,4 +47,37 @@ public class RoleManager {
|
|||||||
|
|
||||||
return new PageImpl<>(records, pageable, total);
|
return new PageImpl<>(records, pageable, total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Role save(Role role) {
|
||||||
|
return roleRepository.save(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Role> getRoleById(Long id) {
|
||||||
|
return roleRepository.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Role fullUpdateById(Long id, Role role) {
|
||||||
|
var updatedAt = LocalDateTime.now();
|
||||||
|
|
||||||
|
var roleToUpdate = roleRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new BizException(
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
messageUtil.getMessage(MessageName.ROLE_NOT_FOUND, id))
|
||||||
|
);
|
||||||
|
|
||||||
|
roleToUpdate.setName(role.getName());
|
||||||
|
roleToUpdate.setCode(role.getCode());
|
||||||
|
roleToUpdate.setSort(role.getSort());
|
||||||
|
roleToUpdate.setDefaultValue(role.getDefaultValue());
|
||||||
|
roleToUpdate.setDescription(role.getDescription());
|
||||||
|
roleToUpdate.setStatus(role.getStatus());
|
||||||
|
roleToUpdate.setUpdatedAt(updatedAt);
|
||||||
|
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteRole(Long id) {
|
||||||
|
roleRepository.deleteById(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.onixbyte.helix.manager;
|
||||||
|
|
||||||
|
import com.onixbyte.helix.properties.TokenProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SecurityManager {
|
||||||
|
|
||||||
|
private final TokenProperties tokenProperties;
|
||||||
|
|
||||||
|
public SecurityManager(TokenProperties tokenProperties) {
|
||||||
|
this.tokenProperties = tokenProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Duration getTokenValidDuration() {
|
||||||
|
return tokenProperties.validTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.onixbyte.helix.manager;
|
|||||||
|
|
||||||
import com.onixbyte.helix.domain.entity.Setting;
|
import com.onixbyte.helix.domain.entity.Setting;
|
||||||
import com.onixbyte.helix.repository.SettingRepository;
|
import com.onixbyte.helix.repository.SettingRepository;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ public class SettingManager {
|
|||||||
|
|
||||||
private final SettingRepository settingRepository;
|
private final SettingRepository settingRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
public SettingManager(SettingRepository settingRepository) {
|
public SettingManager(SettingRepository settingRepository) {
|
||||||
this.settingRepository = settingRepository;
|
this.settingRepository = settingRepository;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024-2026 OnixByte
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.onixbyte.helix.manager;
|
package com.onixbyte.helix.manager;
|
||||||
|
|
||||||
import com.onixbyte.helix.common.regex.Patterns;
|
import com.onixbyte.helix.common.regex.Patterns;
|
||||||
import com.onixbyte.helix.constant.CacheName;
|
import com.onixbyte.helix.domain.web.request.ResetPasswordRequest;
|
||||||
|
import com.onixbyte.helix.mapper.UserCredentialMapper;
|
||||||
|
import com.onixbyte.helix.shared.CacheName;
|
||||||
import com.onixbyte.helix.domain.database.query.wrapper.QueryUserWrapper;
|
import com.onixbyte.helix.domain.database.query.wrapper.QueryUserWrapper;
|
||||||
import com.onixbyte.helix.domain.entity.User;
|
import com.onixbyte.helix.domain.entity.User;
|
||||||
import com.onixbyte.helix.domain.web.response.UserDetailResponse;
|
import com.onixbyte.helix.domain.web.response.UserDetailResponse;
|
||||||
@@ -9,7 +33,9 @@ import com.onixbyte.helix.exception.BizException;
|
|||||||
import com.onixbyte.helix.mapper.UserMapper;
|
import com.onixbyte.helix.mapper.UserMapper;
|
||||||
import com.onixbyte.helix.repository.UserRepository;
|
import com.onixbyte.helix.repository.UserRepository;
|
||||||
import com.onixbyte.region.Region;
|
import com.onixbyte.region.Region;
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.cache.annotation.CachePut;
|
import org.springframework.cache.annotation.CachePut;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.data.domain.Example;
|
import org.springframework.data.domain.Example;
|
||||||
@@ -29,11 +55,19 @@ public class UserManager {
|
|||||||
private final UserMapper userMapper;
|
private final UserMapper userMapper;
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
private final UserCredentialMapper userCredentialMapper;
|
||||||
|
|
||||||
public UserManager(UserMapper userMapper, UserRepository userRepository, PasswordEncoder passwordEncoder) {
|
@Autowired
|
||||||
|
public UserManager(
|
||||||
|
UserMapper userMapper,
|
||||||
|
UserRepository userRepository,
|
||||||
|
PasswordEncoder passwordEncoder,
|
||||||
|
UserCredentialMapper userCredentialMapper
|
||||||
|
) {
|
||||||
this.userMapper = userMapper;
|
this.userMapper = userMapper;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.passwordEncoder = passwordEncoder;
|
this.passwordEncoder = passwordEncoder;
|
||||||
|
this.userCredentialMapper = userCredentialMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,12 +113,7 @@ public class UserManager {
|
|||||||
@Transactional(rollbackFor = Throwable.class)
|
@Transactional(rollbackFor = Throwable.class)
|
||||||
public User updateUser(User user) {
|
public User updateUser(User user) {
|
||||||
var userToUpdate = userRepository.findById(user.getId())
|
var userToUpdate = userRepository.findById(user.getId())
|
||||||
.orElseThrow(() -> new BizException(HttpStatus.BAD_REQUEST, "找不到 ID 为" + user.getId() + "的用户信息"));
|
.orElseThrow(() -> new BizException(HttpStatus.BAD_REQUEST, MessageName.USER_NOT_FOUND, user.getId()));
|
||||||
|
|
||||||
Optional.ofNullable(user.getPassword())
|
|
||||||
.filter(StringUtils::isNotBlank)
|
|
||||||
.map(passwordEncoder::encode)
|
|
||||||
.ifPresent(userToUpdate::setPassword);
|
|
||||||
|
|
||||||
Optional.ofNullable(user.getFullName())
|
Optional.ofNullable(user.getFullName())
|
||||||
.filter(StringUtils::isNotBlank)
|
.filter(StringUtils::isNotBlank)
|
||||||
@@ -120,4 +149,12 @@ public class UserManager {
|
|||||||
|
|
||||||
return userToUpdate;
|
return userToUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(rollbackFor = Throwable.class)
|
||||||
|
public void updatePasswordById(Long id, ResetPasswordRequest request) {
|
||||||
|
userCredentialMapper.updateUserCredential(
|
||||||
|
id,
|
||||||
|
passwordEncoder.encode(request.password())
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package com.onixbyte.helix.manager;
|
package com.onixbyte.helix.manager;
|
||||||
|
|
||||||
import com.onixbyte.helix.domain.entity.UserRole;
|
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 com.onixbyte.helix.repository.UserRoleRepository;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -15,11 +13,10 @@ import java.util.List;
|
|||||||
public class UserRoleManager {
|
public class UserRoleManager {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(UserRoleManager.class);
|
private static final Logger log = LoggerFactory.getLogger(UserRoleManager.class);
|
||||||
private final UserRoleMapper userRoleMapper;
|
|
||||||
private final UserRoleRepository userRoleRepository;
|
private final UserRoleRepository userRoleRepository;
|
||||||
|
|
||||||
public UserRoleManager(UserRoleMapper userRoleMapper, UserRoleRepository userRoleRepository) {
|
@Autowired
|
||||||
this.userRoleMapper = userRoleMapper;
|
public UserRoleManager(UserRoleRepository userRoleRepository) {
|
||||||
this.userRoleRepository = userRoleRepository;
|
this.userRoleRepository = userRoleRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,4 +28,9 @@ public class UserRoleManager {
|
|||||||
var affectedRows = userRoleRepository.deleteByUserId(userId);
|
var affectedRows = userRoleRepository.deleteByUserId(userId);
|
||||||
log.info("用户 {} 的角色关联被全部移除(共 {} 条)。", userId, affectedRows);
|
log.info("用户 {} 的角色关联被全部移除(共 {} 条)。", userId, affectedRows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void deleteByRoleId(Long roleId) {
|
||||||
|
var affectedRows = userRoleRepository.deleteByRoleId(roleId);
|
||||||
|
log.info("角色 {} 的用户关联被全部移除(共 {} 条)。", roleId, affectedRows);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface RoleAuthorityMapper {
|
||||||
|
|
||||||
|
int deleteByRoleId(@Param("roleId") Long roleId);
|
||||||
|
|
||||||
|
int deleteByAuthorityId(@Param("authorityId") Long authorityId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024-2026 OnixByte
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.onixbyte.helix.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface UserCredentialMapper {
|
||||||
|
|
||||||
|
int updateUserCredential(
|
||||||
|
@Param("userId") Long userId,
|
||||||
|
@Param("encodedPassword") String encodedPassword
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
package com.onixbyte.helix.properties;
|
package com.onixbyte.helix.properties;
|
||||||
|
|
||||||
|
import com.onixbyte.helix.enumeration.ApplicationMode;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||||
|
|
||||||
@ConfigurationProperties(prefix = "app.common")
|
@ConfigurationProperties(prefix = "app.common")
|
||||||
public record ApplicationProperties(
|
public record ApplicationProperties(
|
||||||
@DefaultValue("default@helix.onixbyte.dev") String defaultEmail
|
@DefaultValue("default@helix.onixbyte.dev") String defaultEmail,
|
||||||
|
@DefaultValue("helix.onixbyte.dev") String externalHost,
|
||||||
|
@DefaultValue("prod") ApplicationMode mode
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.onixbyte.helix.properties;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||||
|
|
||||||
|
@ConfigurationProperties(prefix = "app.auth")
|
||||||
|
public record AuthenticationProperties(
|
||||||
|
@DefaultValue("true") Boolean sslEnabled,
|
||||||
|
@DefaultValue("true") Boolean secureCookieEnabled
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -2,8 +2,16 @@ package com.onixbyte.helix.repository;
|
|||||||
|
|
||||||
import com.onixbyte.helix.domain.entity.Authority;
|
import com.onixbyte.helix.domain.entity.Authority;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface AuthorityRepository extends JpaRepository<Authority, Long> {
|
public interface AuthorityRepository extends JpaRepository<Authority, Long> {
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
select a.name
|
||||||
|
from Authority a
|
||||||
|
where a.id = :authorityId
|
||||||
|
""")
|
||||||
|
String findAuthorityNameById(Long authorityId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,19 @@ package com.onixbyte.helix.repository;
|
|||||||
|
|
||||||
import com.onixbyte.helix.domain.entity.Department;
|
import com.onixbyte.helix.domain.entity.Department;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface DepartmentRepository extends JpaRepository<Department, Long> {
|
public interface DepartmentRepository extends JpaRepository<Department, Long> {
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
select max(d.sort)
|
||||||
|
from Department d
|
||||||
|
where (:parentId is null and d.parentId is null)
|
||||||
|
or d.parentId = :parentId
|
||||||
|
""")
|
||||||
|
Integer findMaxSort(Long parentId);
|
||||||
|
|
||||||
|
boolean existsByName(String name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,13 @@ package com.onixbyte.helix.repository;
|
|||||||
import com.onixbyte.helix.domain.entity.RoleAuthority;
|
import com.onixbyte.helix.domain.entity.RoleAuthority;
|
||||||
import com.onixbyte.helix.domain.entity.embeddable.RoleAuthorityId;
|
import com.onixbyte.helix.domain.entity.embeddable.RoleAuthorityId;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface RoleAuthorityRepository extends JpaRepository<RoleAuthority, RoleAuthorityId> {
|
public interface RoleAuthorityRepository extends JpaRepository<RoleAuthority, RoleAuthorityId> {
|
||||||
|
@Modifying
|
||||||
|
@Query("DELETE FROM RoleAuthority ra WHERE ra.id.roleId = :roleId")
|
||||||
|
int deleteByRoleId(Long roleId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.onixbyte.helix.repository;
|
||||||
|
|
||||||
|
import com.onixbyte.helix.domain.entity.UserCredential;
|
||||||
|
import com.onixbyte.helix.domain.entity.embeddable.UserCredentialId;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface UserCredentialRepository extends JpaRepository<UserCredential, UserCredentialId> {
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.onixbyte.helix.repository;
|
|
||||||
|
|
||||||
import com.onixbyte.helix.domain.entity.UserIdentity;
|
|
||||||
import com.onixbyte.helix.domain.entity.embeddable.UserIdentityId;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
|
|
||||||
@Repository
|
|
||||||
public interface UserIdentityRepository extends JpaRepository<UserIdentity, UserIdentityId> {
|
|
||||||
}
|
|
||||||
@@ -13,4 +13,8 @@ public interface UserRoleRepository extends JpaRepository<UserRole, UserRoleId>
|
|||||||
@Modifying
|
@Modifying
|
||||||
@Query("DELETE FROM UserRole ur WHERE ur.id.userId = :userId")
|
@Query("DELETE FROM UserRole ur WHERE ur.id.userId = :userId")
|
||||||
int deleteByUserId(Long userId);
|
int deleteByUserId(Long userId);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query("DELETE FROM UserRole ur WHERE ur.id.roleId = :roleId")
|
||||||
|
int deleteByRoleId(Long roleId);
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -2,7 +2,6 @@ package com.onixbyte.helix.security.entrypoint;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.onixbyte.helix.domain.web.response.BizExceptionResponse;
|
import com.onixbyte.helix.domain.web.response.BizExceptionResponse;
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
|||||||
+26
-6
@@ -1,12 +1,18 @@
|
|||||||
package com.onixbyte.helix.security.provider;
|
package com.onixbyte.helix.security.provider;
|
||||||
|
|
||||||
import com.onixbyte.helix.domain.entity.Authority;
|
import com.onixbyte.helix.domain.entity.Authority;
|
||||||
|
import com.onixbyte.helix.domain.entity.UserCredential;
|
||||||
|
import com.onixbyte.helix.enumeration.CredentialProvider;
|
||||||
import com.onixbyte.helix.exception.BizException;
|
import com.onixbyte.helix.exception.BizException;
|
||||||
import com.onixbyte.helix.manager.AuthorityManager;
|
import com.onixbyte.helix.manager.AuthorityManager;
|
||||||
import com.onixbyte.helix.manager.UserManager;
|
import com.onixbyte.helix.manager.UserManager;
|
||||||
|
import com.onixbyte.helix.repository.UserCredentialRepository;
|
||||||
import com.onixbyte.helix.security.authentication.UsernamePasswordAuthentication;
|
import com.onixbyte.helix.security.authentication.UsernamePasswordAuthentication;
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Example;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@@ -23,34 +29,48 @@ public class UsernamePasswordAuthenticationProvider implements AuthenticationPro
|
|||||||
private final UserManager userManager;
|
private final UserManager userManager;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
private final AuthorityManager authorityManager;
|
private final AuthorityManager authorityManager;
|
||||||
|
private final UserCredentialRepository userCredentialRepository;
|
||||||
|
|
||||||
public UsernamePasswordAuthenticationProvider(UserManager userManager, PasswordEncoder passwordEncoder, AuthorityManager authorityManager) {
|
@Autowired
|
||||||
|
public UsernamePasswordAuthenticationProvider(
|
||||||
|
UserManager userManager,
|
||||||
|
PasswordEncoder passwordEncoder,
|
||||||
|
AuthorityManager authorityManager,
|
||||||
|
UserCredentialRepository userCredentialRepository
|
||||||
|
) {
|
||||||
this.userManager = userManager;
|
this.userManager = userManager;
|
||||||
this.passwordEncoder = passwordEncoder;
|
this.passwordEncoder = passwordEncoder;
|
||||||
this.authorityManager = authorityManager;
|
this.authorityManager = authorityManager;
|
||||||
|
this.userCredentialRepository = userCredentialRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||||
if (!(authentication instanceof UsernamePasswordAuthentication usernamePasswordAuthentication)) {
|
if (!(authentication instanceof UsernamePasswordAuthentication usernamePasswordAuthentication)) {
|
||||||
throw new BizException(HttpStatus.INTERNAL_SERVER_ERROR, "用户认证失败,请稍后再试。");
|
throw new BizException(HttpStatus.INTERNAL_SERVER_ERROR, MessageName.AUTH_PROVIDER_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get user from database
|
// get user from database
|
||||||
var user = userManager.selectByUsername(usernamePasswordAuthentication.getPrincipal());
|
var user = userManager.selectByUsername(usernamePasswordAuthentication.getPrincipal());
|
||||||
if (Objects.isNull(user)) {
|
if (Objects.isNull(user)) {
|
||||||
log.error("User {} is trying to authenticate but no user found.", usernamePasswordAuthentication.getPrincipal());
|
log.error("User {} is trying to authenticate but no user found.", usernamePasswordAuthentication.getPrincipal());
|
||||||
throw new BizException(HttpStatus.UNAUTHORIZED, "用户名或密码错误。");
|
throw new BizException(HttpStatus.UNAUTHORIZED, MessageName.AUTH_PROVIDER_BAD_CREDENTIALS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get user credentials from database
|
||||||
|
var userCredentials = userCredentialRepository.findOne(Example.of(UserCredential.builder()
|
||||||
|
.provider(CredentialProvider.LOCAL)
|
||||||
|
.userId(user.getId())
|
||||||
|
.build()))
|
||||||
|
.orElseThrow(() -> new BizException(HttpStatus.UNAUTHORIZED, MessageName.AUTH_PROVIDER_PASSWORD_NOT_CONFIGURED));
|
||||||
|
|
||||||
// validate password
|
// validate password
|
||||||
if (!passwordEncoder.matches(usernamePasswordAuthentication.getCredentials(), user.getPassword())) {
|
if (!passwordEncoder.matches(usernamePasswordAuthentication.getCredentials(), userCredentials.getCredential())) {
|
||||||
log.error("User {} is trying to authenticate but password is incorrect.", usernamePasswordAuthentication.getPrincipal());
|
log.error("User {} is trying to authenticate but password is incorrect.", usernamePasswordAuthentication.getPrincipal());
|
||||||
throw new BizException(HttpStatus.UNAUTHORIZED, "用户名或密码错误。");
|
throw new BizException(HttpStatus.UNAUTHORIZED, MessageName.AUTH_PROVIDER_BAD_CREDENTIALS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// erase credentials
|
// erase credentials
|
||||||
user.setPassword(null);
|
|
||||||
usernamePasswordAuthentication.eraseCredentials();
|
usernamePasswordAuthentication.eraseCredentials();
|
||||||
|
|
||||||
// get authorities
|
// get authorities
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import com.onixbyte.helix.domain.entity.Asset;
|
|||||||
import com.onixbyte.helix.exception.BizException;
|
import com.onixbyte.helix.exception.BizException;
|
||||||
import com.onixbyte.helix.manager.AssetManager;
|
import com.onixbyte.helix.manager.AssetManager;
|
||||||
import com.onixbyte.helix.properties.AssetProperties;
|
import com.onixbyte.helix.properties.AssetProperties;
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
import com.onixbyte.helix.utils.SecurityUtil;
|
import com.onixbyte.helix.utils.SecurityUtil;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@@ -34,6 +36,7 @@ public class AssetService {
|
|||||||
private final S3Client s3Client;
|
private final S3Client s3Client;
|
||||||
private final AssetManager assetManager;
|
private final AssetManager assetManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
public AssetService(
|
public AssetService(
|
||||||
AssetProperties assetProperties,
|
AssetProperties assetProperties,
|
||||||
S3Client s3Client,
|
S3Client s3Client,
|
||||||
@@ -60,7 +63,7 @@ public class AssetService {
|
|||||||
if (Objects.isNull(prefix) || prefix.isBlank() || prefix.startsWith("/") || prefix.startsWith("..")) {
|
if (Objects.isNull(prefix) || prefix.isBlank() || prefix.startsWith("/") || prefix.startsWith("..")) {
|
||||||
throw new BizException(
|
throw new BizException(
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
"Prefix must not be empty, and should not start with '/' or '..'."
|
MessageName.ASSET_INVALID_PREFIX
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +109,7 @@ public class AssetService {
|
|||||||
var asset = assetManager.queryByAssetId(assetId);
|
var asset = assetManager.queryByAssetId(assetId);
|
||||||
|
|
||||||
if (!Objects.equals(currentUser.getId(), asset.getUploadBy())) {
|
if (!Objects.equals(currentUser.getId(), asset.getUploadBy())) {
|
||||||
throw new BizException(HttpStatus.FORBIDDEN, "You are not able to delete an asset that is not uploaded by you.");
|
throw new BizException(HttpStatus.FORBIDDEN, MessageName.ASSET_DELETE_FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
assetManager.deleteById(assetId);
|
assetManager.deleteById(assetId);
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
package com.onixbyte.helix.service;
|
package com.onixbyte.helix.service;
|
||||||
|
|
||||||
import com.onixbyte.helix.client.TokenClient;
|
|
||||||
import com.onixbyte.helix.constant.SettingName;
|
|
||||||
import com.onixbyte.helix.domain.entity.Setting;
|
import com.onixbyte.helix.domain.entity.Setting;
|
||||||
import com.onixbyte.helix.domain.web.request.UsernamePasswordLoginRequest;
|
import com.onixbyte.helix.domain.entity.User;
|
||||||
import com.onixbyte.helix.domain.web.response.LoginSuccessResponse;
|
import com.onixbyte.helix.domain.web.request.LoginRequest;
|
||||||
import com.onixbyte.helix.exception.BizException;
|
import com.onixbyte.helix.exception.BizException;
|
||||||
|
import com.onixbyte.helix.manager.ApplicationManager;
|
||||||
import com.onixbyte.helix.manager.CaptchaManager;
|
import com.onixbyte.helix.manager.CaptchaManager;
|
||||||
|
import com.onixbyte.helix.manager.SecurityManager;
|
||||||
import com.onixbyte.helix.manager.SettingManager;
|
import com.onixbyte.helix.manager.SettingManager;
|
||||||
import com.onixbyte.helix.security.authentication.UsernamePasswordAuthentication;
|
import com.onixbyte.helix.security.authentication.UsernamePasswordAuthentication;
|
||||||
|
import com.onixbyte.helix.shared.MessageName;
|
||||||
|
import com.onixbyte.helix.shared.SettingName;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseCookie;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@@ -26,20 +30,22 @@ public class AuthService {
|
|||||||
|
|
||||||
private final CaptchaManager captchaManager;
|
private final CaptchaManager captchaManager;
|
||||||
private final AuthenticationManager authenticationManager;
|
private final AuthenticationManager authenticationManager;
|
||||||
private final TokenClient tokenClient;
|
|
||||||
private final SettingManager settingManager;
|
private final SettingManager settingManager;
|
||||||
|
private final SecurityManager securityManager;
|
||||||
|
private final ApplicationManager applicationManager;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AuthService(
|
public AuthService(
|
||||||
CaptchaManager captchaManager,
|
CaptchaManager captchaManager,
|
||||||
AuthenticationManager authenticationManager,
|
AuthenticationManager authenticationManager,
|
||||||
TokenClient tokenClient,
|
SettingManager settingManager,
|
||||||
SettingManager settingManager
|
SecurityManager securityManager,
|
||||||
) {
|
ApplicationManager applicationManager) {
|
||||||
this.captchaManager = captchaManager;
|
this.captchaManager = captchaManager;
|
||||||
this.authenticationManager = authenticationManager;
|
this.authenticationManager = authenticationManager;
|
||||||
this.tokenClient = tokenClient;
|
|
||||||
this.settingManager = settingManager;
|
this.settingManager = settingManager;
|
||||||
|
this.securityManager = securityManager;
|
||||||
|
this.applicationManager = applicationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,7 +55,7 @@ public class AuthService {
|
|||||||
* @return user information and user identity token
|
* @return user information and user identity token
|
||||||
* @throws BizException if the user does not exist, or the username and password are incorrect
|
* @throws BizException if the user does not exist, or the username and password are incorrect
|
||||||
*/
|
*/
|
||||||
public LoginSuccessResponse login(UsernamePasswordLoginRequest request) {
|
public User login(LoginRequest request) {
|
||||||
var captchaEnabled = Optional.ofNullable(settingManager.getSettingByName(SettingName.CAPTCHA_ENABLED))
|
var captchaEnabled = Optional.ofNullable(settingManager.getSettingByName(SettingName.CAPTCHA_ENABLED))
|
||||||
.map(Setting::asBoolean)
|
.map(Setting::asBoolean)
|
||||||
.orElse(false);
|
.orElse(false);
|
||||||
@@ -58,10 +64,10 @@ public class AuthService {
|
|||||||
var rawCaptcha = captchaManager.getCaptcha(uuid);
|
var rawCaptcha = captchaManager.getCaptcha(uuid);
|
||||||
|
|
||||||
if (Objects.isNull(rawCaptcha) || rawCaptcha.isBlank()) {
|
if (Objects.isNull(rawCaptcha) || rawCaptcha.isBlank()) {
|
||||||
throw new BizException(HttpStatus.BAD_REQUEST, "未找到验证码");
|
throw new BizException(HttpStatus.BAD_REQUEST, MessageName.AUTH_LOGIN_CAPTCHA_NOT_FOUND);
|
||||||
}
|
}
|
||||||
if (!rawCaptcha.equalsIgnoreCase(request.captcha())) {
|
if (!rawCaptcha.equalsIgnoreCase(request.captcha())) {
|
||||||
throw new BizException(HttpStatus.BAD_REQUEST, "验证码错误");
|
throw new BizException(HttpStatus.BAD_REQUEST, MessageName.AUTH_LOGIN_CAPTCHA_INCORRECT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,12 +79,10 @@ public class AuthService {
|
|||||||
_authentication.getClass()
|
_authentication.getClass()
|
||||||
);
|
);
|
||||||
throw new BizException(HttpStatus.INTERNAL_SERVER_ERROR,
|
throw new BizException(HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
"Cannot perform login due to server crashes.");
|
MessageName.AUTH_LOGIN_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
var token = tokenClient.generateToken(authentication.getDetails());
|
return authentication.getDetails();
|
||||||
|
|
||||||
return new LoginSuccessResponse(token, authentication.getDetails());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,4 +95,45 @@ public class AuthService {
|
|||||||
.map(Setting::asBoolean)
|
.map(Setting::asBoolean)
|
||||||
.orElse(false);
|
.orElse(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ResponseCookie buildCookie(String cookieName, String value) {
|
||||||
|
return buildCookieInternal(cookieName, value, securityManager.getTokenValidDuration())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResponseCookie buildCookie(String cookieName, String value, Duration validDuration) {
|
||||||
|
return buildCookieInternal(cookieName, value, validDuration)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a response cookie builder with specified name, value and valid duration.
|
||||||
|
*
|
||||||
|
* @param name name of the cookie
|
||||||
|
* @param value value of the cookie
|
||||||
|
* @param validDuration valid duration of the cookie
|
||||||
|
* @return cookie builder
|
||||||
|
*/
|
||||||
|
protected ResponseCookie.ResponseCookieBuilder buildCookieInternal(
|
||||||
|
String name,
|
||||||
|
String value,
|
||||||
|
Duration validDuration
|
||||||
|
) {
|
||||||
|
var applicationMode = applicationManager.getApplicationMode();
|
||||||
|
|
||||||
|
var cookieBuilder = ResponseCookie.from(name, value)
|
||||||
|
.maxAge(securityManager.getTokenValidDuration())
|
||||||
|
.secure(true)
|
||||||
|
.maxAge(validDuration)
|
||||||
|
.path("/");
|
||||||
|
|
||||||
|
return switch (applicationMode) {
|
||||||
|
case PRODUCTION -> cookieBuilder
|
||||||
|
.httpOnly(true)
|
||||||
|
.sameSite("LAX");
|
||||||
|
case DEVELOPMENT -> cookieBuilder
|
||||||
|
.sameSite("NONE");
|
||||||
|
case null -> cookieBuilder;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user