init: SR System — Helpdesk & ITAM Portal
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
.gradle
|
||||
build
|
||||
*.md
|
||||
.gradle/
|
||||
build/
|
||||
@@ -0,0 +1,13 @@
|
||||
FROM eclipse-temurin:17-jdk AS build
|
||||
WORKDIR /app
|
||||
COPY gradlew build.gradle settings.gradle ./
|
||||
COPY gradle gradle
|
||||
RUN chmod +x gradlew && ./gradlew dependencies --no-daemon
|
||||
COPY src src
|
||||
RUN ./gradlew bootJar --no-daemon
|
||||
|
||||
FROM eclipse-temurin:17-jre
|
||||
WORKDIR /app
|
||||
COPY --from=build /app/build/libs/*.jar app.jar
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["java", "-jar", "app.jar"]
|
||||
@@ -0,0 +1,45 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.2.0'
|
||||
id 'io.spring.dependency-management' version '1.1.4'
|
||||
}
|
||||
|
||||
group = 'com'
|
||||
version = '0.0.1-SNAPSHOT'
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
configurations {
|
||||
compileOnly {
|
||||
extendsFrom annotationProcessor
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
|
||||
implementation 'org.flywaydb:flyway-core'
|
||||
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
|
||||
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5'
|
||||
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-batch'
|
||||
implementation 'org.postgresql:postgresql'
|
||||
implementation 'org.projectlombok:lombok:1.18.38'
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.38'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testImplementation 'org.springframework.security:spring-security-test'
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
|
||||
networkTimeout=10000
|
||||
retries=0
|
||||
retryBackOffMs=500
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
Vendored
+82
@@ -0,0 +1,82 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables, and ensure extensions are enabled
|
||||
setlocal EnableExtensions
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
|
||||
@rem which allows us to clear the local environment before executing the java command
|
||||
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
|
||||
|
||||
:exitWithErrorLevel
|
||||
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
|
||||
"%COMSPEC%" /c exit %ERRORLEVEL%
|
||||
@@ -0,0 +1 @@
|
||||
rootProject.name = 'sr-system-backend'
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.srsystem;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SrsystemApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SrsystemApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.srsystem.batch;
|
||||
|
||||
import com.srsystem.dto.NotificationEvent;
|
||||
import com.srsystem.entity.Ticket;
|
||||
import com.srsystem.entity.enums.TicketStatus;
|
||||
import com.srsystem.listener.NotificationPublisher;
|
||||
import com.srsystem.repository.TicketRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class SlaExpirationJob {
|
||||
|
||||
private final TicketRepository ticketRepository;
|
||||
private final NotificationPublisher notificationPublisher;
|
||||
|
||||
private static final List<TicketStatus> EXCLUDED_STATUSES = List.of(TicketStatus.RESOLVED, TicketStatus.CLOSED);
|
||||
|
||||
@Transactional
|
||||
public int processExpiredTickets() {
|
||||
List<Ticket> expiredTickets = ticketRepository
|
||||
.findBySlaWarnFlagAndStatusNotIn('N', EXCLUDED_STATUSES);
|
||||
|
||||
int count = 0;
|
||||
for (Ticket ticket : expiredTickets) {
|
||||
if (ticket.getDueDate() != null && ticket.getDueDate().isBefore(LocalDateTime.now())) {
|
||||
ticket.setSlaWarnFlag('Y');
|
||||
ticketRepository.save(ticket);
|
||||
|
||||
if (ticket.getRequester() != null) {
|
||||
notificationPublisher.publish(new NotificationEvent(
|
||||
"SLA_WARNING",
|
||||
"SLA Expired: Ticket #" + ticket.getTicketId(),
|
||||
"Ticket \"" + ticket.getTitle() + "\" has exceeded its SLA deadline.",
|
||||
"/issues/" + ticket.getTicketId(),
|
||||
ticket.getRequester().getEmpId()
|
||||
));
|
||||
}
|
||||
|
||||
if (ticket.getAssignee() != null) {
|
||||
notificationPublisher.publish(new NotificationEvent(
|
||||
"SLA_WARNING",
|
||||
"SLA Expired: Ticket #" + ticket.getTicketId(),
|
||||
"Assigned ticket \"" + ticket.getTitle() + "\" has exceeded its SLA deadline.",
|
||||
"/issues/" + ticket.getTicketId(),
|
||||
ticket.getAssignee().getEmpId()
|
||||
));
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
log.info("SLA Expiration Job: {} tickets marked as overdue", count);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.srsystem.batch;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class SlaScheduler {
|
||||
|
||||
private final SlaExpirationJob slaExpirationJob;
|
||||
|
||||
@Scheduled(cron = "0 0 * * * *")
|
||||
public void runSlaCheck() {
|
||||
log.info("SLA Scheduler: Starting hourly SLA expiration check");
|
||||
try {
|
||||
int processed = slaExpirationJob.processExpiredTickets();
|
||||
log.info("SLA Scheduler: Completed. {} tickets processed.", processed);
|
||||
} catch (Exception e) {
|
||||
log.error("SLA Scheduler: Error during SLA check", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.srsystem.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
public class AsyncConfig {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.srsystem.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
public class BatchConfig {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.srsystem.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||
|
||||
@Configuration
|
||||
@EnableJpaAuditing
|
||||
public class JpaConfig {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.srsystem.config;
|
||||
|
||||
import com.srsystem.config.jwt.JwtAuthenticationFilter;
|
||||
import com.srsystem.config.jwt.JwtTokenProvider;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity
|
||||
@RequiredArgsConstructor
|
||||
public class SecurityConfig {
|
||||
|
||||
private final JwtTokenProvider jwtTokenProvider;
|
||||
private final JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/api/v1/auth/**", "/api/v1/health", "/api/v1/wake").permitAll()
|
||||
.requestMatchers("/api/v1/**").authenticated()
|
||||
.anyRequest().permitAll()
|
||||
)
|
||||
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BCryptPasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() {
|
||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
||||
provider.setPasswordEncoder(passwordEncoder());
|
||||
return new ProviderManager(provider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
configuration.setAllowedOriginPatterns(List.of("*"));
|
||||
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
|
||||
configuration.setAllowedHeaders(List.of("*"));
|
||||
configuration.setAllowCredentials(true);
|
||||
configuration.setMaxAge(3600L);
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
return source;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.srsystem.config;
|
||||
|
||||
import com.srsystem.config.jwt.CurrentUserArgumentResolver;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
private final CurrentUserArgumentResolver currentUserArgumentResolver;
|
||||
|
||||
public WebConfig(CurrentUserArgumentResolver currentUserArgumentResolver) {
|
||||
this.currentUserArgumentResolver = currentUserArgumentResolver;
|
||||
}
|
||||
|
||||
@Value("${file.upload-dir:./uploads}")
|
||||
private String uploadDir;
|
||||
|
||||
@Override
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||
resolvers.add(currentUserArgumentResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("/uploads/**")
|
||||
.addResourceLocations("file:" + uploadDir + "/");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.srsystem.config.jwt;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface CurrentUser {
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.srsystem.config.jwt;
|
||||
|
||||
import com.srsystem.entity.Employee;
|
||||
import com.srsystem.service.AuthService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final AuthService authService;
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(CurrentUser.class) &&
|
||||
parameter.getParameterType().equals(Employee.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication == null || !authentication.isAuthenticated()) {
|
||||
log.warn("No authentication found");
|
||||
return null;
|
||||
}
|
||||
String loginId = authentication.getName();
|
||||
log.info("Resolving current user for loginId: {}", loginId);
|
||||
Employee emp = authService.getEmployeeByLoginId(loginId);
|
||||
if (emp == null) {
|
||||
log.warn("Employee not found for loginId: {}", loginId);
|
||||
return null;
|
||||
}
|
||||
log.info("Found employee: empId={}, name={}", emp.getEmpId(), emp.getName());
|
||||
return emp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.srsystem.config.jwt;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
private final JwtTokenProvider jwtTokenProvider;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
String token = resolveToken(request);
|
||||
|
||||
if (StringUtils.hasText(token) && jwtTokenProvider.validateToken(token)) {
|
||||
String loginId = jwtTokenProvider.getLoginId(token);
|
||||
String role = jwtTokenProvider.getRole(token);
|
||||
|
||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
|
||||
loginId,
|
||||
null,
|
||||
Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role))
|
||||
);
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
log.debug("Security context set for user: {}", loginId);
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private String resolveToken(HttpServletRequest request) {
|
||||
String bearerToken = request.getHeader("Authorization");
|
||||
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
|
||||
return bearerToken.substring(7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.srsystem.config.jwt;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class JwtTokenProvider {
|
||||
|
||||
private final SecretKey secretKey;
|
||||
private final long accessTokenExpiration;
|
||||
private final long refreshTokenExpiration;
|
||||
|
||||
public JwtTokenProvider(
|
||||
@Value("${jwt.secret}") String secret,
|
||||
@Value("${jwt.access-token-expiration}") long accessTokenExpiration,
|
||||
@Value("${jwt.refresh-token-expiration}") long refreshTokenExpiration) {
|
||||
this.secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
|
||||
this.accessTokenExpiration = accessTokenExpiration;
|
||||
this.refreshTokenExpiration = refreshTokenExpiration;
|
||||
}
|
||||
|
||||
public String generateAccessToken(String loginId, String role) {
|
||||
Date now = new Date();
|
||||
Date expiryDate = new Date(now.getTime() + accessTokenExpiration);
|
||||
|
||||
return Jwts.builder()
|
||||
.subject(loginId)
|
||||
.claim("role", role)
|
||||
.issuedAt(now)
|
||||
.expiration(expiryDate)
|
||||
.signWith(secretKey)
|
||||
.compact();
|
||||
}
|
||||
|
||||
public String generateRefreshToken(String loginId) {
|
||||
Date now = new Date();
|
||||
Date expiryDate = new Date(now.getTime() + refreshTokenExpiration);
|
||||
|
||||
return Jwts.builder()
|
||||
.subject(loginId)
|
||||
.issuedAt(now)
|
||||
.expiration(expiryDate)
|
||||
.signWith(secretKey)
|
||||
.compact();
|
||||
}
|
||||
|
||||
public String getLoginId(String token) {
|
||||
return getClaims(token).getSubject();
|
||||
}
|
||||
|
||||
public String getRole(String token) {
|
||||
return getClaims(token).get("role", String.class);
|
||||
}
|
||||
|
||||
public boolean validateToken(String token) {
|
||||
try {
|
||||
getClaims(token);
|
||||
return true;
|
||||
} catch (ExpiredJwtException e) {
|
||||
log.warn("Expired JWT token: {}", e.getMessage());
|
||||
} catch (UnsupportedJwtException e) {
|
||||
log.warn("Unsupported JWT token: {}", e.getMessage());
|
||||
} catch (MalformedJwtException e) {
|
||||
log.warn("Malformed JWT token: {}", e.getMessage());
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Invalid JWT token: {}", e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Claims getClaims(String token) {
|
||||
return Jwts.parser()
|
||||
.verifyWith(secretKey)
|
||||
.build()
|
||||
.parseSignedClaims(token)
|
||||
.getPayload();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.srsystem.controller;
|
||||
|
||||
import com.srsystem.dto.ApiResponse;
|
||||
import com.srsystem.dto.LoginRequest;
|
||||
import com.srsystem.dto.LoginResponse;
|
||||
import com.srsystem.dto.RefreshTokenRequest;
|
||||
import com.srsystem.service.AuthService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Tag(name = "Authentication", description = "인증 관련 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/auth")
|
||||
@RequiredArgsConstructor
|
||||
public class AuthController {
|
||||
|
||||
private final AuthService authService;
|
||||
|
||||
@Operation(summary = "로그인", description = "아이디와 비밀번호로 로그인하여 액세스 토큰과 리프레시 토큰을 발급받습니다")
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<ApiResponse<LoginResponse>> login(@Valid @RequestBody LoginRequest request) {
|
||||
LoginResponse response = authService.login(request);
|
||||
return ResponseEntity.ok(ApiResponse.ok(response));
|
||||
}
|
||||
|
||||
@Operation(summary = "토큰 갱신", description = "리프레시 토큰을 사용하여 새로운 액세스 토큰을 발급받습니다")
|
||||
@PostMapping("/refresh")
|
||||
public ResponseEntity<ApiResponse<LoginResponse>> refresh(@RequestBody RefreshTokenRequest request) {
|
||||
LoginResponse response = authService.refresh(request.refreshToken());
|
||||
return ResponseEntity.ok(ApiResponse.ok(response));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.srsystem.controller;
|
||||
|
||||
import com.srsystem.config.jwt.CurrentUser;
|
||||
import com.srsystem.dto.ApiResponse;
|
||||
import com.srsystem.dto.CalendarEvent;
|
||||
import com.srsystem.entity.Employee;
|
||||
import com.srsystem.service.CalendarService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "Calendar", description = "통합 일정 조회 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/calendar")
|
||||
@RequiredArgsConstructor
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class CalendarController {
|
||||
|
||||
private final CalendarService calendarService;
|
||||
|
||||
@Operation(summary = "통합 일정 조회", description = "기간 내 모든 일정 (티켓 마감, 보고서 제출, 파견 기간) 을 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping("/events")
|
||||
public ResponseEntity<ApiResponse<List<CalendarEvent>>> getEvents(
|
||||
@RequestParam String start,
|
||||
@RequestParam String end,
|
||||
@CurrentUser Employee currentUser) {
|
||||
LocalDate startDate = LocalDate.parse(start);
|
||||
LocalDate endDate = LocalDate.parse(end);
|
||||
List<CalendarEvent> events = calendarService.getEvents(currentUser.getEmpId(), currentUser.getRoleCode(), startDate, endDate);
|
||||
return ResponseEntity.ok(ApiResponse.ok(events));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.srsystem.controller;
|
||||
|
||||
import com.srsystem.dto.ApiResponse;
|
||||
import com.srsystem.dto.CommonCodeRequest;
|
||||
import com.srsystem.entity.CommonCode;
|
||||
import com.srsystem.service.CommonCodeService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "Common Code", description = "공통 코드 관리 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/common-codes")
|
||||
@RequiredArgsConstructor
|
||||
public class CommonCodeController {
|
||||
|
||||
private final CommonCodeService commonCodeService;
|
||||
|
||||
@Operation(summary = "공통 코드 목록 조회", description = "그룹 코드에 해당하는 공통 코드 목록을 조회합니다")
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
public ResponseEntity<ApiResponse<List<CommonCode>>> getCodes(@RequestParam(required = false) String groupCode) {
|
||||
List<CommonCode> codes = commonCodeService.getCodes(groupCode);
|
||||
return ResponseEntity.ok(ApiResponse.ok(codes));
|
||||
}
|
||||
|
||||
@Operation(summary = "공통 코드 그룹 목록 조회", description = "모든 공통 코드 그룹 목록을 조회합니다")
|
||||
@GetMapping("/groups")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
public ResponseEntity<ApiResponse<List<String>>> getGroups() {
|
||||
List<String> groups = commonCodeService.getGroupCodes();
|
||||
return ResponseEntity.ok(ApiResponse.ok(groups));
|
||||
}
|
||||
|
||||
@Operation(summary = "공통 코드 생성", description = "새로운 공통 코드를 생성합니다 (ADMIN 전용)")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@PostMapping
|
||||
public ResponseEntity<ApiResponse<CommonCode>> createCode(@Valid @RequestBody CommonCodeRequest request) {
|
||||
CommonCode code = commonCodeService.createCode(request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("공통 코드가 생성되었습니다.", code));
|
||||
}
|
||||
|
||||
@Operation(summary = "공통 코드 수정", description = "기존 공통 코드를 수정합니다 (ADMIN 전용)")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<ApiResponse<CommonCode>> updateCode(@PathVariable Long id, @Valid @RequestBody CommonCodeRequest request) {
|
||||
CommonCode code = commonCodeService.updateCode(id, request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("공통 코드가 수정되었습니다.", code));
|
||||
}
|
||||
|
||||
@Operation(summary = "공통 코드 삭제", description = "공통 코드를 삭제합니다 (ADMIN 전용)")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<ApiResponse<Void>> deleteCode(@PathVariable Long id) {
|
||||
commonCodeService.deleteCode(id);
|
||||
return ResponseEntity.ok(ApiResponse.ok("공통 코드가 삭제되었습니다.", null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.srsystem.controller;
|
||||
|
||||
import com.srsystem.config.jwt.CurrentUser;
|
||||
import com.srsystem.dto.ApiResponse;
|
||||
import com.srsystem.dto.CreateDispatchRequest;
|
||||
import com.srsystem.entity.DispatchHistory;
|
||||
import com.srsystem.entity.Employee;
|
||||
import com.srsystem.service.DispatchHistoryService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.web.PageableDefault;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "Dispatch History", description = "파견 이력 관리 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/dispatch-histories")
|
||||
@RequiredArgsConstructor
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class DispatchHistoryController {
|
||||
|
||||
private final DispatchHistoryService dispatchHistoryService;
|
||||
|
||||
@Operation(summary = "파견 이력 생성", description = "직원을 프로젝트에 배정합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PostMapping
|
||||
public ResponseEntity<ApiResponse<DispatchHistory>> createDispatch(
|
||||
@Valid @RequestBody CreateDispatchRequest request) {
|
||||
DispatchHistory history = dispatchHistoryService.createDispatch(request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("파견이 등록되었습니다", history));
|
||||
}
|
||||
|
||||
@Operation(summary = "파견 이력 종료", description = "파견을 종료합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PutMapping("/{dispatchId}/end")
|
||||
public ResponseEntity<ApiResponse<DispatchHistory>> endDispatch(
|
||||
@PathVariable Long dispatchId) {
|
||||
DispatchHistory history = dispatchHistoryService.endDispatch(dispatchId);
|
||||
return ResponseEntity.ok(ApiResponse.ok("파견이 종료되었습니다", history));
|
||||
}
|
||||
|
||||
@Operation(summary = "현재 파견 중인 직원 목록", description = "파견중인 직원 목록을 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@GetMapping("/active")
|
||||
public ResponseEntity<ApiResponse<List<DispatchHistory>>> getActiveDispatches() {
|
||||
List<DispatchHistory> list = dispatchHistoryService.getActiveDispatches();
|
||||
return ResponseEntity.ok(ApiResponse.ok(list));
|
||||
}
|
||||
|
||||
@Operation(summary = "파견 이력 전체 목록", description = "페이징 및 필터 조건으로 파견 이력 목록을 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<Page<DispatchHistory>>> findAll(
|
||||
@PageableDefault(size = 20) Pageable pageable,
|
||||
@RequestParam(required = false) Long empId,
|
||||
@RequestParam(required = false) Long projectId) {
|
||||
return ResponseEntity.ok(ApiResponse.ok(dispatchHistoryService.findAll(pageable, empId, projectId)));
|
||||
}
|
||||
|
||||
@Operation(summary = "파견 이력 단건 조회", description = "파견 이력 ID로 단건 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@GetMapping("/{dispatchId}")
|
||||
public ResponseEntity<ApiResponse<DispatchHistory>> findById(@PathVariable Long dispatchId) {
|
||||
return ResponseEntity.ok(ApiResponse.ok(dispatchHistoryService.findById(dispatchId)));
|
||||
}
|
||||
|
||||
@Operation(summary = "파견 이력 삭제", description = "파견 이력을 삭제합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@DeleteMapping("/{dispatchId}")
|
||||
public ResponseEntity<ApiResponse<Void>> delete(@PathVariable Long dispatchId) {
|
||||
dispatchHistoryService.delete(dispatchId);
|
||||
return ResponseEntity.ok(ApiResponse.ok("파견 이력이 삭제되었습니다", null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package com.srsystem.controller;
|
||||
|
||||
import com.srsystem.config.jwt.CurrentUser;
|
||||
import com.srsystem.dto.ApiResponse;
|
||||
import com.srsystem.dto.ChangePasswordRequest;
|
||||
import com.srsystem.dto.EmployeeSearchResult;
|
||||
import com.srsystem.dto.EmployeeSummary;
|
||||
import com.srsystem.dto.PortfolioResponse;
|
||||
import com.srsystem.dto.UpdateAchievementsRequest;
|
||||
import com.srsystem.entity.DispatchHistory;
|
||||
import com.srsystem.entity.Employee;
|
||||
import com.srsystem.entity.enums.EmpStatus;
|
||||
import com.srsystem.service.DispatchHistoryService;
|
||||
import com.srsystem.service.EmployeeService;
|
||||
import com.srsystem.service.PortfolioService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.web.PageableDefault;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "Employee", description = "직원 관리 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/employees")
|
||||
@RequiredArgsConstructor
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class EmployeeController {
|
||||
|
||||
private final EmployeeService employeeService;
|
||||
private final DispatchHistoryService dispatchHistoryService;
|
||||
private final PortfolioService portfolioService;
|
||||
|
||||
@Operation(summary = "직원 목록 조회", description = "페이징된 직원 목록을 조회합니다. 상태 필터 옵션 포함")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<Page<Employee>>> findAll(
|
||||
@RequestParam(required = false) EmpStatus status,
|
||||
@PageableDefault(size = 20) Pageable pageable) {
|
||||
Page<Employee> employees = employeeService.findAll(status, pageable);
|
||||
return ResponseEntity.ok(ApiResponse.ok(employees));
|
||||
}
|
||||
|
||||
@Operation(summary = "직원 단건 조회", description = "직원 ID 로 단일 직원 정보를 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping("/{empId}")
|
||||
public ResponseEntity<ApiResponse<Employee>> findById(@PathVariable Long empId) {
|
||||
Employee employee = employeeService.findById(empId);
|
||||
return ResponseEntity.ok(ApiResponse.ok(employee));
|
||||
}
|
||||
|
||||
@Operation(summary = "현재 사용자 프로필 조회", description = "로그인한 사용자의 프로필 정보를 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping("/me")
|
||||
public ResponseEntity<ApiResponse<EmployeeSummary>> getMe(@CurrentUser Employee employee) {
|
||||
EmployeeSummary summary = employeeService.toSummary(employee);
|
||||
return ResponseEntity.ok(ApiResponse.ok(summary));
|
||||
}
|
||||
|
||||
@Operation(summary = "직원 검색 자동완성", description = "이름 키워드로 직원을 검색합니다 (디바운싱 300ms 권장)")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@GetMapping("/search")
|
||||
public ResponseEntity<ApiResponse<List<EmployeeSearchResult>>> search(
|
||||
@RequestParam String keyword) {
|
||||
List<EmployeeSearchResult> results = employeeService.searchByName(keyword);
|
||||
return ResponseEntity.ok(ApiResponse.ok(results));
|
||||
}
|
||||
|
||||
@Operation(summary = "직원 생성", description = "새로운 직원 정보를 생성합니다")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@PostMapping
|
||||
public ResponseEntity<ApiResponse<Employee>> create(@RequestBody Employee employee) {
|
||||
Employee created = employeeService.create(employee);
|
||||
return ResponseEntity.ok(ApiResponse.ok("직원이 등록되었습니다", created));
|
||||
}
|
||||
|
||||
@Operation(summary = "직원 정보 수정", description = "직원 정보를 수정합니다 (퇴사자는 수정 불가)")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@PutMapping("/{empId}")
|
||||
public ResponseEntity<ApiResponse<Employee>> update(
|
||||
@PathVariable Long empId,
|
||||
@RequestBody Employee employee) {
|
||||
Employee updated = employeeService.update(empId, employee);
|
||||
return ResponseEntity.ok(ApiResponse.ok("직원 정보가 수정되었습니다", updated));
|
||||
}
|
||||
|
||||
@Operation(summary = "직원 삭제", description = "직원을 삭제합니다 (Soft Delete, 퇴사자는 삭제 불가)")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@DeleteMapping("/{empId}")
|
||||
public ResponseEntity<ApiResponse<Void>> delete(@PathVariable Long empId) {
|
||||
employeeService.delete(empId);
|
||||
return ResponseEntity.ok(ApiResponse.ok("직원이 삭제되었습니다", null));
|
||||
}
|
||||
|
||||
@Operation(summary = "포트폴리오 조회", description = "직원의 파견 이력 (포트폴리오) 을 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@Transactional(readOnly = true)
|
||||
@GetMapping("/{empId}/dispatch-histories")
|
||||
public ResponseEntity<ApiResponse<List<DispatchHistory>>> getDispatchHistories(
|
||||
@PathVariable Long empId,
|
||||
@Parameter(hidden = true) @CurrentUser Employee currentUser) {
|
||||
|
||||
if (!currentUser.getEmpId().equals(empId) &&
|
||||
!currentUser.getRoleCode().name().equals("ADMIN") &&
|
||||
!currentUser.getRoleCode().name().equals("MANAGER")) {
|
||||
return ResponseEntity.status(403).body(ApiResponse.error("권한이 없습니다"));
|
||||
}
|
||||
|
||||
List<DispatchHistory> histories = dispatchHistoryService.findByEmployeeId(empId);
|
||||
return ResponseEntity.ok(ApiResponse.ok(histories));
|
||||
}
|
||||
|
||||
@Operation(summary = "포트폴리오 조회", description = "직원의 포트폴리오를 조회합니다. 파견 이력별로 보고서 요약과 해결 SR 티켓이 자동 바인딩됩니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping("/{empId}/portfolio")
|
||||
public ResponseEntity<ApiResponse<List<PortfolioResponse>>> getPortfolio(
|
||||
@PathVariable Long empId,
|
||||
@Parameter(hidden = true) @CurrentUser Employee currentUser) {
|
||||
if (!currentUser.getEmpId().equals(empId) &&
|
||||
!currentUser.getRoleCode().name().equals("ADMIN") &&
|
||||
!currentUser.getRoleCode().name().equals("MANAGER")) {
|
||||
return ResponseEntity.status(403).body(ApiResponse.error("권한이 없습니다"));
|
||||
}
|
||||
List<PortfolioResponse> portfolio = portfolioService.getPortfolio(empId);
|
||||
return ResponseEntity.ok(ApiResponse.ok(portfolio));
|
||||
}
|
||||
|
||||
@Operation(summary = "포트폴리오 성과 저장", description = "파견 이력의 Achievements 필드를 업데이트합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@PutMapping("/{empId}/dispatch-histories/{dispatchId}/achievements")
|
||||
public ResponseEntity<ApiResponse<Void>> updateAchievements(
|
||||
@PathVariable Long empId,
|
||||
@PathVariable Long dispatchId,
|
||||
@RequestBody UpdateAchievementsRequest request,
|
||||
@Parameter(hidden = true) @CurrentUser Employee currentUser) {
|
||||
if (!currentUser.getEmpId().equals(empId) &&
|
||||
!currentUser.getRoleCode().name().equals("ADMIN") &&
|
||||
!currentUser.getRoleCode().name().equals("MANAGER")) {
|
||||
return ResponseEntity.status(403).body(ApiResponse.error("권한이 없습니다"));
|
||||
}
|
||||
portfolioService.updateAchievements(dispatchId, request.getAchievements());
|
||||
return ResponseEntity.ok(ApiResponse.ok("성과가 저장되었습니다", null));
|
||||
}
|
||||
|
||||
@Operation(summary = "비밀번호 변경", description = "현재 사용자의 비밀번호를 변경합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@PutMapping("/me/password")
|
||||
public ResponseEntity<ApiResponse<Void>> changePassword(
|
||||
@RequestBody @Valid ChangePasswordRequest request,
|
||||
@CurrentUser Employee currentUser) {
|
||||
employeeService.changePassword(currentUser.getEmpId(), request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("비밀번호가 변경되었습니다", null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package com.srsystem.controller;
|
||||
|
||||
import com.srsystem.dto.ApiResponse;
|
||||
import com.srsystem.dto.CreateEquipmentRequest;
|
||||
import com.srsystem.dto.CreateMaintenanceRequest;
|
||||
import com.srsystem.dto.EquipmentSummary;
|
||||
import com.srsystem.dto.UpdateEquipmentRequest;
|
||||
import com.srsystem.entity.Equipment;
|
||||
import com.srsystem.entity.EquipmentAssignment;
|
||||
import com.srsystem.entity.EquipmentMaintenance;
|
||||
import com.srsystem.entity.enums.EquipmentStatus;
|
||||
import com.srsystem.entity.enums.EquipmentType;
|
||||
import com.srsystem.service.EquipmentService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.web.PageableDefault;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Tag(name = "Equipment", description = "IT 자산 관리 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/equipment")
|
||||
@RequiredArgsConstructor
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class EquipmentController {
|
||||
|
||||
private final EquipmentService equipmentService;
|
||||
|
||||
@Operation(summary = "장비 목록 조회", description = "필터 조건으로 장비 목록을 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<Page<Equipment>>> getEquipment(
|
||||
@RequestParam(required = false) String type,
|
||||
@RequestParam(required = false) EquipmentStatus status,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@PageableDefault(size = 20) Pageable pageable) {
|
||||
Page<Equipment> equipment = equipmentService.getEquipment(type, status, keyword, pageable);
|
||||
return ResponseEntity.ok(ApiResponse.ok(equipment));
|
||||
}
|
||||
|
||||
@Operation(summary = "장비 상태별 카운트", description = "장비 상태별 개수를 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@GetMapping("/summary")
|
||||
public ResponseEntity<ApiResponse<EquipmentSummary>> getSummary() {
|
||||
Map<String, Long> counts = equipmentService.getSummary();
|
||||
EquipmentSummary summary = new EquipmentSummary(counts);
|
||||
return ResponseEntity.ok(ApiResponse.ok(summary));
|
||||
}
|
||||
|
||||
@Operation(summary = "장비 단건 조회", description = "장비 ID 로 단일 장비 정보를 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@GetMapping("/{equipId}")
|
||||
public ResponseEntity<ApiResponse<Equipment>> getEquipment(@PathVariable Long equipId) {
|
||||
Equipment equipment = equipmentService.getEquipment(equipId);
|
||||
return ResponseEntity.ok(ApiResponse.ok(equipment));
|
||||
}
|
||||
|
||||
@Operation(summary = "장비 생성", description = "새로운 장비를 생성합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PostMapping
|
||||
public ResponseEntity<ApiResponse<Equipment>> createEquipment(@Valid @RequestBody CreateEquipmentRequest request) {
|
||||
Equipment equipment = equipmentService.createEquipment(request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("장비가 등록되었습니다", equipment));
|
||||
}
|
||||
|
||||
@Operation(summary = "장비 정보 수정", description = "장비 정보를 수정합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PutMapping("/{equipId}")
|
||||
public ResponseEntity<ApiResponse<Equipment>> updateEquipment(
|
||||
@PathVariable Long equipId,
|
||||
@RequestBody UpdateEquipmentRequest request) {
|
||||
Equipment equipment = equipmentService.updateEquipment(equipId, request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("장비 정보가 수정되었습니다", equipment));
|
||||
}
|
||||
|
||||
@Operation(summary = "장비 삭제", description = "장비를 삭제합니다 (Soft Delete)")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@DeleteMapping("/{equipId}")
|
||||
public ResponseEntity<ApiResponse<Void>> deleteEquipment(@PathVariable Long equipId) {
|
||||
equipmentService.deleteEquipment(equipId);
|
||||
return ResponseEntity.ok(ApiResponse.ok("장비가 삭제되었습니다", null));
|
||||
}
|
||||
|
||||
@Operation(summary = "장비 지급", description = "사용자에게 장비를 지급합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PostMapping("/{equipId}/assign")
|
||||
public ResponseEntity<ApiResponse<EquipmentAssignment>> assignEquipment(
|
||||
@PathVariable Long equipId,
|
||||
@RequestBody Map<String, Object> body) {
|
||||
Long userId = ((Number) body.get("userId")).longValue();
|
||||
String condition = (String) body.get("condition");
|
||||
EquipmentAssignment assignment = equipmentService.assignEquipment(equipId, userId, condition);
|
||||
return ResponseEntity.ok(ApiResponse.ok("장비가 지급되었습니다", assignment));
|
||||
}
|
||||
|
||||
@Operation(summary = "장비 반납", description = "장비를 반납합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PutMapping("/{equipId}/return")
|
||||
public ResponseEntity<ApiResponse<EquipmentAssignment>> returnEquipment(
|
||||
@PathVariable Long equipId,
|
||||
@RequestBody Map<String, String> body) {
|
||||
EquipmentAssignment assignment = equipmentService.returnEquipment(equipId, body.get("condition"));
|
||||
return ResponseEntity.ok(ApiResponse.ok("장비가 반납되었습니다", assignment));
|
||||
}
|
||||
|
||||
@Operation(summary = "장비 지급 이력 조회", description = "장비의 지급/반납 이력을 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@GetMapping("/{equipId}/assignments")
|
||||
public ResponseEntity<ApiResponse<List<EquipmentAssignment>>> getAssignments(@PathVariable Long equipId) {
|
||||
List<EquipmentAssignment> assignments = equipmentService.getAssignments(equipId);
|
||||
return ResponseEntity.ok(ApiResponse.ok(assignments));
|
||||
}
|
||||
|
||||
@Operation(summary = "장비 유지보수 이력 조회", description = "장비의 유지보수 이력을 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@GetMapping("/{equipId}/maintenances")
|
||||
public ResponseEntity<ApiResponse<List<EquipmentMaintenance>>> getMaintenanceHistory(@PathVariable Long equipId) {
|
||||
List<EquipmentMaintenance> history = equipmentService.getMaintenanceHistory(equipId);
|
||||
return ResponseEntity.ok(ApiResponse.ok(history));
|
||||
}
|
||||
|
||||
@Operation(summary = "장비 유지보수 기록 추가", description = "장비의 유지보수 기록을 추가합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PostMapping("/{equipId}/maintenance")
|
||||
public ResponseEntity<ApiResponse<EquipmentMaintenance>> addMaintenance(
|
||||
@PathVariable Long equipId,
|
||||
@Valid @RequestBody CreateMaintenanceRequest request) {
|
||||
EquipmentMaintenance maintenance = equipmentService.addMaintenance(equipId, request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("유지보수 기록이 등록되었습니다", maintenance));
|
||||
}
|
||||
|
||||
@Operation(summary = "장비 이미지 추가", description = "장비에 이미지를 추가합니다 (JSONB 배열)")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PostMapping("/{equipmentId}/images")
|
||||
public ResponseEntity<ApiResponse<Equipment>> addImage(
|
||||
@PathVariable Long equipmentId,
|
||||
@RequestParam("file") MultipartFile file) {
|
||||
Equipment equipment = equipmentService.addImage(equipmentId, file);
|
||||
return ResponseEntity.ok(ApiResponse.ok("이미지가 추가되었습니다", equipment));
|
||||
}
|
||||
|
||||
@Operation(summary = "장비 이미지 삭제", description = "장비에서 특정 이미지를 삭제합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@DeleteMapping("/{equipmentId}/images/{imageIndex}")
|
||||
public ResponseEntity<ApiResponse<Equipment>> removeImage(
|
||||
@PathVariable Long equipmentId,
|
||||
@PathVariable int imageIndex) {
|
||||
Equipment equipment = equipmentService.removeImage(equipmentId, imageIndex);
|
||||
return ResponseEntity.ok(ApiResponse.ok("이미지가 삭제되었습니다", equipment));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.srsystem.controller;
|
||||
|
||||
import com.srsystem.config.jwt.CurrentUser;
|
||||
import com.srsystem.dto.ApiResponse;
|
||||
import com.srsystem.entity.Employee;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.UUID;
|
||||
|
||||
@Tag(name = "File", description = "파일 업로드 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/files")
|
||||
@RequiredArgsConstructor
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class FileUploadController {
|
||||
|
||||
@Value("${file.upload-dir:./uploads}")
|
||||
private String uploadDir;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
File dir = new File(uploadDir);
|
||||
if (!dir.exists()) dir.mkdirs();
|
||||
}
|
||||
|
||||
@Operation(summary = "파일 업로드", description = "이미지 파일을 업로드하고 URL 을 반환합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@PostMapping("/upload")
|
||||
public ResponseEntity<ApiResponse<String>> uploadFile(
|
||||
@RequestParam("file") MultipartFile file,
|
||||
@CurrentUser Employee currentUser) {
|
||||
try {
|
||||
String originalName = file.getOriginalFilename();
|
||||
String ext = "";
|
||||
if (originalName != null && originalName.contains(".")) {
|
||||
ext = originalName.substring(originalName.lastIndexOf("."));
|
||||
}
|
||||
String storedName = UUID.randomUUID().toString() + ext;
|
||||
Path targetPath = Paths.get(uploadDir, storedName);
|
||||
Files.copy(file.getInputStream(), targetPath);
|
||||
String url = "/uploads/" + storedName;
|
||||
return ResponseEntity.ok(ApiResponse.ok("파일 업로드 성공", url));
|
||||
} catch (IOException e) {
|
||||
return ResponseEntity.internalServerError().body(ApiResponse.error("파일 업로드 실패: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.srsystem.controller;
|
||||
|
||||
import com.srsystem.config.jwt.CurrentUser;
|
||||
import com.srsystem.dto.ApiResponse;
|
||||
import com.srsystem.entity.Employee;
|
||||
import com.srsystem.entity.Notification;
|
||||
import com.srsystem.service.NotificationService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Tag(name = "Notification", description = "알림 관리 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/notifications")
|
||||
@RequiredArgsConstructor
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class NotificationController {
|
||||
|
||||
private final NotificationService notificationService;
|
||||
|
||||
@Operation(summary = "내 알림 목록 조회", description = "로그인한 사용자의 알림 목록을 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<List<Notification>>> getMyNotifications(@CurrentUser Employee currentUser) {
|
||||
List<Notification> notifications = notificationService.getMyNotifications(currentUser.getEmpId());
|
||||
return ResponseEntity.ok(ApiResponse.ok(notifications));
|
||||
}
|
||||
|
||||
@Operation(summary = "안읽은 알림 카운트", description = "안읽은 알림 개수를 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping("/unread")
|
||||
public ResponseEntity<ApiResponse<Map<String, Long>>> getUnreadCount(@CurrentUser Employee currentUser) {
|
||||
long count = notificationService.getUnreadCount(currentUser.getEmpId());
|
||||
return ResponseEntity.ok(ApiResponse.ok(Map.of("count", count)));
|
||||
}
|
||||
|
||||
@Operation(summary = "알림 읽음 처리", description = "알림을 읽음으로 표시합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@PutMapping("/{notificationId}/read")
|
||||
public ResponseEntity<ApiResponse<Void>> markAsRead(@PathVariable Long notificationId) {
|
||||
notificationService.markAsRead(notificationId);
|
||||
return ResponseEntity.ok(ApiResponse.ok("알림이 읽음 처리되었습니다", null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.srsystem.controller;
|
||||
|
||||
import com.srsystem.dto.ApiResponse;
|
||||
import com.srsystem.dto.HealthResponse;
|
||||
import com.srsystem.dto.WakeRequest;
|
||||
import com.srsystem.dto.WakeResponse;
|
||||
import com.srsystem.service.ParkingService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1")
|
||||
@Tag(name = "Parking")
|
||||
public class ParkingController {
|
||||
|
||||
private final ParkingService parkingService;
|
||||
|
||||
public ParkingController(ParkingService parkingService) {
|
||||
this.parkingService = parkingService;
|
||||
}
|
||||
|
||||
@GetMapping("/health")
|
||||
public ResponseEntity<ApiResponse<HealthResponse>> checkHealth() {
|
||||
HealthResponse response = parkingService.checkStatus();
|
||||
return ResponseEntity.ok(ApiResponse.ok(response));
|
||||
}
|
||||
|
||||
@PostMapping("/wake")
|
||||
public ResponseEntity<ApiResponse<WakeResponse>> requestWake(@Valid @RequestBody WakeRequest request) {
|
||||
WakeResponse response = parkingService.requestWake(request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("Wake request processed successfully", response));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.srsystem.controller;
|
||||
|
||||
import com.srsystem.config.jwt.CurrentUser;
|
||||
import com.srsystem.dto.ApiResponse;
|
||||
import com.srsystem.dto.CreateProjectRequest;
|
||||
import com.srsystem.entity.Employee;
|
||||
import com.srsystem.entity.Project;
|
||||
import com.srsystem.entity.enums.ProjectStatus;
|
||||
import com.srsystem.service.ProjectService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "Project", description = "프로젝트 관리 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/projects")
|
||||
@RequiredArgsConstructor
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class ProjectController {
|
||||
|
||||
private final ProjectService projectService;
|
||||
|
||||
@Operation(summary = "내 프로젝트 목록 조회", description = "로그인한 사용자가 참여 중인 프로젝트 목록을 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping("/my")
|
||||
public ResponseEntity<ApiResponse<List<Project>>> getMyProjects(@CurrentUser Employee currentUser) {
|
||||
List<Project> projects = projectService.getMyProjects(currentUser.getEmpId());
|
||||
return ResponseEntity.ok(ApiResponse.ok(projects));
|
||||
}
|
||||
|
||||
@Operation(summary = "프로젝트 단건 조회", description = "프로젝트 ID 로 단일 프로젝트 정보를 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping("/{projectId}")
|
||||
public ResponseEntity<ApiResponse<Project>> getProject(@PathVariable Long projectId) {
|
||||
Project project = projectService.getProject(projectId);
|
||||
return ResponseEntity.ok(ApiResponse.ok(project));
|
||||
}
|
||||
|
||||
@Operation(summary = "프로젝트 목록 조회", description = "전체 프로젝트 목록을 조회합니다. 상태 필터 옵션 포함")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@Transactional(readOnly = true)
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<List<Project>>> findAll(
|
||||
@RequestParam(required = false) ProjectStatus status) {
|
||||
List<Project> projects = projectService.findAll(status);
|
||||
return ResponseEntity.ok(ApiResponse.ok(projects));
|
||||
}
|
||||
|
||||
@Operation(summary = "프로젝트 생성", description = "새로운 프로젝트를 등록합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PostMapping
|
||||
public ResponseEntity<ApiResponse<Project>> create(
|
||||
@RequestBody @Valid CreateProjectRequest request) {
|
||||
Project project = projectService.create(request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("프로젝트가 등록되었습니다", project));
|
||||
}
|
||||
|
||||
@Operation(summary = "프로젝트 수정", description = "프로젝트 정보를 수정합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PutMapping("/{projectId}")
|
||||
public ResponseEntity<ApiResponse<Project>> update(
|
||||
@PathVariable Long projectId,
|
||||
@RequestBody @Valid CreateProjectRequest request) {
|
||||
Project project = projectService.update(projectId, request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("프로젝트가 수정되었습니다", project));
|
||||
}
|
||||
|
||||
@Operation(summary = "프로젝트 삭제", description = "프로젝트를 삭제합니다 (Soft Delete)")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@DeleteMapping("/{projectId}")
|
||||
public ResponseEntity<ApiResponse<Void>> delete(
|
||||
@PathVariable Long projectId) {
|
||||
projectService.delete(projectId);
|
||||
return ResponseEntity.ok(ApiResponse.ok("프로젝트가 삭제되었습니다", null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.srsystem.controller;
|
||||
|
||||
import com.srsystem.config.jwt.CurrentUser;
|
||||
import com.srsystem.dto.ApiResponse;
|
||||
import com.srsystem.dto.CreateReportRequest;
|
||||
import com.srsystem.dto.ReportSummary;
|
||||
import com.srsystem.dto.ReviewReportRequest;
|
||||
import com.srsystem.dto.TicketSummary;
|
||||
import com.srsystem.dto.WorkReportResponse;
|
||||
import com.srsystem.entity.Employee;
|
||||
import com.srsystem.entity.enums.RoleCode;
|
||||
import com.srsystem.entity.enums.ReportStatus;
|
||||
import com.srsystem.entity.enums.ReportType;
|
||||
import com.srsystem.service.ReportService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.web.PageableDefault;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Tag(name = "Report", description = "업무 보고 관리 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/reports")
|
||||
@RequiredArgsConstructor
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class ReportController {
|
||||
|
||||
private final ReportService reportService;
|
||||
|
||||
@Operation(summary = "보고서 목록 조회", description = "필터 조건으로 보고서 목록을 조회합니다. EMPLOYEE 는 본인만, MANAGER+ 는 전체")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<Page<WorkReportResponse>>> getReports(
|
||||
@RequestParam(required = false) ReportStatus status,
|
||||
@RequestParam(required = false) String type,
|
||||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate targetDate,
|
||||
@PageableDefault(size = 20) Pageable pageable,
|
||||
@CurrentUser Employee currentUser) {
|
||||
|
||||
Page<WorkReportResponse> reports;
|
||||
|
||||
if (currentUser.getRoleCode() == RoleCode.EMPLOYEE) {
|
||||
// Employee sees only their own reports
|
||||
reports = reportService.findByUserId(currentUser.getEmpId(), status, type, targetDate, pageable);
|
||||
} else {
|
||||
// Manager/Admin see all reports, with SUBMITTED first
|
||||
reports = reportService.getReports(status, type, targetDate, null, pageable);
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.ok(reports));
|
||||
}
|
||||
|
||||
@Operation(summary = "보고서 단건 조회", description = "보고서 ID 로 단일 보고서 정보를 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping("/{reportId}")
|
||||
public ResponseEntity<ApiResponse<WorkReportResponse>> getReport(@PathVariable Long reportId) {
|
||||
WorkReportResponse report = reportService.getReport(reportId);
|
||||
return ResponseEntity.ok(ApiResponse.ok(report));
|
||||
}
|
||||
|
||||
@Operation(summary = "보고서 작성", description = "새로운 업무 보고서를 작성합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@PostMapping
|
||||
public ResponseEntity<ApiResponse<WorkReportResponse>> createReport(
|
||||
@Valid @RequestBody CreateReportRequest request,
|
||||
@CurrentUser Employee currentUser) {
|
||||
WorkReportResponse report = reportService.createReport(currentUser.getEmpId(), request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("보고서가 작성되었습니다", report));
|
||||
}
|
||||
|
||||
@Operation(summary = "보고서 상태 변경", description = "보고서 상태를 변경합니다 (DRAFT→SUBMITTED)")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@PutMapping("/{reportId}/status")
|
||||
public ResponseEntity<ApiResponse<WorkReportResponse>> updateStatus(
|
||||
@PathVariable Long reportId,
|
||||
@RequestBody Map<String, String> body,
|
||||
@CurrentUser Employee currentUser) {
|
||||
ReportStatus newStatus = ReportStatus.valueOf(body.get("status"));
|
||||
WorkReportResponse report = reportService.updateStatus(reportId, currentUser.getEmpId(), newStatus);
|
||||
return ResponseEntity.ok(ApiResponse.ok("보고서 상태가 변경되었습니다", report));
|
||||
}
|
||||
|
||||
@Operation(summary = "보고서 결재", description = "관리자가 보고서를 승인/반려합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PutMapping("/{reportId}/review")
|
||||
public ResponseEntity<ApiResponse<WorkReportResponse>> reviewReport(
|
||||
@PathVariable Long reportId,
|
||||
@Valid @RequestBody ReviewReportRequest request,
|
||||
@CurrentUser Employee currentUser) {
|
||||
WorkReportResponse report = reportService.reviewReport(reportId, currentUser.getEmpId(), request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("보고서가 결재되었습니다", report));
|
||||
}
|
||||
|
||||
@Operation(summary = "보고서 삭제", description = "보고서를 삭제합니다 (LOCKED 상태는 불가)")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@DeleteMapping("/{reportId}")
|
||||
public ResponseEntity<ApiResponse<Void>> deleteReport(@PathVariable Long reportId) {
|
||||
reportService.deleteReport(reportId);
|
||||
return ResponseEntity.ok(ApiResponse.ok("보고서가 삭제되었습니다", null));
|
||||
}
|
||||
|
||||
@Operation(summary = "보고서 결재 요약 조회", description = "상태별 보고서 개수를 반환합니다 (MANAGER/ADMIN)")
|
||||
@PreAuthorize("hasAnyRole('MANAGER','ADMIN')")
|
||||
@GetMapping("/summary")
|
||||
public ResponseEntity<ApiResponse<ReportSummary>> getSummary() {
|
||||
return ResponseEntity.ok(ApiResponse.ok(reportService.getSummary()));
|
||||
}
|
||||
|
||||
@Operation(summary = "SR 자동 완성", description = "해당 일자의 해결/종료된 SR 내역을 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping("/sr-auto-complete")
|
||||
public ResponseEntity<ApiResponse<List<TicketSummary>>> getSrAutoComplete(
|
||||
@RequestParam String date,
|
||||
@CurrentUser Employee currentUser) {
|
||||
List<TicketSummary> tickets = reportService.getResolvedTicketsForDate(currentUser.getEmpId(), date);
|
||||
return ResponseEntity.ok(ApiResponse.ok(tickets));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.srsystem.controller;
|
||||
|
||||
import com.srsystem.config.jwt.CurrentUser;
|
||||
import com.srsystem.dto.ApiResponse;
|
||||
import com.srsystem.dto.CreateCommentRequest;
|
||||
import com.srsystem.dto.CreateTicketRequest;
|
||||
import com.srsystem.dto.IssueTicket;
|
||||
import com.srsystem.dto.TicketCommentResponse;
|
||||
import com.srsystem.entity.Employee;
|
||||
import com.srsystem.entity.enums.RoleCode;
|
||||
import com.srsystem.entity.enums.TicketCategory;
|
||||
import com.srsystem.entity.enums.TicketPriority;
|
||||
import com.srsystem.entity.enums.TicketStatus;
|
||||
import com.srsystem.service.TicketService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.web.PageableDefault;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Tag(name = "Ticket", description = "SR 티켓 관리 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/tickets")
|
||||
@RequiredArgsConstructor
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class TicketController {
|
||||
|
||||
private final TicketService ticketService;
|
||||
|
||||
@Operation(summary = "티켓 목록 조회", description = "필터 조건으로 티켓 목록을 조회합니다. EMPLOYEE 는 본인 티켓만, MANAGER/ADMIN 은 전체 조회")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<Page<IssueTicket>>> getTickets(
|
||||
@RequestParam(required = false) TicketStatus status,
|
||||
@RequestParam(required = false) String category,
|
||||
@RequestParam(required = false) String priority,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@PageableDefault(size = 20) Pageable pageable,
|
||||
@CurrentUser Employee currentUser) {
|
||||
|
||||
Long requesterId = currentUser.getRoleCode() == RoleCode.EMPLOYEE ? currentUser.getEmpId() : null;
|
||||
Page<IssueTicket> tickets = ticketService.getTickets(status, category, priority, keyword, pageable, requesterId);
|
||||
return ResponseEntity.ok(ApiResponse.ok(tickets));
|
||||
}
|
||||
|
||||
@Operation(summary = "티켓 단건 조회", description = "티켓 ID 로 단일 티켓 정보를 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping("/{ticketId}")
|
||||
public ResponseEntity<ApiResponse<IssueTicket>> getTicket(@PathVariable Long ticketId) {
|
||||
IssueTicket ticket = ticketService.getTicket(ticketId);
|
||||
return ResponseEntity.ok(ApiResponse.ok(ticket));
|
||||
}
|
||||
|
||||
@Operation(summary = "티켓 생성", description = "새로운 SR 티켓을 생성합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@PostMapping
|
||||
public ResponseEntity<ApiResponse<IssueTicket>> createTicket(
|
||||
@Valid @RequestBody CreateTicketRequest request,
|
||||
@CurrentUser Employee currentUser) {
|
||||
IssueTicket ticket = ticketService.createTicket(currentUser.getEmpId(), request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("티켓이 등록되었습니다", ticket));
|
||||
}
|
||||
|
||||
@Operation(summary = "티켓 상태 변경", description = "티켓 상태를 변경합니다 (OPEN→ASSIGNED→IN_PROGRESS→RESOLVED→CLOSED)")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PutMapping("/{ticketId}/status")
|
||||
public ResponseEntity<ApiResponse<IssueTicket>> updateStatus(
|
||||
@PathVariable Long ticketId,
|
||||
@RequestBody Map<String, String> body,
|
||||
@CurrentUser Employee currentUser) {
|
||||
TicketStatus newStatus = TicketStatus.valueOf(body.get("status"));
|
||||
IssueTicket ticket = ticketService.updateStatus(ticketId, currentUser.getEmpId(), newStatus);
|
||||
return ResponseEntity.ok(ApiResponse.ok("티켓 상태가 변경되었습니다", ticket));
|
||||
}
|
||||
|
||||
@Operation(summary = "티켓 담당자 배정", description = "티켓에 담당자를 배정합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PutMapping("/{ticketId}/assign")
|
||||
public ResponseEntity<ApiResponse<IssueTicket>> assignTicket(
|
||||
@PathVariable Long ticketId,
|
||||
@RequestBody Map<String, Long> body) {
|
||||
IssueTicket ticket = ticketService.assignTicket(ticketId, body.get("assigneeId"));
|
||||
return ResponseEntity.ok(ApiResponse.ok("티켓이 배정되었습니다", ticket));
|
||||
}
|
||||
|
||||
@Operation(summary = "티켓 해결 처리", description = "티켓을 해결 처리하고 해결 노트를 기록합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PutMapping("/{ticketId}/resolve")
|
||||
public ResponseEntity<ApiResponse<IssueTicket>> resolveTicket(
|
||||
@PathVariable Long ticketId,
|
||||
@RequestBody Map<String, String> body) {
|
||||
IssueTicket ticket = ticketService.resolveTicket(ticketId, body.get("resolutionNote"));
|
||||
return ResponseEntity.ok(ApiResponse.ok("티켓이 해결되었습니다", ticket));
|
||||
}
|
||||
|
||||
@Operation(summary = "티켓 삭제", description = "티켓을 삭제합니다 (Soft Delete)")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@DeleteMapping("/{ticketId}")
|
||||
public ResponseEntity<ApiResponse<Void>> deleteTicket(@PathVariable Long ticketId) {
|
||||
ticketService.deleteTicket(ticketId);
|
||||
return ResponseEntity.ok(ApiResponse.ok("티켓이 삭제되었습니다", null));
|
||||
}
|
||||
|
||||
@Operation(summary = "댓글 목록 조회", description = "티켓의 댓글 목록을 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping("/{ticketId}/comments")
|
||||
public ResponseEntity<ApiResponse<List<TicketCommentResponse>>> getComments(@PathVariable Long ticketId) {
|
||||
List<TicketCommentResponse> comments = ticketService.getComments(ticketId);
|
||||
return ResponseEntity.ok(ApiResponse.ok(comments));
|
||||
}
|
||||
|
||||
@Operation(summary = "댓글 등록", description = "티켓에 댓글을 등록합니다 (CLOSED 티켓은 불가)")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@PostMapping("/{ticketId}/comments")
|
||||
public ResponseEntity<ApiResponse<TicketCommentResponse>> addComment(
|
||||
@PathVariable Long ticketId,
|
||||
@Valid @RequestBody CreateCommentRequest request,
|
||||
@CurrentUser Employee currentUser) {
|
||||
TicketCommentResponse response = ticketService.addComment(ticketId, currentUser.getEmpId(), request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("댓글이 등록되었습니다", response));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.srsystem.controller;
|
||||
|
||||
import com.srsystem.config.jwt.CurrentUser;
|
||||
import com.srsystem.dto.ApiResponse;
|
||||
import com.srsystem.dto.TrainingSummary;
|
||||
import com.srsystem.dto.UpdateProgressRequest;
|
||||
import com.srsystem.entity.Employee;
|
||||
import com.srsystem.entity.TrainingProgress;
|
||||
import com.srsystem.service.TrainingService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "Training", description = "교육 관리 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/training")
|
||||
@RequiredArgsConstructor
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class TrainingController {
|
||||
|
||||
private final TrainingService trainingService;
|
||||
|
||||
@Operation(summary = "내 교육 진도 조회", description = "로그인한 사용자의 교육 진도 목록을 조회합니다")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<List<TrainingProgress>>> getMyProgress(@CurrentUser Employee currentUser) {
|
||||
List<TrainingProgress> progress = trainingService.getMyProgress(currentUser.getEmpId());
|
||||
return ResponseEntity.ok(ApiResponse.ok(progress));
|
||||
}
|
||||
|
||||
@Operation(summary = "교육 진도 업데이트", description = "교육 진도율을 업데이트합니다")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@PutMapping("/{progressId}")
|
||||
public ResponseEntity<ApiResponse<TrainingProgress>> updateProgress(
|
||||
@PathVariable Long progressId,
|
||||
@Valid @RequestBody UpdateProgressRequest request) {
|
||||
TrainingProgress progress = trainingService.updateProgress(progressId, request);
|
||||
return ResponseEntity.ok(ApiResponse.ok("교육 진도가 업데이트되었습니다", progress));
|
||||
}
|
||||
|
||||
@Operation(summary = "전체 교육 현황 조회", description = "모든 사용자의 교육 이수 현황을 조회합니다 (관리자용)")
|
||||
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
|
||||
@GetMapping("/summary")
|
||||
public ResponseEntity<ApiResponse<List<TrainingSummary>>> getSummary() {
|
||||
List<TrainingSummary> summary = trainingService.getSummary();
|
||||
return ResponseEntity.ok(ApiResponse.ok(summary));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.srsystem.controller;
|
||||
|
||||
import com.srsystem.config.jwt.CurrentUser;
|
||||
import com.srsystem.dto.ApiResponse;
|
||||
import com.srsystem.entity.Employee;
|
||||
import com.srsystem.entity.WeeklyPlan;
|
||||
import com.srsystem.service.WeeklyPlanService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Tag(name = "Weekly Plan", description = "주간 계획 관리 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/weekly-plans")
|
||||
@RequiredArgsConstructor
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class WeeklyPlanController {
|
||||
|
||||
private final WeeklyPlanService weeklyPlanService;
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@Operation(summary = "내 주간 계획 목록 조회", description = "현재 사용자의 모든 주간 계획 목록을 조회합니다.")
|
||||
public ResponseEntity<ApiResponse<List<WeeklyPlan>>> getMyPlans(@CurrentUser Employee currentUser) {
|
||||
return ResponseEntity.ok(ApiResponse.ok(weeklyPlanService.getMyPlans(currentUser.getEmpId())));
|
||||
}
|
||||
|
||||
@GetMapping("/{planId}")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@Operation(summary = "주간 계획 상세 조회", description = "특정 주간 계획의 상세 정보를 조회합니다.")
|
||||
public ResponseEntity<ApiResponse<WeeklyPlan>> getPlan(@PathVariable Long planId) {
|
||||
return ResponseEntity.ok(ApiResponse.ok(weeklyPlanService.getPlan(planId)));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@Operation(summary = "주간 계획 생성", description = "새로운 주간 계획을 생성합니다.")
|
||||
public ResponseEntity<ApiResponse<WeeklyPlan>> createPlan(
|
||||
@RequestBody Map<String, String> body,
|
||||
@CurrentUser Employee currentUser) {
|
||||
WeeklyPlan plan = weeklyPlanService.createPlan(currentUser.getEmpId(), body.get("tasks"));
|
||||
return ResponseEntity.ok(ApiResponse.ok("주간 계획이 생성되었습니다", plan));
|
||||
}
|
||||
|
||||
@PutMapping("/{planId}")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@Operation(summary = "주간 계획 수정", description = "기존 주간 계획의 태스크와 상태를 수정합니다.")
|
||||
public ResponseEntity<ApiResponse<WeeklyPlan>> updatePlan(
|
||||
@PathVariable Long planId,
|
||||
@RequestBody Map<String, String> body) {
|
||||
String statusStr = body.get("status");
|
||||
com.srsystem.entity.enums.WeeklyPlanStatus status = statusStr != null ?
|
||||
com.srsystem.entity.enums.WeeklyPlanStatus.valueOf(statusStr) : null;
|
||||
WeeklyPlan plan = weeklyPlanService.updatePlan(planId, body.get("tasks"), status);
|
||||
return ResponseEntity.ok(ApiResponse.ok("주간 계획이 수정되었습니다", plan));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{planId}")
|
||||
@PreAuthorize("hasAnyRole('EMPLOYEE', 'MANAGER', 'ADMIN')")
|
||||
@Operation(summary = "주간 계획 삭제", description = "주간 계획을 삭제합니다 (Soft Delete).")
|
||||
public ResponseEntity<ApiResponse<Void>> deletePlan(@PathVariable Long planId) {
|
||||
weeklyPlanService.deletePlan(planId);
|
||||
return ResponseEntity.ok(ApiResponse.ok("주간 계획이 삭제되었습니다", null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
public record ApiResponse<T>(boolean success, String message, T data) {
|
||||
|
||||
public static <T> ApiResponse<T> ok(T data) {
|
||||
return new ApiResponse<>(true, "OK", data);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> ok(String message, T data) {
|
||||
return new ApiResponse<>(true, message, data);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> error(String message) {
|
||||
return new ApiResponse<>(false, message, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
public record CalendarEvent(
|
||||
String id,
|
||||
String title,
|
||||
String date,
|
||||
String type,
|
||||
String status,
|
||||
String endDate
|
||||
) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public record ChangePasswordRequest(
|
||||
@NotBlank String currentPassword,
|
||||
@NotBlank String newPassword
|
||||
) {}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public record CommonCodeRequest(
|
||||
@NotBlank String groupCode,
|
||||
@NotBlank String codeValue,
|
||||
@NotBlank String codeName,
|
||||
String optionVal1,
|
||||
String optionVal2,
|
||||
@NotNull Integer sortOrder,
|
||||
@NotNull Character isActive
|
||||
) {}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public record CreateCommentRequest(
|
||||
@NotBlank String commentText,
|
||||
String attachedFileUrl
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public record CreateDispatchRequest(
|
||||
@NotNull Long employeeId,
|
||||
@NotNull Long projectId,
|
||||
@NotBlank String roleTitle,
|
||||
@NotNull Integer contributionRate,
|
||||
@NotBlank String individualTech,
|
||||
@NotBlank String performedTasks,
|
||||
LocalDate startDate
|
||||
) {}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import com.srsystem.entity.enums.EquipmentStatus;
|
||||
import com.srsystem.entity.enums.EquipmentType;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public record CreateEquipmentRequest(
|
||||
@NotNull EquipmentType equipmentType,
|
||||
@NotBlank String manufacturer,
|
||||
@NotBlank String modelName,
|
||||
@NotBlank String serialNumber,
|
||||
@NotBlank String assetCode,
|
||||
String specifications,
|
||||
@NotNull EquipmentStatus status,
|
||||
@NotNull LocalDate purchaseDate,
|
||||
LocalDate warrantyExpDate
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import com.srsystem.entity.enums.MaintenanceType;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public record CreateMaintenanceRequest(
|
||||
@NotNull MaintenanceType maintenanceType,
|
||||
@NotBlank String issueDescription,
|
||||
String actionTaken,
|
||||
@NotNull BigDecimal cost,
|
||||
@NotBlank String reportedBy
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import com.srsystem.entity.enums.ContractType;
|
||||
import com.srsystem.entity.enums.ProjectType;
|
||||
import com.srsystem.entity.enums.WorkLocationType;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public record CreateProjectRequest(
|
||||
@NotBlank @Size(max = 255)
|
||||
String projectName,
|
||||
|
||||
@NotNull
|
||||
ProjectType projectType,
|
||||
|
||||
@NotBlank @Size(max = 255)
|
||||
String contractCompany,
|
||||
|
||||
@Size(max = 255)
|
||||
String endUserCompany,
|
||||
|
||||
@NotNull
|
||||
ContractType contractType,
|
||||
|
||||
@NotNull
|
||||
LocalDate startDate,
|
||||
|
||||
LocalDate endDate,
|
||||
|
||||
@NotNull
|
||||
WorkLocationType workLocationType,
|
||||
|
||||
@Size(max = 500)
|
||||
String workAddress,
|
||||
|
||||
String techEnvironment,
|
||||
|
||||
String mainTaskDesc,
|
||||
|
||||
Long internalManagerId
|
||||
) {}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import com.srsystem.entity.enums.ReportType;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
public record CreateReportRequest(
|
||||
Long dispatchId,
|
||||
@NotNull ReportType reportType,
|
||||
@NotNull LocalDate targetDate,
|
||||
@NotBlank String completedTasks,
|
||||
@NotBlank String plannedTasks,
|
||||
String issuesAndRisks,
|
||||
String supportRequested,
|
||||
BigDecimal workHours,
|
||||
String blockerFlag,
|
||||
String artifacts,
|
||||
Long weeklyPlanId
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import com.srsystem.entity.enums.TicketCategory;
|
||||
import com.srsystem.entity.enums.TicketPriority;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public record CreateTicketRequest(
|
||||
@NotNull Long projectId,
|
||||
@NotNull TicketCategory category,
|
||||
@NotNull TicketPriority priority,
|
||||
@NotBlank String title,
|
||||
@NotBlank String description
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
public record EmployeeSearchResult(
|
||||
Long empId,
|
||||
String name,
|
||||
String email,
|
||||
String department
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import com.srsystem.entity.enums.DispatchStatus;
|
||||
import com.srsystem.entity.enums.EmpStatus;
|
||||
import com.srsystem.entity.enums.RoleCode;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public record EmployeeSummary(
|
||||
Long empId,
|
||||
String name,
|
||||
String email,
|
||||
String phone,
|
||||
RoleCode roleCode,
|
||||
EmpStatus empStatus,
|
||||
DispatchStatus dispatchStatus,
|
||||
LocalDate joinedDate,
|
||||
LocalDate leftDate
|
||||
) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public record EquipmentSummary(
|
||||
Map<String, Long> counts
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
public record HealthResponse(boolean parked, String message, String resumeTime, String parkingSchedule) {
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import com.srsystem.entity.enums.TicketCategory;
|
||||
import com.srsystem.entity.enums.TicketPriority;
|
||||
import com.srsystem.entity.enums.TicketStatus;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public record IssueTicket(
|
||||
Long ticketId,
|
||||
String projectName,
|
||||
String requesterName,
|
||||
String assigneeName,
|
||||
TicketCategory category,
|
||||
TicketPriority priority,
|
||||
String title,
|
||||
String description,
|
||||
TicketStatus status,
|
||||
LocalDateTime dueDate,
|
||||
LocalDateTime resolvedAt,
|
||||
String resolutionNote,
|
||||
boolean slaWarnFlag,
|
||||
LocalDateTime createdAt
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import com.srsystem.entity.enums.DispatchStatus;
|
||||
import com.srsystem.entity.enums.RoleCode;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public record LoginRequest(
|
||||
@NotBlank(message = "로그인 ID 는 필수입니다")
|
||||
String loginId,
|
||||
@NotBlank(message = "비밀번호는 필수입니다")
|
||||
String password
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import com.srsystem.entity.enums.DispatchStatus;
|
||||
import com.srsystem.entity.enums.RoleCode;
|
||||
|
||||
public record LoginResponse(
|
||||
String accessToken,
|
||||
String refreshToken,
|
||||
EmployeeSummary employee
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class NotificationEvent {
|
||||
private final String type;
|
||||
private final String title;
|
||||
private final String message;
|
||||
private final String linkUrl;
|
||||
private final Long recipientId;
|
||||
|
||||
public NotificationEvent(String type, String title, String message, String linkUrl, Long recipientId) {
|
||||
this.type = type;
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
this.linkUrl = linkUrl;
|
||||
this.recipientId = recipientId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public class PortfolioResponse {
|
||||
|
||||
private Long dispatchId;
|
||||
private String projectName;
|
||||
private String roleTitle;
|
||||
private Integer contributionRate;
|
||||
private String individualTech;
|
||||
private String performedTasks;
|
||||
private String achievements;
|
||||
private LocalDate startDate;
|
||||
private LocalDate endDate;
|
||||
private Integer internalRating;
|
||||
private List<BoundReport> reports;
|
||||
private List<BoundTicket> resolvedTickets;
|
||||
|
||||
// Inner classes
|
||||
public static class BoundReport {
|
||||
public String targetDate;
|
||||
public String reportType;
|
||||
public String completedTasks;
|
||||
}
|
||||
|
||||
public static class BoundTicket {
|
||||
public Long ticketId;
|
||||
public String title;
|
||||
public String category;
|
||||
public String resolutionNote;
|
||||
}
|
||||
|
||||
public PortfolioResponse() {}
|
||||
|
||||
// --- Getters & Setters ---
|
||||
|
||||
public Long getDispatchId() { return dispatchId; }
|
||||
public void setDispatchId(Long dispatchId) { this.dispatchId = dispatchId; }
|
||||
|
||||
public String getProjectName() { return projectName; }
|
||||
public void setProjectName(String projectName) { this.projectName = projectName; }
|
||||
|
||||
public String getRoleTitle() { return roleTitle; }
|
||||
public void setRoleTitle(String roleTitle) { this.roleTitle = roleTitle; }
|
||||
|
||||
public Integer getContributionRate() { return contributionRate; }
|
||||
public void setContributionRate(Integer contributionRate) { this.contributionRate = contributionRate; }
|
||||
|
||||
public String getIndividualTech() { return individualTech; }
|
||||
public void setIndividualTech(String individualTech) { this.individualTech = individualTech; }
|
||||
|
||||
public String getPerformedTasks() { return performedTasks; }
|
||||
public void setPerformedTasks(String performedTasks) { this.performedTasks = performedTasks; }
|
||||
|
||||
public String getAchievements() { return achievements; }
|
||||
public void setAchievements(String achievements) { this.achievements = achievements; }
|
||||
|
||||
public LocalDate getStartDate() { return startDate; }
|
||||
public void setStartDate(LocalDate startDate) { this.startDate = startDate; }
|
||||
|
||||
public LocalDate getEndDate() { return endDate; }
|
||||
public void setEndDate(LocalDate endDate) { this.endDate = endDate; }
|
||||
|
||||
public Integer getInternalRating() { return internalRating; }
|
||||
public void setInternalRating(Integer internalRating) { this.internalRating = internalRating; }
|
||||
|
||||
public List<BoundReport> getReports() { return reports; }
|
||||
public void setReports(List<BoundReport> reports) { this.reports = reports; }
|
||||
|
||||
public List<BoundTicket> getResolvedTickets() { return resolvedTickets; }
|
||||
public void setResolvedTickets(List<BoundTicket> resolvedTickets) { this.resolvedTickets = resolvedTickets; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public record RefreshTokenRequest(
|
||||
@NotBlank(message = "리프레시 토큰은 필수입니다")
|
||||
String refreshToken
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
public record ReportSummary(
|
||||
long submitted,
|
||||
long approved,
|
||||
long rejected,
|
||||
long draft
|
||||
) {}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import com.srsystem.entity.enums.ReportStatus;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public record ReviewReportRequest(
|
||||
@NotNull ReportStatus status,
|
||||
String reviewComment
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public record TicketCommentResponse(
|
||||
Long commentId,
|
||||
String userName,
|
||||
String commentText,
|
||||
String attachedFileUrl,
|
||||
LocalDateTime createdAt
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
public record TicketSummary(
|
||||
Long ticketId,
|
||||
String title,
|
||||
String category,
|
||||
String priority,
|
||||
String resolutionNote,
|
||||
String resolvedAt
|
||||
) {}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
public record TrainingSummary(
|
||||
Long userId,
|
||||
String userName,
|
||||
long totalCourses,
|
||||
long completedCount,
|
||||
double avgProgressRate
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UpdateAchievementsRequest {
|
||||
private String achievements;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import com.srsystem.entity.enums.EquipmentStatus;
|
||||
import com.srsystem.entity.enums.EquipmentType;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public record UpdateEquipmentRequest(
|
||||
EquipmentType equipmentType,
|
||||
String manufacturer,
|
||||
String modelName,
|
||||
String serialNumber,
|
||||
String assetCode,
|
||||
String specifications,
|
||||
EquipmentStatus status,
|
||||
LocalDate purchaseDate,
|
||||
LocalDate warrantyExpDate
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import com.srsystem.entity.enums.TrainingStatus;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
|
||||
public record UpdateProgressRequest(
|
||||
@Min(0) @Max(100) Integer progressRate,
|
||||
TrainingStatus status
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public record WakeRequest(
|
||||
@NotBlank String requestedBy,
|
||||
@NotBlank String reason,
|
||||
@NotNull String requestedAt
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
public record WakeResponse(boolean success, String message, String estimatedWakeTime) {
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.srsystem.dto;
|
||||
|
||||
import com.srsystem.entity.enums.ReportStatus;
|
||||
import com.srsystem.entity.enums.ReportType;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public record WorkReportResponse(
|
||||
Long reportId,
|
||||
String userName,
|
||||
String projectName,
|
||||
ReportType reportType,
|
||||
LocalDate targetDate,
|
||||
String completedTasks,
|
||||
String plannedTasks,
|
||||
String issuesAndRisks,
|
||||
String supportRequested,
|
||||
ReportStatus status,
|
||||
String reviewerName,
|
||||
String reviewComment,
|
||||
LocalDateTime reviewedAt,
|
||||
LocalDateTime createdAt,
|
||||
BigDecimal workHours,
|
||||
String blockerFlag,
|
||||
String artifacts
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@MappedSuperclass
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@SQLDelete(sql = "UPDATE #{#entityName} SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
@Getter
|
||||
@Setter
|
||||
public abstract class BaseEntity {
|
||||
|
||||
@CreatedDate
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@LastModifiedDate
|
||||
@Column(name = "updated_at", nullable = false)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@Column(name = "is_deleted", nullable = false, length = 1)
|
||||
private char isDeleted = 'N';
|
||||
|
||||
public void setIsDeleted(char isDeleted) {
|
||||
this.isDeleted = isDeleted;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tb_common_code")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@SQLDelete(sql = "UPDATE tb_common_code SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
public class CommonCode extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_common_codes")
|
||||
@SequenceGenerator(name = "sq_common_codes", sequenceName = "sq_common_codes", allocationSize = 50)
|
||||
@Column(name = "id")
|
||||
private Long codeId;
|
||||
|
||||
@Column(name = "group_code", nullable = false, length = 50)
|
||||
private String groupCode;
|
||||
|
||||
@Column(name = "code_value", nullable = false, length = 100)
|
||||
private String codeValue;
|
||||
|
||||
@Column(name = "code_name", nullable = false, length = 255)
|
||||
private String codeName;
|
||||
|
||||
@Column(name = "option_val1", length = 255)
|
||||
private String optionVal1;
|
||||
|
||||
@Column(name = "option_val2", length = 255)
|
||||
private String optionVal2;
|
||||
|
||||
@Column(name = "sort_order", nullable = false)
|
||||
private Integer sortOrder;
|
||||
|
||||
@Column(name = "is_active", nullable = false, length = 1)
|
||||
private char isActive = 'Y';
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tb_daily_task_progress")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@SQLDelete(sql = "UPDATE tb_daily_task_progress SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
public class DailyTaskProgress extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_daily_task_progress")
|
||||
@SequenceGenerator(name = "sq_daily_task_progress", sequenceName = "sq_daily_task_progress", allocationSize = 50)
|
||||
@Column(name = "id")
|
||||
private Long progressId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "report_id", nullable = false)
|
||||
private Report report;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "weekly_plan_id", nullable = false)
|
||||
private WeeklyPlan weeklyPlan;
|
||||
|
||||
@Column(name = "task_name", nullable = false, length = 500)
|
||||
private String taskName;
|
||||
|
||||
@Column(name = "progress_rate", nullable = false)
|
||||
private Integer progressRate;
|
||||
|
||||
@Column(name = "work_hours", nullable = false)
|
||||
private BigDecimal workHours;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tb_dispatch_history")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@SQLDelete(sql = "UPDATE tb_dispatch_history SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
public class DispatchHistory extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_dispatch_histories")
|
||||
@SequenceGenerator(name = "sq_dispatch_histories", sequenceName = "sq_dispatch_histories", allocationSize = 50)
|
||||
@Column(name = "id")
|
||||
private Long dispatchId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "employee_id", nullable = false)
|
||||
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
|
||||
private Employee employee;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "project_id", nullable = false)
|
||||
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
|
||||
private Project project;
|
||||
|
||||
@Column(name = "start_date", nullable = false)
|
||||
private LocalDate startDate;
|
||||
|
||||
@Column(name = "end_date")
|
||||
private LocalDate endDate;
|
||||
|
||||
@Column(name = "role_title", nullable = false, length = 255)
|
||||
private String roleTitle;
|
||||
|
||||
@Column(name = "contribution_rate", nullable = false)
|
||||
private Integer contributionRate;
|
||||
|
||||
@Column(name = "individual_tech", columnDefinition = "TEXT", nullable = false)
|
||||
private String individualTech;
|
||||
|
||||
@Column(name = "performed_tasks", columnDefinition = "TEXT", nullable = false)
|
||||
private String performedTasks;
|
||||
|
||||
@Column(name = "achievements", columnDefinition = "TEXT")
|
||||
private String achievements;
|
||||
|
||||
@Column(name = "client_feedback", columnDefinition = "TEXT")
|
||||
private String clientFeedback;
|
||||
|
||||
@Column(name = "internal_rating")
|
||||
private Integer internalRating;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.srsystem.entity.enums.DispatchStatus;
|
||||
import com.srsystem.entity.enums.EmpStatus;
|
||||
import com.srsystem.entity.enums.RoleCode;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tb_employee")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@SQLDelete(sql = "UPDATE tb_employee SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
public class Employee extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_employees")
|
||||
@SequenceGenerator(name = "sq_employees", sequenceName = "sq_employees", allocationSize = 50)
|
||||
@Column(name = "id")
|
||||
private Long empId;
|
||||
|
||||
@Column(name = "login_id", nullable = false, unique = true, length = 50)
|
||||
private String loginId;
|
||||
|
||||
@Column(name = "password", nullable = false)
|
||||
private String password;
|
||||
|
||||
@Column(name = "name", nullable = false, length = 100)
|
||||
private String name;
|
||||
|
||||
@Column(name = "email", length = 255)
|
||||
private String email;
|
||||
|
||||
@Column(name = "phone", length = 20)
|
||||
private String phone;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "role_code", nullable = false, length = 20)
|
||||
private RoleCode roleCode;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "emp_status", nullable = false, length = 20)
|
||||
private EmpStatus empStatus;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "dispatch_status", nullable = false, length = 20)
|
||||
private DispatchStatus dispatchStatus;
|
||||
|
||||
@Column(name = "joined_date")
|
||||
private LocalDate joinedDate;
|
||||
|
||||
@Column(name = "left_date")
|
||||
private LocalDate leftDate;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import com.srsystem.entity.enums.EquipmentStatus;
|
||||
import com.srsystem.entity.enums.EquipmentType;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tb_equipment")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@SQLDelete(sql = "UPDATE tb_equipment SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
public class Equipment extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_equipment")
|
||||
@SequenceGenerator(name = "sq_equipment", sequenceName = "sq_equipment", allocationSize = 50)
|
||||
@Column(name = "id")
|
||||
private Long equipmentId;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "equipment_type", nullable = false, length = 20)
|
||||
private EquipmentType equipmentType;
|
||||
|
||||
@Column(name = "manufacturer", nullable = false, length = 255)
|
||||
private String manufacturer;
|
||||
|
||||
@Column(name = "model_name", nullable = false, length = 255)
|
||||
private String modelName;
|
||||
|
||||
@Column(name = "serial_number", nullable = false, unique = true, length = 100)
|
||||
private String serialNumber;
|
||||
|
||||
@Column(name = "asset_code", nullable = false, unique = true, length = 100)
|
||||
private String assetCode;
|
||||
|
||||
@Column(name = "specifications", columnDefinition = "JSONB")
|
||||
private String specifications;
|
||||
|
||||
@Column(name = "images", columnDefinition = "JSONB")
|
||||
private String images;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private EquipmentStatus status;
|
||||
|
||||
@Column(name = "purchase_date", nullable = false)
|
||||
private LocalDate purchaseDate;
|
||||
|
||||
@Column(name = "warranty_exp_date")
|
||||
private LocalDate warrantyExpDate;
|
||||
|
||||
@Version
|
||||
@Column(name = "version", nullable = false)
|
||||
private Long version;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tb_equipment_assignment")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@SQLDelete(sql = "UPDATE tb_equipment_assignment SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
public class EquipmentAssignment extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_equipment_assignments")
|
||||
@SequenceGenerator(name = "sq_equipment_assignments", sequenceName = "sq_equipment_assignments", allocationSize = 50)
|
||||
@Column(name = "id")
|
||||
private Long assignmentId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "equipment_id", nullable = false)
|
||||
private Equipment equipment;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private Employee user;
|
||||
|
||||
@Column(name = "assigned_date", nullable = false)
|
||||
private LocalDateTime assignedDate;
|
||||
|
||||
@Column(name = "returned_date")
|
||||
private LocalDateTime returnedDate;
|
||||
|
||||
@Column(name = "cond_on_assign", columnDefinition = "TEXT", nullable = false)
|
||||
private String condOnAssign;
|
||||
|
||||
@Column(name = "cond_on_return", columnDefinition = "TEXT")
|
||||
private String condOnReturn;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import com.srsystem.entity.enums.MaintenanceType;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tb_equipment_maintenance")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@SQLDelete(sql = "UPDATE tb_equipment_maintenance SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
public class EquipmentMaintenance extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_equipment_maintenance")
|
||||
@SequenceGenerator(name = "sq_equipment_maintenance", sequenceName = "sq_equipment_maintenance", allocationSize = 50)
|
||||
@Column(name = "id")
|
||||
private Long maintenanceId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "equipment_id", nullable = false)
|
||||
private Equipment equipment;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "reported_by", nullable = false)
|
||||
private Employee reportedBy;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "maintenance_type", nullable = false, length = 20)
|
||||
private MaintenanceType maintenanceType;
|
||||
|
||||
@Column(name = "issue_description", columnDefinition = "TEXT", nullable = false)
|
||||
private String issueDescription;
|
||||
|
||||
@Column(name = "action_taken", columnDefinition = "TEXT")
|
||||
private String actionTaken;
|
||||
|
||||
@Column(name = "cost", nullable = false, precision = 12, scale = 2)
|
||||
private BigDecimal cost;
|
||||
|
||||
@Column(name = "reported_date", nullable = false)
|
||||
private LocalDateTime reportedDate;
|
||||
|
||||
@Column(name = "resolved_date")
|
||||
private LocalDateTime resolvedDate;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tb_notification")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@SQLDelete(sql = "UPDATE tb_notification SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
public class Notification extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_notifications")
|
||||
@SequenceGenerator(name = "sq_notifications", sequenceName = "sq_notifications", allocationSize = 50)
|
||||
@Column(name = "id")
|
||||
private Long notificationId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private Employee user;
|
||||
|
||||
@Column(name = "type", nullable = false, length = 50)
|
||||
private String type;
|
||||
|
||||
@Column(name = "title", nullable = false, length = 255)
|
||||
private String title;
|
||||
|
||||
@Column(name = "message", columnDefinition = "TEXT", nullable = false)
|
||||
private String message;
|
||||
|
||||
@Column(name = "is_read", nullable = false, length = 1)
|
||||
private char isRead = 'N';
|
||||
|
||||
@Column(name = "link_url", length = 500)
|
||||
private String linkUrl;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.srsystem.entity.enums.ContractType;
|
||||
import com.srsystem.entity.enums.ProjectStatus;
|
||||
import com.srsystem.entity.enums.ProjectType;
|
||||
import com.srsystem.entity.enums.WorkLocationType;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tb_project")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@SQLDelete(sql = "UPDATE tb_project SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
public class Project extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_projects")
|
||||
@SequenceGenerator(name = "sq_projects", sequenceName = "sq_projects", allocationSize = 50)
|
||||
@Column(name = "id")
|
||||
private Long projectId;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "project_type", nullable = false, length = 20)
|
||||
private ProjectType projectType;
|
||||
|
||||
@Column(name = "project_name", nullable = false, length = 255)
|
||||
private String projectName;
|
||||
|
||||
@Column(name = "contract_company", nullable = false, length = 255)
|
||||
private String contractCompany;
|
||||
|
||||
@Column(name = "end_user_company", length = 255)
|
||||
private String endUserCompany;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "contract_type", nullable = false, length = 20)
|
||||
private ContractType contractType;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private ProjectStatus status;
|
||||
|
||||
@Column(name = "start_date", nullable = false)
|
||||
private LocalDate startDate;
|
||||
|
||||
@Column(name = "end_date")
|
||||
private LocalDate endDate;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "work_location_type", nullable = false, length = 20)
|
||||
private WorkLocationType workLocationType;
|
||||
|
||||
@Column(name = "work_address", length = 500)
|
||||
private String workAddress;
|
||||
|
||||
@Column(name = "tech_environment", columnDefinition = "TEXT")
|
||||
private String techEnvironment;
|
||||
|
||||
@Column(name = "main_task_desc", columnDefinition = "TEXT")
|
||||
private String mainTaskDesc;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "internal_manager_id")
|
||||
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
|
||||
private Employee internalManager;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import com.srsystem.entity.enums.ReportStatus;
|
||||
import com.srsystem.entity.enums.ReportType;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tb_report")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@SQLDelete(sql = "UPDATE tb_report SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
public class Report extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_reports")
|
||||
@SequenceGenerator(name = "sq_reports", sequenceName = "sq_reports", allocationSize = 50)
|
||||
@Column(name = "id")
|
||||
private Long reportId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "dispatch_id")
|
||||
private DispatchHistory dispatch;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private Employee user;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "report_type", nullable = false, length = 20)
|
||||
private ReportType reportType;
|
||||
|
||||
@Column(name = "target_date", nullable = false)
|
||||
private LocalDate targetDate;
|
||||
|
||||
@Column(name = "completed_tasks", columnDefinition = "TEXT", nullable = false)
|
||||
private String completedTasks;
|
||||
|
||||
@Column(name = "planned_tasks", columnDefinition = "TEXT", nullable = false)
|
||||
private String plannedTasks;
|
||||
|
||||
@Column(name = "issues_and_risks", columnDefinition = "TEXT")
|
||||
private String issuesAndRisks;
|
||||
|
||||
@Column(name = "support_requested", columnDefinition = "TEXT")
|
||||
private String supportRequested;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private ReportStatus status;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "reviewer_id")
|
||||
private Employee reviewer;
|
||||
|
||||
@Column(name = "review_comment", columnDefinition = "TEXT")
|
||||
private String reviewComment;
|
||||
|
||||
@Column(name = "reviewed_at")
|
||||
private LocalDateTime reviewedAt;
|
||||
|
||||
@Column(name = "work_hours")
|
||||
private BigDecimal workHours;
|
||||
|
||||
@Column(name = "blocker_flag", length = 1)
|
||||
@Builder.Default
|
||||
private char blockerFlag = 'N';
|
||||
|
||||
@Column(name = "artifacts", columnDefinition = "JSONB")
|
||||
private String artifacts;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "weekly_plan_id")
|
||||
private WeeklyPlan weeklyPlan;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import com.srsystem.entity.enums.TicketCategory;
|
||||
import com.srsystem.entity.enums.TicketPriority;
|
||||
import com.srsystem.entity.enums.TicketStatus;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tb_ticket")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@SQLDelete(sql = "UPDATE tb_ticket SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
public class Ticket extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_tickets")
|
||||
@SequenceGenerator(name = "sq_tickets", sequenceName = "sq_tickets", allocationSize = 50)
|
||||
@Column(name = "id")
|
||||
private Long ticketId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "project_id", nullable = false)
|
||||
private Project project;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "requester_id", nullable = false)
|
||||
private Employee requester;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "assignee_id")
|
||||
private Employee assignee;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "category", nullable = false, length = 20)
|
||||
private TicketCategory category;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "priority", nullable = false, length = 20)
|
||||
private TicketPriority priority;
|
||||
|
||||
@Column(name = "title", nullable = false, length = 500)
|
||||
private String title;
|
||||
|
||||
@Column(name = "description", columnDefinition = "TEXT", nullable = false)
|
||||
private String description;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private TicketStatus status;
|
||||
|
||||
@Column(name = "due_date")
|
||||
private LocalDateTime dueDate;
|
||||
|
||||
@Column(name = "resolved_at")
|
||||
private LocalDateTime resolvedAt;
|
||||
|
||||
@Column(name = "resolution_note", columnDefinition = "TEXT")
|
||||
private String resolutionNote;
|
||||
|
||||
@Column(name = "sla_warn_flag", nullable = false, length = 1)
|
||||
private char slaWarnFlag = 'N';
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tb_ticket_comment")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@SQLDelete(sql = "UPDATE tb_ticket_comment SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
public class TicketComment extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_ticket_comments")
|
||||
@SequenceGenerator(name = "sq_ticket_comments", sequenceName = "sq_ticket_comments", allocationSize = 50)
|
||||
@Column(name = "id")
|
||||
private Long commentId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "ticket_id", nullable = false)
|
||||
private Ticket ticket;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private Employee user;
|
||||
|
||||
@Column(name = "comment_text", columnDefinition = "TEXT", nullable = false)
|
||||
private String commentText;
|
||||
|
||||
@Column(name = "attached_file_url", length = 1000)
|
||||
private String attachedFileUrl;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import com.srsystem.entity.enums.TrainingStatus;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tb_training_progress")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@SQLDelete(sql = "UPDATE tb_training_progress SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
public class TrainingProgress extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_training_progress")
|
||||
@SequenceGenerator(name = "sq_training_progress", sequenceName = "sq_training_progress", allocationSize = 50)
|
||||
@Column(name = "id")
|
||||
private Long progressId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private Employee user;
|
||||
|
||||
@Column(name = "course_name", nullable = false, length = 255)
|
||||
private String courseName;
|
||||
|
||||
@Column(name = "progress_rate", nullable = false)
|
||||
private Integer progressRate;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private TrainingStatus status;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.srsystem.entity;
|
||||
|
||||
import com.srsystem.entity.enums.WeeklyPlanStatus;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tb_weekly_plan")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@SQLDelete(sql = "UPDATE tb_weekly_plan SET is_deleted = 'Y' WHERE id = ?")
|
||||
@SQLRestriction("is_deleted = 'N'")
|
||||
public class WeeklyPlan extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sq_weekly_plans")
|
||||
@SequenceGenerator(name = "sq_weekly_plans", sequenceName = "sq_weekly_plans", allocationSize = 50)
|
||||
@Column(name = "id")
|
||||
private Long planId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private Employee user;
|
||||
|
||||
@Column(name = "week_start", nullable = false)
|
||||
private LocalDate weekStart;
|
||||
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private WeeklyPlanStatus status;
|
||||
|
||||
@Column(name = "tasks", columnDefinition = "JSONB")
|
||||
private String tasks;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum ContractType {
|
||||
TURN_KEY,
|
||||
MM
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum DispatchStatus {
|
||||
WAITING,
|
||||
DISPATCHED
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum EmpStatus {
|
||||
ACTIVE,
|
||||
ON_LEAVE,
|
||||
RESIGNED
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum EquipmentStatus {
|
||||
AVAILABLE,
|
||||
IN_USE,
|
||||
REPAIRING,
|
||||
RETIRED,
|
||||
LOST
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum EquipmentType {
|
||||
LAPTOP,
|
||||
MONITOR,
|
||||
TEST_DEVICE,
|
||||
ACCESSORY
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum MaintenanceType {
|
||||
REPAIR,
|
||||
UPGRADE,
|
||||
INSPECTION,
|
||||
DISPOSAL
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum ProjectStatus {
|
||||
PREPARING,
|
||||
ONGOING,
|
||||
COMPLETED,
|
||||
HOLD
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum ProjectType {
|
||||
SI,
|
||||
SM,
|
||||
MIGRATION
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum ReportStatus {
|
||||
DRAFT,
|
||||
SUBMITTED,
|
||||
APPROVED,
|
||||
REJECTED
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum ReportType {
|
||||
DAILY,
|
||||
WEEKLY,
|
||||
MONTHLY,
|
||||
RETURN
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum RoleCode {
|
||||
ADMIN,
|
||||
MANAGER,
|
||||
EMPLOYEE
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum TicketCategory {
|
||||
BUG,
|
||||
FEATURE,
|
||||
SUPPORT,
|
||||
QA
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum TicketPriority {
|
||||
LOW,
|
||||
NORMAL,
|
||||
HIGH,
|
||||
CRITICAL
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum TicketStatus {
|
||||
OPEN,
|
||||
ASSIGNED,
|
||||
IN_PROGRESS,
|
||||
RESOLVED,
|
||||
CLOSED
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum TrainingStatus {
|
||||
NOT_STARTED,
|
||||
ONGOING,
|
||||
COMPLETED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum WeeklyPlanStatus {
|
||||
DRAFT, ACTIVE, CLOSED
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.srsystem.entity.enums;
|
||||
|
||||
public enum WorkLocationType {
|
||||
ON_SITE,
|
||||
REMOTE,
|
||||
HYBRID
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.srsystem.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class BusinessException extends RuntimeException {
|
||||
private final ErrorCode errorCode;
|
||||
|
||||
public BusinessException(ErrorCode errorCode) {
|
||||
super(errorCode.getMessage());
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.srsystem.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
@Getter
|
||||
public enum ErrorCode {
|
||||
RESIGNED_EMPLOYEE(HttpStatus.BAD_REQUEST, "EMP_ERR_001", "퇴사 처리된 직원 정보는 수정할 수 없습니다"),
|
||||
LOCKED_REPORT(HttpStatus.BAD_REQUEST, "RPT_ERR_001", "결재 완료 보고서는 수정/삭제할 수 없습니다"),
|
||||
FUTURE_REPORT_DATE(HttpStatus.BAD_REQUEST, "RPT_ERR_002", "미래 날짜의 업무 보고는 작성할 수 없습니다"),
|
||||
CLOSED_TICKET_MOD(HttpStatus.BAD_REQUEST, "TCK_ERR_001", "종료된 티켓은 댓글/수정이 불가능합니다"),
|
||||
ASSIGNEE_REQUIRED(HttpStatus.BAD_REQUEST, "TCK_ERR_002", "담당자 미배정 티켓은 IN_PROGRESS 전환 불가"),
|
||||
EQUIP_NOT_AVAILABLE(HttpStatus.BAD_REQUEST, "EQP_ERR_001", "지급 가능한 재고 상태가 아닙니다"),
|
||||
INVALID_ASSIGNEE(HttpStatus.BAD_REQUEST, "EQP_ERR_002", "재직 중인 직원에게만 장비 지급 가능"),
|
||||
SYSTEM_CONCURRENCY(HttpStatus.CONFLICT, "SYS_ERR_001", "동시성 충돌 - 다시 시도해 주세요"),
|
||||
DUPLICATE_LOGIN_ID(HttpStatus.CONFLICT, "EMP_ERR_002", "이미 사용 중인 로그인 ID 입니다"),
|
||||
INVALID_CREDENTIALS(HttpStatus.UNAUTHORIZED, "AUTH_ERR_001", "아이디 또는 비밀번호가 일치하지 않습니다"),
|
||||
EMPLOYEE_NOT_FOUND(HttpStatus.NOT_FOUND, "EMP_ERR_003", "직원을 찾을 수 없습니다"),
|
||||
TICKET_NOT_FOUND(HttpStatus.NOT_FOUND, "TCK_ERR_003", "티켓을 찾을 수 없습니다"),
|
||||
PROJECT_NOT_FOUND(HttpStatus.NOT_FOUND, "PRJ_ERR_001", "프로젝트를 찾을 수 없습니다"),
|
||||
EQUIPMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "EQP_ERR_003", "장비를 찾을 수 없습니다"),
|
||||
REPORT_NOT_FOUND(HttpStatus.NOT_FOUND, "RPT_ERR_003", "보고서를 찾을 수 없습니다"),
|
||||
INVALID_ROLE(HttpStatus.FORBIDDEN, "AUTH_ERR_002", "권한이 없습니다"),
|
||||
DISPATCH_NOT_FOUND(HttpStatus.NOT_FOUND, "DSP_ERR_001", "파견 이력을 찾을 수 없습니다"),
|
||||
AUTH_INVALID_CREDENTIALS(HttpStatus.BAD_REQUEST, "AUTH_ERR_003", "현재 비밀번호가 일치하지 않습니다"),
|
||||
CODE_NOT_FOUND(HttpStatus.NOT_FOUND, "COD_ERR_001", "공통코드를 찾을 수 없습니다"),
|
||||
PLAN_NOT_FOUND(HttpStatus.NOT_FOUND, "PLN_ERR_001", "주간 계획을 찾을 수 없습니다");
|
||||
|
||||
private final HttpStatus httpStatus;
|
||||
private final String errorCode;
|
||||
private final String message;
|
||||
|
||||
ErrorCode(HttpStatus httpStatus, String errorCode, String message) {
|
||||
this.httpStatus = httpStatus;
|
||||
this.errorCode = errorCode;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user