admin/front 분리

This commit is contained in:
2026-07-20 17:49:24 +09:00
parent 9269278e40
commit ed41ef24cb
673 changed files with 16782 additions and 11277 deletions
+13
View File
@@ -0,0 +1,13 @@
FROM amazoncorretto:21
RUN yum -y install curl && yum clean all
WORKDIR /api
COPY build/libs/api-admin.jar api-admin.jar
ENV JAVA_OPTS=""
EXPOSE 8111
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /api/api-admin.jar"]
+22
View File
@@ -0,0 +1,22 @@
plugins {
id 'org.springframework.boot'
}
dependencies {
implementation project(':01-api-core')
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.0'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
bootJar {
archiveFileName = 'api-admin.jar'
}
@@ -0,0 +1,15 @@
package com.alist.api.admin;
import com.alist.api.core.config.ApiCoreConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
@Import(ApiCoreConfig.class)
@SpringBootApplication
public class ApiAdminApplication {
public static void main(String[] args) {
SpringApplication.run(ApiAdminApplication.class, args);
}
}
@@ -0,0 +1,24 @@
package com.alist.api.admin.config;
import com.alist.api.core.config.cors.CorsCache;
import com.alist.api.core.config.cors.CorsCachePublisher;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class AdminCorsCacheBootstrap {
private final CorsCache corsCache;
private final CorsCachePublisher corsCachePublisher;
public AdminCorsCacheBootstrap(CorsCache corsCache, CorsCachePublisher corsCachePublisher) {
this.corsCache = corsCache;
this.corsCachePublisher = corsCachePublisher;
}
@EventListener(ApplicationReadyEvent.class)
public void initializeCorsCache() {
corsCache.syncFromDatabase();
corsCachePublisher.publishReload();
}
}
@@ -0,0 +1,37 @@
package com.alist.api.admin.config;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenApiConfig {
private static final String SECURITY_SCHEME_NAME = "bearerAuth";
@Value("${api.version}")
private String apiVersion;
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.info(new Info()
.title("A*List Admin API")
.version(apiVersion)
.description("A*List Admin API 입니다.!"))
.addSecurityItem(new SecurityRequirement().addList(SECURITY_SCHEME_NAME))
.components(new Components()
.addSecuritySchemes(SECURITY_SCHEME_NAME,
new SecurityScheme()
.name("Authorization")
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
)
);
}
}
@@ -0,0 +1,93 @@
package com.alist.api.admin.config;
import com.alist.api.core.common.jwt.JwtAccessDeniedHandler;
import com.alist.api.core.common.jwt.JwtAuthenticationEntryPoint;
import com.alist.api.core.common.jwt.JwtAuthenticationFilter;
import com.alist.api.core.common.jwt.JwtTokenProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Slf4j
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Value("${swagger.login.id}")
private String swaggerLoginId;
@Value("${swagger.login.password}")
private String swaggerLoginPassword;
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) {
return new JwtAuthenticationFilter(jwtTokenProvider, "ADMIN", "adminAccessToken");
}
@Bean
@Order(1)
public SecurityFilterChain swaggerFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html")
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults())
.csrf(csrf -> csrf.disable());
return http.build();
}
@Bean
@Order(2)
public SecurityFilterChain apiFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception {
http
.cors(Customizer.withDefaults())
.csrf(csrf -> csrf.disable())
.exceptionHandling(ex -> ex
.authenticationEntryPoint(new JwtAuthenticationEntryPoint())
.accessDeniedHandler(new JwtAccessDeniedHandler())
)
.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/"
, "/actuator/health"
, "/auth/**"
, "/user/add"
, "/tus/file/hook"
, "/tus/file/upload/auth"
, "/file/**"
).permitAll()
.anyRequest().hasRole("ADMIN")
)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails alist = User.builder()
.username(swaggerLoginId)
.password(passwordEncoder().encode(swaggerLoginPassword))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(alist);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@@ -0,0 +1,142 @@
package com.alist.api.admin.modules.auth;
import com.alist.api.admin.modules.auth.form.AuthApiKeyLoginForm;
import com.alist.api.admin.modules.auth.form.AuthLoginForm;
import com.alist.api.admin.modules.auth.service.AuthService;
import com.alist.api.admin.modules.auth.vo.AuthApiKeyLoginVo;
import com.alist.api.admin.modules.auth.vo.AuthLoginCheckedVo;
import com.alist.api.admin.modules.auth.vo.AuthLoginVo;
import com.alist.api.admin.modules.auth.vo.AuthLogoutVo;
import com.alist.api.admin.modules.auth.vo.AuthRefreshVo;
import com.alist.api.admin.modules.auth.form.AuthApiRefreshForm;
import com.alist.api.admin.modules.auth.vo.AuthApiRefreshVo;
import com.alist.api.core.common.response.ApiResponse;
import com.alist.api.core.common.response.ApiResponseCode;
import com.alist.api.core.common.utils.SecurityUtil;
import com.alist.api.core.common.utils.SessionUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CookieValue;
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;
@Tag(
name = "A-1. Admin 로그인"
, description = "관리자 인증 및 토큰 발급 API"
)
@RestController
@RequestMapping("/auth")
public class AuthController {
private final AuthService authService;
public AuthController(AuthService authService) {
this.authService = authService;
}
@Operation(
summary = "관리자 API Key 로그인"
, description = "Swagger 및 외부 API 테스트용으로 Access Token과 Refresh Token을 JSON으로 반환합니다."
)
@PostMapping("/apiKeyLogin")
public ResponseEntity<ApiResponse<AuthApiKeyLoginVo>> apiKeyLogin(
@Valid @RequestBody AuthApiKeyLoginForm authApiKeyLoginForm
) {
AuthApiKeyLoginVo authApiKeyLoginVo = authService.selectApiKeyLogin(authApiKeyLoginForm);
if (authApiKeyLoginVo.getResultCode() == 2003) {
return ApiResponse.entity(authApiKeyLoginVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(authApiKeyLoginVo, ApiResponseCode.CODE_2001, "관리자 Access Token 발급");
}
@Operation(
summary = "관리자 로그인"
, description = "아이디와 비밀번호를 검증하고 HttpOnly Access/Refresh Token 쿠키를 발급합니다."
)
@PostMapping("/login")
public ResponseEntity<ApiResponse<AuthLoginVo>> login(
@Valid @RequestBody AuthLoginForm authLoginForm
, HttpServletResponse response
) {
AuthLoginVo authLoginVo = authService.selectLogin(authLoginForm, response);
if (authLoginVo.getResultCode() == 2003) {
return ApiResponse.entity(authLoginVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(authLoginVo, ApiResponseCode.CODE_2001, "관리자 로그인");
}
@Operation(
summary = "관리자 로그인 상태 확인"
, description = "현재 Access/Refresh Token 쿠키 존재 여부와 로그인한 관리자 정보를 조회합니다."
)
@GetMapping("/loginChecked")
public ResponseEntity<ApiResponse<AuthLoginCheckedVo>> loginChecked(
HttpServletRequest request
) {
String accessToken = SessionUtil.resolveSsoCookieValue(request, "adminAccessToken");
String refreshToken = SessionUtil.resolveSsoCookieValue(request, "adminRefreshToken");
AuthLoginCheckedVo authLoginCheckedVo = authService.selectLoginChecked(SecurityUtil.getLoginUserTokenIdx(), accessToken != null && !accessToken.isBlank(), refreshToken != null && !refreshToken.isBlank());
return ApiResponse.entity(authLoginCheckedVo, ApiResponseCode.CODE_200);
}
@Operation(
summary = "관리자 로그아웃"
, description = "저장된 Refresh Token을 무효화하고 Access/Refresh Token 쿠키를 삭제합니다."
)
@PostMapping("/logout")
public ResponseEntity<ApiResponse<AuthLogoutVo>> logout(
@CookieValue(name = "adminRefreshToken", required = false) String refreshToken
, HttpServletResponse response
) {
AuthLogoutVo authLogoutVo = authService.updateLogout(refreshToken, response);
return ApiResponse.entity(authLogoutVo, ApiResponseCode.CODE_200);
}
@Operation(
summary = "관리자 Access Token 재발급"
, description = "유효한 Refresh Token을 검증하고 Access/Refresh Token 쿠키를 새로 발급합니다."
)
@PostMapping("/refresh")
public ResponseEntity<ApiResponse<AuthRefreshVo>> refresh(
@CookieValue(name = "adminRefreshToken", required = false) String refreshToken
, HttpServletResponse response
) {
AuthRefreshVo authRefreshVo = authService.selectRefresh(refreshToken, response);
if (authRefreshVo.getResultCode() == 401) {
return ApiResponse.entity(authRefreshVo, ApiResponseCode.CODE_401);
}
return ApiResponse.entity(authRefreshVo, ApiResponseCode.CODE_200);
}
@Operation(
summary = "외부 API 관리자 토큰 재발급"
, description = "JSON의 관리자 Refresh Token을 검증하고 새 Access/Refresh Token을 JSON으로 반환합니다. Cookie를 사용하지 않습니다."
)
@PostMapping("/apiRefresh")
public ResponseEntity<ApiResponse<AuthApiRefreshVo>> apiRefresh(
@Valid @RequestBody AuthApiRefreshForm authApiRefreshForm
) {
AuthApiRefreshVo authApiRefreshVo = authService.selectApiRefresh(authApiRefreshForm);
if (authApiRefreshVo.getResultCode() == 401) {
return ApiResponse.entity(authApiRefreshVo, ApiResponseCode.CODE_401);
}
return ApiResponse.entity(authApiRefreshVo, ApiResponseCode.CODE_200, "관리자 API 토큰 재발급");
}
}
@@ -0,0 +1,16 @@
package com.alist.api.admin.modules.auth.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 API Key 로그인 요청")
@Getter
@Setter
public class AuthApiKeyLoginForm {
@Schema(description = "발급된 API Key", example = "ypdz9hl7WAp2D03HC5h9koEC49R6LAkLSuoSdgEwVQA",
requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "API Key는 필수입니다.")
private String userApiKey;
}
@@ -0,0 +1,19 @@
package com.alist.api.admin.modules.auth.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "외부 API 관리자 refresh 요청")
@Getter
@Setter
public class AuthApiRefreshForm {
@Schema(
description = "API Key 로그인 또는 이전 API refresh 응답에서 받은 관리자 Refresh Token"
, example = "eyJhbGciOiJIUzI1NiJ9..."
, requiredMode = Schema.RequiredMode.REQUIRED
)
@NotBlank(message = "관리자 Refresh Token은 필수입니다.")
private String adminRefreshToken;
}
@@ -0,0 +1,19 @@
package com.alist.api.admin.modules.auth.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 로그인 요청")
@Getter
@Setter
public class AuthLoginForm {
@Schema(description = "관리자 아이디", example = "admin", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "아이디는 필수입니다.")
private String id;
@Schema(description = "관리자 비밀번호", example = "password", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "비밀번호는 필수입니다.")
private String password;
}
@@ -0,0 +1,322 @@
package com.alist.api.admin.modules.auth.service;
import com.alist.api.admin.modules.auth.form.AuthApiKeyLoginForm;
import com.alist.api.admin.modules.auth.form.AuthLoginForm;
import com.alist.api.admin.modules.auth.vo.AuthApiKeyLoginVo;
import com.alist.api.admin.modules.auth.vo.AuthLoginCheckedVo;
import com.alist.api.admin.modules.auth.vo.AuthLoginVo;
import com.alist.api.admin.modules.auth.vo.AuthLogoutVo;
import com.alist.api.admin.modules.auth.vo.AuthRefreshVo;
import com.alist.api.admin.modules.auth.form.AuthApiRefreshForm;
import com.alist.api.admin.modules.auth.vo.AuthApiRefreshVo;
import com.alist.api.core.common.jwt.JwtTokenProvider;
import com.alist.api.core.common.utils.SessionUtil;
import com.alist.api.core.modules.bespoke.testUser.dto.TestUserLoginViewDto;
import com.alist.api.core.modules.bespoke.testUser.service.TestUserAuthService;
import com.alist.api.core.modules.bespoke.testUser.vo.TestUserLoginViewVo;
import com.alist.api.core.modules.bespoke.testUserToken.dto.TestUserTokenApiKeyLoginViewDto;
import com.alist.api.core.modules.bespoke.testUserToken.dto.TestUserTokenLoginCheckedViewDto;
import com.alist.api.core.modules.bespoke.testUserToken.dto.TestUserTokenRefreshViewDto;
import com.alist.api.core.modules.bespoke.testUserToken.service.TestUserTokenAuthService;
import com.alist.api.core.modules.bespoke.testUserToken.vo.TestUserTokenApiKeyLoginViewVo;
import com.alist.api.core.modules.bespoke.testUserToken.vo.TestUserTokenLoginCheckedViewVo;
import com.alist.api.core.modules.bespoke.testUserToken.vo.TestUserTokenRefreshViewVo;
import com.alist.api.core.modules.standard.testUser.dto.TestUserModifyDto;
import com.alist.api.core.modules.standard.testUser.service.TestUserService;
import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenModifyDto;
import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
@Slf4j
@Service
public class AuthService {
private static final String ADMIN_USER_TYPE = "A";
private static final String ADMIN_ROLE = "ADMIN";
private static final String ADMIN_SCOPE = "ADMIN";
private static final String REFRESH_TOKEN_TYPE = "REFRESH";
private static final String ACCESS_TOKEN_COOKIE_NAME = "adminAccessToken";
private static final String REFRESH_TOKEN_COOKIE_NAME = "adminRefreshToken";
@Value("${cookie.secure}")
private boolean cookieSecure;
@Value("${cookie.domain:}")
private String cookieDomain;
@Value("${cookie.same-site:Lax}")
private String cookieSameSite;
@Value("${jwt.access-token-validity-seconds}")
private long accessTokenValiditySeconds;
@Value("${jwt.refresh-token-validity-seconds}")
private long refreshTokenValiditySeconds;
private final TestUserAuthService testUserAuthService;
private final TestUserTokenAuthService testUserTokenAuthService;
private final TestUserService testUserService;
private final TestUserTokenService testUserTokenService;
private final JwtTokenProvider jwtTokenProvider;
private final PasswordEncoder passwordEncoder;
public AuthService(TestUserAuthService testUserAuthService
, TestUserTokenAuthService testUserTokenAuthService
, TestUserService testUserService
, TestUserTokenService testUserTokenService
, JwtTokenProvider jwtTokenProvider
, PasswordEncoder passwordEncoder
) {
this.testUserAuthService = testUserAuthService;
this.testUserTokenAuthService = testUserTokenAuthService;
this.testUserService = testUserService;
this.testUserTokenService = testUserTokenService;
this.jwtTokenProvider = jwtTokenProvider;
this.passwordEncoder = passwordEncoder;
}
@Transactional
public AuthLoginVo selectLogin(AuthLoginForm authLoginForm, HttpServletResponse response) {
TestUserLoginViewDto testUserLoginViewDto = new TestUserLoginViewDto();
testUserLoginViewDto.setId(authLoginForm.getId().trim());
testUserLoginViewDto.setUserType(ADMIN_USER_TYPE);
TestUserLoginViewVo testUserLoginViewVo =
testUserAuthService.testUserLoginView(testUserLoginViewDto);
if (testUserLoginViewVo == null
|| !passwordEncoder.matches(
authLoginForm.getPassword()
, testUserLoginViewVo.getPassword()
)) {
return createLoginFailVo();
}
issueTokenCookie(testUserLoginViewVo.getUserTokenIdx(), response);
updateLastLoginAt(testUserLoginViewVo.getUserIdx());
AuthLoginVo authLoginVo = new AuthLoginVo();
authLoginVo.setLogin(true);
authLoginVo.setUserIdx(testUserLoginViewVo.getUserIdx());
authLoginVo.setId(testUserLoginViewVo.getId());
authLoginVo.setUserRole(testUserLoginViewVo.getUserRole());
authLoginVo.setUserType(testUserLoginViewVo.getUserType());
authLoginVo.setResultCode(2001);
return authLoginVo;
}
@Transactional
public AuthApiKeyLoginVo selectApiKeyLogin(AuthApiKeyLoginForm authApiKeyLoginForm) {
TestUserTokenApiKeyLoginViewDto testUserTokenApiKeyLoginViewDto = new TestUserTokenApiKeyLoginViewDto();
testUserTokenApiKeyLoginViewDto.setUserApiKey(authApiKeyLoginForm.getUserApiKey().trim());
testUserTokenApiKeyLoginViewDto.setUserType(ADMIN_USER_TYPE);
TestUserTokenApiKeyLoginViewVo testUserTokenApiKeyLoginViewVo = testUserTokenAuthService.testUserTokenApiKeyLoginView(testUserTokenApiKeyLoginViewDto);
if (testUserTokenApiKeyLoginViewVo == null) {
return createApiKeyLoginFailVo();
}
String adminAccessToken = jwtTokenProvider.createAdminAccessToken(testUserTokenApiKeyLoginViewVo.getUserTokenIdx());
String adminRefreshToken = jwtTokenProvider.createAdminRefreshToken(testUserTokenApiKeyLoginViewVo.getUserTokenIdx());
updateRefreshToken(testUserTokenApiKeyLoginViewVo.getUserTokenIdx(), adminRefreshToken);
updateLastLoginAt(testUserTokenApiKeyLoginViewVo.getUserIdx());
AuthApiKeyLoginVo authApiKeyLoginVo = new AuthApiKeyLoginVo();
authApiKeyLoginVo.setLogin(true);
authApiKeyLoginVo.setUserIdx(testUserTokenApiKeyLoginViewVo.getUserIdx());
authApiKeyLoginVo.setId(testUserTokenApiKeyLoginViewVo.getId());
authApiKeyLoginVo.setUserRole(testUserTokenApiKeyLoginViewVo.getUserRole());
authApiKeyLoginVo.setUserType(testUserTokenApiKeyLoginViewVo.getUserType());
authApiKeyLoginVo.setAdminAccessToken(adminAccessToken);
authApiKeyLoginVo.setAdminRefreshToken(adminRefreshToken);
authApiKeyLoginVo.setResultCode(2001);
return authApiKeyLoginVo;
}
@Transactional(readOnly = true)
public AuthLoginCheckedVo selectLoginChecked(Integer userTokenIdx, boolean accessTokenExists, boolean refreshTokenExists) {
AuthLoginCheckedVo authLoginCheckedVo = new AuthLoginCheckedVo();
authLoginCheckedVo.setAccessToken(accessTokenExists);
authLoginCheckedVo.setRefreshToken(refreshTokenExists);
if (userTokenIdx == null) {
authLoginCheckedVo.setLoggedIn(false);
return authLoginCheckedVo;
}
TestUserTokenLoginCheckedViewDto testUserTokenLoginCheckedViewDto = new TestUserTokenLoginCheckedViewDto();
testUserTokenLoginCheckedViewDto.setUserTokenIdx(userTokenIdx);
testUserTokenLoginCheckedViewDto.setUserType(ADMIN_USER_TYPE);
TestUserTokenLoginCheckedViewVo testUserTokenLoginCheckedViewVo = testUserTokenAuthService.testUserTokenLoginCheckedView(testUserTokenLoginCheckedViewDto);
if (testUserTokenLoginCheckedViewVo == null) {
authLoginCheckedVo.setLoggedIn(false);
return authLoginCheckedVo;
}
authLoginCheckedVo.setLoggedIn(true);
authLoginCheckedVo.setUserIdx(testUserTokenLoginCheckedViewVo.getUserIdx());
authLoginCheckedVo.setUserTokenIdx(testUserTokenLoginCheckedViewVo.getUserTokenIdx());
authLoginCheckedVo.setId(testUserTokenLoginCheckedViewVo.getId());
authLoginCheckedVo.setUserRole(testUserTokenLoginCheckedViewVo.getUserRole());
authLoginCheckedVo.setUserType(testUserTokenLoginCheckedViewVo.getUserType());
return authLoginCheckedVo;
}
@Transactional
public AuthRefreshVo selectRefresh(String refreshToken, HttpServletResponse response) {
AuthRefreshVo authRefreshVo = new AuthRefreshVo();
TestUserTokenRefreshViewVo testUserTokenRefreshViewVo = selectRefreshTokenOwner(refreshToken);
if (testUserTokenRefreshViewVo == null) {
authRefreshVo.setRefreshed(false);
authRefreshVo.setResultCode(401);
return authRefreshVo;
}
issueTokenCookie(testUserTokenRefreshViewVo.getUserTokenIdx(), response);
authRefreshVo.setRefreshed(true);
authRefreshVo.setResultCode(200);
return authRefreshVo;
}
@Transactional
public AuthApiRefreshVo selectApiRefresh(AuthApiRefreshForm authApiRefreshForm) {
AuthApiRefreshVo authApiRefreshVo = new AuthApiRefreshVo();
String previousRefreshToken = authApiRefreshForm.getAdminRefreshToken().trim();
TestUserTokenRefreshViewVo testUserTokenRefreshViewVo = selectRefreshTokenOwner(previousRefreshToken);
if (testUserTokenRefreshViewVo == null) {
authApiRefreshVo.setRefreshed(false);
authApiRefreshVo.setResultCode(401);
return authApiRefreshVo;
}
String adminAccessToken = jwtTokenProvider.createAdminAccessToken(testUserTokenRefreshViewVo.getUserTokenIdx());
String adminRefreshToken = jwtTokenProvider.createAdminRefreshToken(testUserTokenRefreshViewVo.getUserTokenIdx());
updateRefreshToken(testUserTokenRefreshViewVo.getUserTokenIdx(), adminRefreshToken);
authApiRefreshVo.setRefreshed(true);
authApiRefreshVo.setAdminAccessToken(adminAccessToken);
authApiRefreshVo.setAdminRefreshToken(adminRefreshToken);
authApiRefreshVo.setResultCode(200);
return authApiRefreshVo;
}
@Transactional
public AuthLogoutVo updateLogout(String refreshToken, HttpServletResponse response) {
TestUserTokenRefreshViewVo testUserTokenRefreshViewVo = selectRefreshTokenOwner(refreshToken);
if (testUserTokenRefreshViewVo != null) {
TestUserTokenModifyDto testUserTokenModifyDto = new TestUserTokenModifyDto();
testUserTokenModifyDto.setUserTokenIdx(testUserTokenRefreshViewVo.getUserTokenIdx());
testUserTokenModifyDto.setRefreshToken("");
testUserTokenService.updateTestUserTokenModify(testUserTokenModifyDto);
}
SessionUtil.expireCookie(response, ACCESS_TOKEN_COOKIE_NAME, cookieDomain, cookieSecure, cookieSameSite);
SessionUtil.expireCookie(response, REFRESH_TOKEN_COOKIE_NAME, cookieDomain, cookieSecure, cookieSameSite);
AuthLogoutVo authLogoutVo = new AuthLogoutVo();
authLogoutVo.setLoggedOut(true);
return authLogoutVo;
}
private void issueTokenCookie(Integer userTokenIdx, HttpServletResponse response) {
String accessToken = jwtTokenProvider.createAdminAccessToken(userTokenIdx);
String refreshToken = jwtTokenProvider.createAdminRefreshToken(userTokenIdx);
updateRefreshToken(userTokenIdx, refreshToken);
SessionUtil.addTokenCookie(response, ACCESS_TOKEN_COOKIE_NAME, accessToken, cookieDomain, cookieSecure, cookieSameSite, accessTokenValiditySeconds);
SessionUtil.addTokenCookie(response, REFRESH_TOKEN_COOKIE_NAME, refreshToken, cookieDomain, cookieSecure, cookieSameSite, refreshTokenValiditySeconds);
}
private void updateRefreshToken(Integer userTokenIdx, String refreshToken) {
TestUserTokenModifyDto testUserTokenModifyDto = new TestUserTokenModifyDto();
testUserTokenModifyDto.setUserTokenIdx(userTokenIdx);
testUserTokenModifyDto.setRefreshToken(refreshToken);
testUserTokenModifyDto.setExpiresAt(LocalDateTime.now().plusSeconds(refreshTokenValiditySeconds));
testUserTokenService.updateTestUserTokenModify(testUserTokenModifyDto);
}
private void updateLastLoginAt(Integer userIdx) {
TestUserModifyDto testUserModifyDto = new TestUserModifyDto();
testUserModifyDto.setUserIdx(userIdx);
testUserModifyDto.setLastLoginAt(LocalDateTime.now());
testUserService.updateTestUserModify(testUserModifyDto);
}
private TestUserTokenRefreshViewVo selectRefreshTokenOwner(String refreshToken) {
if (!isAdminRefreshToken(refreshToken)) {
return null;
}
Integer userTokenIdx;
try {
userTokenIdx = Integer.valueOf(jwtTokenProvider.getUserTokenIdx(refreshToken));
} catch (NumberFormatException e) {
return null;
}
TestUserTokenRefreshViewDto testUserTokenRefreshViewDto = new TestUserTokenRefreshViewDto();
testUserTokenRefreshViewDto.setUserTokenIdx(userTokenIdx);
testUserTokenRefreshViewDto.setUserType(ADMIN_USER_TYPE);
TestUserTokenRefreshViewVo testUserTokenRefreshViewVo = testUserTokenAuthService.testUserTokenRefreshView(testUserTokenRefreshViewDto);
if (testUserTokenRefreshViewVo == null || !refreshToken.equals(testUserTokenRefreshViewVo.getRefreshToken())) {
return null;
}
return testUserTokenRefreshViewVo;
}
private boolean isAdminRefreshToken(String refreshToken) {
return refreshToken != null
&& jwtTokenProvider.validateToken(refreshToken)
&& ADMIN_ROLE.equals(jwtTokenProvider.getRole(refreshToken))
&& ADMIN_SCOPE.equals(jwtTokenProvider.getScope(refreshToken))
&& REFRESH_TOKEN_TYPE.equals(
jwtTokenProvider.getTokenType(refreshToken)
);
}
private AuthLoginVo createLoginFailVo() {
AuthLoginVo authLoginVo = new AuthLoginVo();
authLoginVo.setLogin(false);
authLoginVo.setResultCode(2003);
return authLoginVo;
}
private AuthApiKeyLoginVo createApiKeyLoginFailVo() {
AuthApiKeyLoginVo authApiKeyLoginVo = new AuthApiKeyLoginVo();
authApiKeyLoginVo.setLogin(false);
authApiKeyLoginVo.setResultCode(2003);
return authApiKeyLoginVo;
}
}
@@ -0,0 +1,22 @@
package com.alist.api.admin.modules.auth.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 API Key 로그인 응답")
@Getter
@Setter
public class AuthApiKeyLoginVo extends AuthLoginVo {
@Schema(
description = "관리자 Access Token. Authorization Bearer 토큰으로 사용합니다."
, example = "eyJhbGciOiJIUzI1NiJ9..."
)
private String adminAccessToken;
@Schema(
description = "관리자 Refresh Token. 관리자 refresh 처리용 토큰입니다."
, example = "eyJhbGciOiJIUzI1NiJ9..."
)
private String adminRefreshToken;
}
@@ -0,0 +1,22 @@
package com.alist.api.admin.modules.auth.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "외부 API 관리자 refresh 응답")
@Getter
@Setter
public class AuthApiRefreshVo extends AuthRefreshVo {
@Schema(
description = "새로 발급된 관리자 Access Token"
, example = "eyJhbGciOiJIUzI1NiJ9..."
)
private String adminAccessToken;
@Schema(
description = "새로 발급된 관리자 Refresh Token. 다음 API refresh 요청에 사용합니다."
, example = "eyJhbGciOiJIUzI1NiJ9..."
)
private String adminRefreshToken;
}
@@ -0,0 +1,34 @@
package com.alist.api.admin.modules.auth.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 로그인 상태 응답")
@Getter
@Setter
public class AuthLoginCheckedVo {
@Schema(description = "로그인 여부", example = "true")
private boolean loggedIn;
@Schema(description = "Access Token 쿠키 존재 여부", example = "true")
private boolean accessToken;
@Schema(description = "Refresh Token 쿠키 존재 여부", example = "true")
private boolean refreshToken;
@Schema(description = "회원 PK", example = "1")
private Integer userIdx;
@Schema(description = "사용자 토큰 PK", example = "1")
private Integer userTokenIdx;
@Schema(description = "관리자 아이디", example = "admin")
private String id;
@Schema(description = "권한 코드", example = "A")
private String userRole;
@Schema(description = "회원 유형", example = "A")
private String userType;
}
@@ -0,0 +1,30 @@
package com.alist.api.admin.modules.auth.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 로그인 응답")
@Getter
@Setter
public class AuthLoginVo {
@Schema(description = "로그인 성공 여부", example = "true")
private boolean login;
@Schema(description = "회원 PK", example = "1")
private Integer userIdx;
@Schema(description = "관리자 아이디", example = "admin")
private String id;
@Schema(description = "권한 코드", example = "A")
private String userRole;
@Schema(description = "회원 유형", example = "A")
private String userType;
@JsonIgnore
@Schema(hidden = true)
private Integer resultCode;
}
@@ -0,0 +1,13 @@
package com.alist.api.admin.modules.auth.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "Admin 로그아웃 응답")
@Getter
@Setter
public class AuthLogoutVo {
@Schema(description = "로그아웃 처리 여부", example = "true")
private boolean loggedOut;
}
@@ -0,0 +1,19 @@
package com.alist.api.admin.modules.auth.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "Admin 리프레시 토큰 발급 응답")
@Getter
@Setter
public class AuthRefreshVo {
@Schema(description = "리프레시 처리 여부", example = "true")
private boolean refreshed;
@JsonIgnore
@Schema(hidden = true)
private Integer resultCode;
}
@@ -0,0 +1,164 @@
package com.alist.api.admin.modules.backOffice;
import com.alist.api.admin.modules.backOffice.form.AdminMemberAddForm;
import com.alist.api.admin.modules.backOffice.form.AdminMemberDeleteForm;
import com.alist.api.admin.modules.backOffice.form.AdminMemberDormantModifyForm;
import com.alist.api.admin.modules.backOffice.form.AdminMemberListForm;
import com.alist.api.admin.modules.backOffice.form.AdminMemberModifyForm;
import com.alist.api.admin.modules.backOffice.form.AdminMemberViewForm;
import com.alist.api.admin.modules.backOffice.form.AdminMemberWithdrawModifyForm;
import com.alist.api.admin.modules.backOffice.service.AdminMemberService;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberAddVo;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberDeleteVo;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberDormantModifyVo;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberListVo;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberModifyVo;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberViewVo;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberWithdrawModifyVo;
import com.alist.api.core.common.response.ApiResponse;
import com.alist.api.core.common.response.ApiResponseCode;
import io.swagger.v3.oas.annotations.Operation;
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.ModelAttribute;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(
name = "B-1. 관리자 계정 관리"
, description = "백오피스 관리자 계정 관리 API"
)
@RestController
@RequestMapping("/backOffice/adminMember")
public class AdminMemberController {
private final AdminMemberService adminMemberService;
public AdminMemberController(AdminMemberService adminMemberService) {
this.adminMemberService = adminMemberService;
}
@Operation(
summary = "관리자 계정 목록 조회"
, description = "검색 조건과 페이징으로 관리자 계정 목록을 조회합니다."
)
@GetMapping("/list")
public ResponseEntity<ApiResponse<AdminMemberListVo>> adminMemberList(
@Valid @ModelAttribute AdminMemberListForm adminMemberListForm
) {
AdminMemberListVo adminMemberListVo = adminMemberService.selectAdminMemberList(adminMemberListForm);
if (adminMemberListVo.getAdminMemberList().isEmpty()) {
return ApiResponse.entity(adminMemberListVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(adminMemberListVo, ApiResponseCode.CODE_2001, "관리자 계정");
}
@Operation(
summary = "관리자 계정 상세 조회"
, description = "회원 PK 기준으로 관리자 계정 상세 정보를 조회합니다."
)
@GetMapping("/view")
public ResponseEntity<ApiResponse<AdminMemberViewVo>> adminMemberView(
@Valid @ModelAttribute AdminMemberViewForm adminMemberViewForm
) {
AdminMemberViewVo adminMemberViewVo = adminMemberService.selectAdminMemberView(adminMemberViewForm);
if (adminMemberViewVo == null) {
return ApiResponse.entity((AdminMemberViewVo) null, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(adminMemberViewVo, ApiResponseCode.CODE_2001, "관리자 계정");
}
@Operation(
summary = "관리자 계정 등록"
, description = "아이디, 비밀번호, 권한, 유형을 입력하여 관리자 계정을 등록합니다."
)
@PostMapping("/add")
public ResponseEntity<ApiResponse<AdminMemberAddVo>> adminMemberAdd(
@Valid @RequestBody AdminMemberAddForm adminMemberAddForm
) {
AdminMemberAddVo adminMemberAddVo = adminMemberService.insertAdminMemberAdd(adminMemberAddForm);
if (adminMemberAddVo.getResultCode() == 2004) {
return ApiResponse.entity(adminMemberAddVo, ApiResponseCode.CODE_2004, "아이디");
}
return ApiResponse.entity(adminMemberAddVo, ApiResponseCode.CODE_2002, "관리자 계정");
}
@Operation(
summary = "관리자 계정 정보 수정"
, description = "이메일, 휴대폰 번호, 권한, 회원 유형을 수정합니다."
)
@PutMapping("/modify")
public ResponseEntity<ApiResponse<AdminMemberModifyVo>> adminMemberModify(
@Valid @RequestBody AdminMemberModifyForm adminMemberModifyForm
) {
AdminMemberModifyVo adminMemberModifyVo = adminMemberService.updateAdminMemberModify(adminMemberModifyForm);
if (!adminMemberModifyVo.isUpdated()) {
return ApiResponse.entity(adminMemberModifyVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(adminMemberModifyVo, ApiResponseCode.CODE_2005, "관리자 계정");
}
@Operation(
summary = "관리자 계정 휴면 상태 변경"
, description = "휴면 여부를 변경합니다. Y면 휴면 처리일을 기록하고 N이면 휴면 처리일을 비웁니다."
)
@PatchMapping("/dormant/modify")
public ResponseEntity<ApiResponse<AdminMemberDormantModifyVo>> adminMemberDormantModify(
@Valid @RequestBody AdminMemberDormantModifyForm adminMemberDormantModifyForm
) {
AdminMemberDormantModifyVo adminMemberDormantModifyVo = adminMemberService.updateAdminMemberDormantModify(adminMemberDormantModifyForm);
if (!adminMemberDormantModifyVo.isUpdated()) {
return ApiResponse.entity(adminMemberDormantModifyVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(adminMemberDormantModifyVo, ApiResponseCode.CODE_2005, "관리자 계정 휴면 상태");
}
@Operation(
summary = "관리자 계정 탈퇴 상태 변경"
, description = "탈퇴 상태를 변경합니다. P 또는 Y면 탈퇴 처리일을 기록하고 N이면 탈퇴 처리일을 비웁니다."
)
@PatchMapping("/withdraw/modify")
public ResponseEntity<ApiResponse<AdminMemberWithdrawModifyVo>> adminMemberWithdrawModify(
@Valid @RequestBody AdminMemberWithdrawModifyForm adminMemberWithdrawModifyForm
) {
AdminMemberWithdrawModifyVo adminMemberWithdrawModifyVo = adminMemberService.updateAdminMemberWithdrawModify(adminMemberWithdrawModifyForm);
if (!adminMemberWithdrawModifyVo.isUpdated()) {
return ApiResponse.entity(adminMemberWithdrawModifyVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(adminMemberWithdrawModifyVo, ApiResponseCode.CODE_2005, "관리자 계정 탈퇴 상태");
}
@Operation(
summary = "관리자 계정 삭제"
, description = "회원 PK 목록을 논리 삭제 처리합니다."
)
@PatchMapping("/delete")
public ResponseEntity<ApiResponse<AdminMemberDeleteVo>> adminMemberDelete(
@Valid @RequestBody AdminMemberDeleteForm adminMemberDeleteForm
) {
AdminMemberDeleteVo adminMemberDeleteVo = adminMemberService.updateAdminMemberDelete(adminMemberDeleteForm);
if (!adminMemberDeleteVo.isDeleted()) {
return ApiResponse.entity(adminMemberDeleteVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(adminMemberDeleteVo, ApiResponseCode.CODE_2005, "관리자 계정");
}
}
@@ -0,0 +1,90 @@
package com.alist.api.admin.modules.backOffice;
import com.alist.api.admin.modules.backOffice.form.CorsAddForm;
import com.alist.api.admin.modules.backOffice.form.CorsDeleteForm;
import com.alist.api.admin.modules.backOffice.form.CorsListForm;
import com.alist.api.admin.modules.backOffice.service.CorsService;
import com.alist.api.admin.modules.backOffice.vo.CorsAddVo;
import com.alist.api.admin.modules.backOffice.vo.CorsDeleteVo;
import com.alist.api.admin.modules.backOffice.vo.CorsListVo;
import com.alist.api.admin.modules.backOffice.vo.CorsReloadVo;
import com.alist.api.core.common.response.ApiResponse;
import com.alist.api.core.common.response.ApiResponseCode;
import io.swagger.v3.oas.annotations.Operation;
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.ModelAttribute;
import org.springframework.web.bind.annotation.PatchMapping;
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 = "B-4. CORS 관리", description = "백오피스 CORS 허용 Origin 관리 API")
@RestController
@RequestMapping("/backOffice/cors")
public class CorsController {
private final CorsService corsService;
public CorsController(CorsService corsService) {
this.corsService = corsService;
}
@Operation(summary = "CORS 허용 Origin 목록 조회", description = "검색 조건과 페이징으로 CORS 허용 Origin 목록을 조회합니다.")
@GetMapping("/list")
public ResponseEntity<ApiResponse<CorsListVo>> corsList(@Valid @ModelAttribute CorsListForm corsListForm) {
CorsListVo corsListVo = corsService.selectCorsList(corsListForm);
if (corsListVo.getCorsList().isEmpty()) {
return ApiResponse.entity(corsListVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(corsListVo, ApiResponseCode.CODE_2001, "CORS 허용 Origin");
}
@Operation(summary = "CORS 허용 Origin 등록", description = "허용 Origin을 등록하고 Front와 Admin의 CORS 캐시를 갱신합니다.")
@PostMapping("/add")
public ResponseEntity<ApiResponse<CorsAddVo>> corsAdd(@Valid @RequestBody CorsAddForm corsAddForm) {
CorsAddVo corsAddVo = corsService.insertCorsAdd(corsAddForm);
if (corsAddVo.getResultCode() == 401) {
return ApiResponse.entity(corsAddVo, ApiResponseCode.CODE_401);
}
if (corsAddVo.getResultCode() == 2004) {
return ApiResponse.entity(corsAddVo, ApiResponseCode.CODE_2004, "CORS 허용 Origin");
}
return ApiResponse.entity(corsAddVo, ApiResponseCode.CODE_2002, "CORS 허용 Origin");
}
@Operation(summary = "CORS 허용 Origin 삭제", description = "허용 Origin을 논리 삭제하고 Front와 Admin의 CORS 캐시를 갱신합니다.")
@PatchMapping("/delete")
public ResponseEntity<ApiResponse<CorsDeleteVo>> corsDelete(@Valid @RequestBody CorsDeleteForm corsDeleteForm) {
CorsDeleteVo corsDeleteVo = corsService.updateCorsDelete(corsDeleteForm);
if (corsDeleteVo.getResultCode() == 401) {
return ApiResponse.entity(corsDeleteVo, ApiResponseCode.CODE_401);
}
if (!corsDeleteVo.isDeleted()) {
return ApiResponse.entity(corsDeleteVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(corsDeleteVo, ApiResponseCode.CODE_2005, "CORS 허용 Origin");
}
@Operation(summary = "CORS Redis 목록 강제 갱신", description = "DB의 허용 Origin 목록을 Redis에 다시 반영하고 Front와 Admin에 갱신 신호를 전송합니다.")
@PostMapping("/reload")
public ResponseEntity<ApiResponse<CorsReloadVo>> corsReload() {
CorsReloadVo corsReloadVo = corsService.updateCorsReload();
if (corsReloadVo.getResultCode() == 401) {
return ApiResponse.entity(corsReloadVo, ApiResponseCode.CODE_401);
}
return ApiResponse.entity(corsReloadVo, ApiResponseCode.CODE_2005, "CORS Redis 목록");
}
}
@@ -0,0 +1,123 @@
package com.alist.api.admin.modules.backOffice;
import com.alist.api.admin.modules.backOffice.form.*;
import com.alist.api.admin.modules.backOffice.service.ScheduleService;
import com.alist.api.admin.modules.backOffice.vo.*;
import com.alist.api.core.common.response.ApiResponse;
import com.alist.api.core.common.response.ApiResponseCode;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@Tag(name = "B-3. 스케줄 관리", description = "백오피스 스케줄 작업 관리 API")
@RestController
@RequestMapping("/backOffice/schedule")
public class ScheduleController {
private final ScheduleService scheduleService;
public ScheduleController(ScheduleService scheduleService) {
this.scheduleService = scheduleService;
}
@Operation(summary = "스케줄 작업 목록 조회", description = "검색 조건과 페이징으로 스케줄 작업 목록을 조회합니다.")
@GetMapping("/list")
public ResponseEntity<ApiResponse<ScheduleListVo>> scheduleList(
@Valid @ModelAttribute ScheduleListForm scheduleListForm
) {
ScheduleListVo scheduleListVo = scheduleService.selectScheduleList(scheduleListForm);
if (scheduleListVo.getScheduleList().isEmpty()) {
return ApiResponse.entity(scheduleListVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(scheduleListVo, ApiResponseCode.CODE_2001, "스케줄 작업");
}
@Operation(summary = "스케줄 작업 상세 조회", description = "스케줄 작업 PK 기준으로 상세 정보를 조회합니다.")
@GetMapping("/view")
public ResponseEntity<ApiResponse<ScheduleViewVo>> scheduleView(
@Valid @ModelAttribute ScheduleViewForm scheduleViewForm
) {
ScheduleViewVo scheduleViewVo = scheduleService.selectScheduleView(scheduleViewForm);
if (scheduleViewVo == null) {
return ApiResponse.entity((ScheduleViewVo) null, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(scheduleViewVo, ApiResponseCode.CODE_2001, "스케줄 작업");
}
@Operation(summary = "스케줄 작업 등록", description = "스케줄 작업을 등록합니다. 작업 코드와 작업 그룹 조합은 중복될 수 없습니다.")
@PostMapping("/add")
public ResponseEntity<ApiResponse<ScheduleAddVo>> scheduleAdd(
@Valid @RequestBody ScheduleAddForm scheduleAddForm
) {
ScheduleAddVo scheduleAddVo = scheduleService.insertScheduleAdd(scheduleAddForm);
if (scheduleAddVo.getResultCode() == 401) {
return ApiResponse.entity(scheduleAddVo, ApiResponseCode.CODE_401);
}
if (scheduleAddVo.getResultCode() == 2004) {
return ApiResponse.entity(scheduleAddVo, ApiResponseCode.CODE_2004, "스케줄 작업");
}
return ApiResponse.entity(scheduleAddVo, ApiResponseCode.CODE_2002, "스케줄 작업");
}
@Operation(summary = "스케줄 작업 수정", description = "스케줄 작업의 기본 정보를 수정합니다.")
@PutMapping("/modify")
public ResponseEntity<ApiResponse<ScheduleModifyVo>> scheduleModify(
@Valid @RequestBody ScheduleModifyForm scheduleModifyForm
) {
ScheduleModifyVo scheduleModifyVo = scheduleService.updateScheduleModify(scheduleModifyForm);
if (scheduleModifyVo.getResultCode() == 401) {
return ApiResponse.entity(scheduleModifyVo, ApiResponseCode.CODE_401);
}
if (scheduleModifyVo.getResultCode() == 2004) {
return ApiResponse.entity(scheduleModifyVo, ApiResponseCode.CODE_2004, "스케줄 작업");
}
if (!scheduleModifyVo.isUpdated()) {
return ApiResponse.entity(scheduleModifyVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(scheduleModifyVo, ApiResponseCode.CODE_2005, "스케줄 작업");
}
@Operation(summary = "스케줄 작업 삭제", description = "스케줄 작업을 논리 삭제 처리합니다.")
@PatchMapping("/delete")
public ResponseEntity<ApiResponse<ScheduleDeleteVo>> scheduleDelete(
@Valid @RequestBody ScheduleDeleteForm scheduleDeleteForm
) {
ScheduleDeleteVo scheduleDeleteVo = scheduleService.updateScheduleDelete(scheduleDeleteForm);
if (scheduleDeleteVo.getResultCode() == 401) {
return ApiResponse.entity(scheduleDeleteVo, ApiResponseCode.CODE_401);
}
if (!scheduleDeleteVo.isDeleted()) {
return ApiResponse.entity(scheduleDeleteVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(scheduleDeleteVo, ApiResponseCode.CODE_2005, "스케줄 작업");
}
@Operation(summary = "스케줄 작업 실행 로그 목록 조회", description = "검색 조건과 페이징으로 스케줄 실행 로그를 조회합니다.")
@GetMapping("/log/list")
public ResponseEntity<ApiResponse<ScheduleLogListVo>> scheduleLogList(
@Valid @ModelAttribute ScheduleLogListForm scheduleLogListForm
) {
ScheduleLogListVo scheduleLogListVo = scheduleService.selectScheduleLogList(scheduleLogListForm);
if (scheduleLogListVo.getScheduleLogList().isEmpty()) {
return ApiResponse.entity(scheduleLogListVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(scheduleLogListVo, ApiResponseCode.CODE_2001, "스케줄 작업 실행 로그");
}
}
@@ -0,0 +1,142 @@
package com.alist.api.admin.modules.backOffice;
import com.alist.api.admin.modules.backOffice.form.SsoAddForm;
import com.alist.api.admin.modules.backOffice.form.SsoDeleteForm;
import com.alist.api.admin.modules.backOffice.form.SsoListForm;
import com.alist.api.admin.modules.backOffice.form.SsoModifyForm;
import com.alist.api.admin.modules.backOffice.form.SsoViewForm;
import com.alist.api.admin.modules.backOffice.service.SsoService;
import com.alist.api.admin.modules.backOffice.vo.SsoAddVo;
import com.alist.api.admin.modules.backOffice.vo.SsoDeleteVo;
import com.alist.api.admin.modules.backOffice.vo.SsoListVo;
import com.alist.api.admin.modules.backOffice.vo.SsoModifyVo;
import com.alist.api.admin.modules.backOffice.vo.SsoViewVo;
import com.alist.api.core.common.response.ApiResponse;
import com.alist.api.core.common.response.ApiResponseCode;
import io.swagger.v3.oas.annotations.Operation;
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.ModelAttribute;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(
name = "B-2. SSO 클라이언트 관리"
, description = "백오피스 SSO 클라이언트 관리 API"
)
@RestController
@RequestMapping("/backOffice/sso")
public class SsoController {
private final SsoService ssoService;
public SsoController(SsoService ssoService) {
this.ssoService = ssoService;
}
@Operation(
summary = "SSO 클라이언트 목록 조회"
, description = "검색 조건과 페이징으로 SSO 클라이언트 목록을 조회합니다."
)
@GetMapping("/list")
public ResponseEntity<ApiResponse<SsoListVo>> ssoList(
@Valid @ModelAttribute SsoListForm ssoListForm
) {
SsoListVo ssoListVo = ssoService.selectSsoList(ssoListForm);
if (ssoListVo.getSsoList().isEmpty()) {
return ApiResponse.entity(ssoListVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(ssoListVo, ApiResponseCode.CODE_2001, "SSO 클라이언트");
}
@Operation(
summary = "SSO 클라이언트 상세 조회"
, description = "SSO 클라이언트 PK 기준으로 상세 정보를 조회합니다."
)
@GetMapping("/view")
public ResponseEntity<ApiResponse<SsoViewVo>> ssoView(
@Valid @ModelAttribute SsoViewForm ssoViewForm
) {
SsoViewVo ssoViewVo = ssoService.selectSsoView(ssoViewForm);
if (ssoViewVo == null) {
return ApiResponse.entity((SsoViewVo) null, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(ssoViewVo, ApiResponseCode.CODE_2001, "SSO 클라이언트");
}
@Operation(
summary = "SSO 클라이언트 등록"
, description = "새 SSO 클라이언트를 등록합니다. CLIENT_ID 중복 시 등록할 수 없습니다."
)
@PostMapping("/add")
public ResponseEntity<ApiResponse<SsoAddVo>> ssoAdd(
@Valid @RequestBody SsoAddForm ssoAddForm
) {
SsoAddVo ssoAddVo = ssoService.insertSsoAdd(ssoAddForm);
if (ssoAddVo.getResultCode() == 401) {
return ApiResponse.entity(ssoAddVo, ApiResponseCode.CODE_401);
}
if (ssoAddVo.getResultCode() == 2004) {
return ApiResponse.entity(ssoAddVo, ApiResponseCode.CODE_2004, "SSO 클라이언트");
}
return ApiResponse.entity(ssoAddVo, ApiResponseCode.CODE_2002, "SSO 클라이언트");
}
@Operation(
summary = "SSO 클라이언트 수정"
, description = "SSO 클라이언트의 식별자, 이름, Redirect URI, 사용 여부를 수정합니다."
)
@PutMapping("/modify")
public ResponseEntity<ApiResponse<SsoModifyVo>> ssoModify(
@Valid @RequestBody SsoModifyForm ssoModifyForm
) {
SsoModifyVo ssoModifyVo = ssoService.updateSsoModify(ssoModifyForm);
if (ssoModifyVo.getResultCode() == 401) {
return ApiResponse.entity(ssoModifyVo, ApiResponseCode.CODE_401);
}
if (ssoModifyVo.getResultCode() == 2004) {
return ApiResponse.entity(ssoModifyVo, ApiResponseCode.CODE_2004, "SSO 클라이언트");
}
if (!ssoModifyVo.isUpdated()) {
return ApiResponse.entity(ssoModifyVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(ssoModifyVo, ApiResponseCode.CODE_2005, "SSO 클라이언트");
}
@Operation(
summary = "SSO 클라이언트 삭제"
, description = "SSO 클라이언트를 논리 삭제 처리합니다."
)
@PatchMapping("/delete")
public ResponseEntity<ApiResponse<SsoDeleteVo>> ssoDelete(
@Valid @RequestBody SsoDeleteForm ssoDeleteForm
) {
SsoDeleteVo ssoDeleteVo = ssoService.updateSsoDelete(ssoDeleteForm);
if (ssoDeleteVo.getResultCode() == 401) {
return ApiResponse.entity(ssoDeleteVo, ApiResponseCode.CODE_401);
}
if (!ssoDeleteVo.isDeleted()) {
return ApiResponse.entity(ssoDeleteVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(ssoDeleteVo, ApiResponseCode.CODE_2005, "SSO 클라이언트");
}
}
@@ -0,0 +1,55 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 계정 등록 요청")
@Getter
@Setter
public class AdminMemberAddForm {
@Schema(description = "아이디", example = "admin01", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "아이디를 입력해주세요.")
private String id;
@Schema(description = "비밀번호. 영문과 숫자를 포함한 8~64자", example = "pass1234", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "비밀번호를 입력해주세요.")
@Size(min = 8, max = 64, message = "비밀번호는 8~64자여야 합니다.")
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)\\S+$", message = "비밀번호는 영문과 숫자를 포함하고 공백이 없어야 합니다.")
private String password;
@Schema(description = "회원 권한. 관리자: A, 교사: CCM/SCM/I, 학생: R/E", example = "A", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "회원 권한을 입력해주세요.")
@Pattern(regexp = "^(CCM|SCM|I|R|E|A)$", message = "회원 권한 값을 확인해주세요.")
private String userRole;
@Schema(description = "회원 유형. A: 관리자, T: 교사, S: 학생", example = "A", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "회원 유형을 입력해주세요.")
@Pattern(regexp = "^(A|T|S)$", message = "회원 유형은 A, T, S만 가능합니다.")
private String userType;
@AssertTrue(message = "회원 유형과 권한 조합을 확인해주세요.")
@Schema(hidden = true)
public boolean isValidUserTypeAndRole() {
if (userType == null || userRole == null) {
return true;
}
if ("A".equals(userType)) {
return "A".equals(userRole);
}
if ("T".equals(userType)) {
return "CCM".equals(userRole)
|| "SCM".equals(userRole)
|| "I".equals(userRole);
}
return "S".equals(userType)
&& ("R".equals(userRole) || "E".equals(userRole));
}
}
@@ -0,0 +1,17 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 계정 삭제 요청")
@Getter
@Setter
public class AdminMemberDeleteForm {
@Schema(description = "삭제할 회원 PK 목록. 콤마로 구분", example = "16,17,18", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "삭제할 회원 PK를 입력해주세요.")
@Pattern(regexp = "^\\d+(,\\d+)*$", message = "회원 PK 목록은 16,17,18 형식으로 입력해주세요.")
private String userIdxList;
}
@@ -0,0 +1,22 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 계정 휴면 상태 변경 요청")
@Getter
@Setter
public class AdminMemberDormantModifyForm {
@Schema(description = "회원 PK", example = "16", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "회원 PK는 필수입니다.")
private Integer userIdx;
@Schema(description = "휴면 여부. Y: 휴면, N: 정상", example = "Y", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "휴면 여부는 필수입니다.")
@Pattern(regexp = "^[YN]$", message = "휴면 여부는 Y 또는 N만 가능합니다.")
private String dormantYn;
}
@@ -0,0 +1,26 @@
package com.alist.api.admin.modules.backOffice.form;
import com.alist.api.core.common.paging.PagingRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 계정 목록 검색 조건")
@Getter
@Setter
public class AdminMemberListForm extends PagingRequest {
@Schema(description = "검색어. 아이디, 이메일, 휴대폰 번호 검색", example = "admin")
private String keyword;
@Schema(description = "회원 권한 코드", example = "A")
private String userRole;
@Schema(description = "회원 유형 코드. A: 관리자, T: 교사, S: 학생", example = "A")
private String userType;
@Schema(description = "휴면 여부. N: 정상, Y: 휴면", example = "N")
private String dormantYn;
@Schema(description = "탈퇴 상태. N: 정상, P: 대기, Y: 완료", example = "N")
private String withdrawStatus;
}
@@ -0,0 +1,42 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 계정 정보 수정 요청")
@Getter
@Setter
public class AdminMemberModifyForm {
@Schema(description = "회원 PK", example = "16", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "회원 PK는 필수입니다.")
private Integer userIdx;
@Schema(description = "이메일", example = "admin@alist.co.kr")
@Email(message = "이메일 형식을 확인해주세요.")
private String email;
@Schema(description = "휴대폰 번호", example = "01012345678")
private String hp;
@Schema(description = "회원 권한", example = "A")
@Pattern(regexp = "^(CCM|SCM|I|R|E|A)?$", message = "회원 권한 값을 확인해주세요.")
private String userRole;
@Schema(description = "회원 유형. A: 관리자, T: 교사, S: 학생", example = "A")
@Pattern(regexp = "^(A|T|S)?$", message = "회원 유형은 A, T, S만 가능합니다.")
private String userType;
@AssertTrue(message = "수정할 정보를 하나 이상 입력해주세요.")
@Schema(hidden = true)
public boolean isModifyValue() {
return (email != null && !email.isBlank())
|| (hp != null && !hp.isBlank())
|| (userRole != null && !userRole.isBlank())
|| (userType != null && !userType.isBlank());
}
}
@@ -0,0 +1,15 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 계정 상세 조회 요청")
@Getter
@Setter
public class AdminMemberViewForm {
@Schema(description = "회원 PK", example = "16", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "회원 PK는 필수입니다.")
private Integer userIdx;
}
@@ -0,0 +1,22 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 계정 탈퇴 상태 변경 요청")
@Getter
@Setter
public class AdminMemberWithdrawModifyForm {
@Schema(description = "회원 PK", example = "16", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "회원 PK는 필수입니다.")
private Integer userIdx;
@Schema(description = "탈퇴 상태. N: 정상, P: 대기, Y: 완료", example = "P", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "탈퇴 상태는 필수입니다.")
@Pattern(regexp = "^[NPY]$", message = "탈퇴 상태는 N, P, Y만 가능합니다.")
private String withdrawStatus;
}
@@ -0,0 +1,20 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "CORS 허용 Origin 등록 요청")
@Getter
@Setter
public class CorsAddForm {
@Schema(description = "API 토큰 PK. 미입력 시 전체 적용", example = "0")
private Integer userTokenIdx;
@NotBlank(message = "허용 Origin을 입력해주세요.")
@Size(max = 255)
@Schema(description = "허용 Origin", example = "https://admin.alist.co.kr", requiredMode = Schema.RequiredMode.REQUIRED)
private String allowedOrigin;
}
@@ -0,0 +1,15 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "CORS 허용 Origin 삭제 요청")
@Getter
@Setter
public class CorsDeleteForm {
@NotNull(message = "CORS 설정 PK를 입력해주세요.")
@Schema(description = "CORS 설정 PK", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer corsIdx;
}
@@ -0,0 +1,17 @@
package com.alist.api.admin.modules.backOffice.form;
import com.alist.api.core.common.paging.PagingRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "CORS 허용 Origin 목록 검색 조건")
@Getter
@Setter
public class CorsListForm extends PagingRequest {
@Schema(description = "API 토큰 PK. 0은 전체 적용 Origin", example = "0")
private Integer userTokenIdx;
@Schema(description = "허용 Origin 검색어", example = "https://admin.alist.co.kr")
private String allowedOrigin;
}
@@ -0,0 +1,22 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "스케줄 작업 등록 요청")
@Getter
@Setter
public class ScheduleAddForm {
@NotBlank(message = "작업 코드를 입력해주세요.") @Size(max = 100) @Schema(description = "작업 코드", example = "DAILY_LEARNING_SUMMARY", requiredMode = Schema.RequiredMode.REQUIRED) private String jobName;
@Size(max = 50) @Schema(description = "작업 그룹. 미입력 시 DEFAULT", example = "DEFAULT") private String jobGroup;
@Size(max = 255) @Schema(description = "작업 설명", example = "일별 학습 통계 집계") private String jobDescription;
@NotBlank(message = "크론식을 입력해주세요.") @Size(max = 100) @Schema(description = "크론식", example = "0 0 2 * * *", requiredMode = Schema.RequiredMode.REQUIRED) private String cronExpr;
@Size(max = 50) @Schema(description = "타임존. 미입력 시 Asia/Seoul", example = "Asia/Seoul") private String timezoneId;
@Min(value = 1, message = "최대 실행 허용 시간은 1초 이상이어야 합니다.") @Schema(description = "최대 실행 허용 시간(초). 미입력 시 1800", example = "1800") private Integer maxRunningSeconds;
@Pattern(regexp = "^(Y|N)$", message = "사용 여부는 Y 또는 N만 가능합니다.") @Schema(description = "사용 여부. Y: 사용, N: 미사용. 미입력 시 Y", example = "Y") private String useYn;
}
@@ -0,0 +1,13 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "스케줄 작업 삭제 요청")
@Getter
@Setter
public class ScheduleDeleteForm {
@NotNull(message = "스케줄 작업 PK를 입력해주세요.") @Schema(description = "스케줄 작업 PK", example = "9", requiredMode = Schema.RequiredMode.REQUIRED) private Long scheduleJobIdx;
}
@@ -0,0 +1,16 @@
package com.alist.api.admin.modules.backOffice.form;
import com.alist.api.core.common.paging.PagingRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "스케줄 작업 목록 검색 조건")
@Getter
@Setter
public class ScheduleListForm extends PagingRequest {
@Schema(description = "검색어. 작업 코드와 작업 설명 검색", example = "DAILY") private String keyword;
@Schema(description = "작업 그룹", example = "DEFAULT") private String jobGroup;
@Schema(description = "마지막 실행 상태. 0: IDLE, 1: RUNNING, 2: SUCCESS, 3: SKIP, 4: FAIL", example = "2") private Short lastStatus;
@Schema(description = "사용 여부. Y: 사용, N: 미사용", example = "Y") private String useYn;
}
@@ -0,0 +1,16 @@
package com.alist.api.admin.modules.backOffice.form;
import com.alist.api.core.common.paging.PagingRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "스케줄 작업 실행 로그 목록 검색 조건")
@Getter
@Setter
public class ScheduleLogListForm extends PagingRequest {
@Schema(description = "스케줄 작업 PK", example = "9") private Long scheduleJobIdx;
@Schema(description = "실행 상태. 1: RUNNING, 2: SUCCESS, 3: SKIP, 4: FAIL", example = "2") private Short status;
@Schema(description = "실행 시작일 검색 시작값", example = "2026-05-01") private String startDateFrom;
@Schema(description = "실행 시작일 검색 종료값", example = "2026-05-28") private String startDateTo;
}
@@ -0,0 +1,24 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "스케줄 작업 수정 요청")
@Getter
@Setter
public class ScheduleModifyForm {
@NotNull(message = "스케줄 작업 PK를 입력해주세요.") @Schema(description = "스케줄 작업 PK", example = "9", requiredMode = Schema.RequiredMode.REQUIRED) private Long scheduleJobIdx;
@NotBlank(message = "작업 코드를 입력해주세요.") @Size(max = 100) @Schema(description = "작업 코드", example = "DAILY_LEARNING_SUMMARY", requiredMode = Schema.RequiredMode.REQUIRED) private String jobName;
@Size(max = 50) @Schema(description = "작업 그룹. 미입력 시 DEFAULT", example = "DEFAULT") private String jobGroup;
@Size(max = 255) @Schema(description = "작업 설명", example = "일별 학습 통계 집계") private String jobDescription;
@NotBlank(message = "크론식을 입력해주세요.") @Size(max = 100) @Schema(description = "크론식", example = "0 0 2 * * *", requiredMode = Schema.RequiredMode.REQUIRED) private String cronExpr;
@Size(max = 50) @Schema(description = "타임존. 미입력 시 Asia/Seoul", example = "Asia/Seoul") private String timezoneId;
@Min(value = 1, message = "최대 실행 허용 시간은 1초 이상이어야 합니다.") @Schema(description = "최대 실행 허용 시간(초). 미입력 시 1800", example = "1800") private Integer maxRunningSeconds;
@Pattern(regexp = "^(Y|N)$", message = "사용 여부는 Y 또는 N만 가능합니다.") @Schema(description = "사용 여부. Y: 사용, N: 미사용. 미입력 시 Y", example = "Y") private String useYn;
}
@@ -0,0 +1,14 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "스케줄 작업 상세 조회 요청")
@Getter
@Setter
public class ScheduleViewForm {
@NotNull(message = "스케줄 작업 PK를 입력해주세요.")
@Schema(description = "스케줄 작업 PK", example = "9", requiredMode = Schema.RequiredMode.REQUIRED) private Long scheduleJobIdx;
}
@@ -0,0 +1,36 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "SSO 클라이언트 등록 요청")
@Getter
@Setter
public class SsoAddForm {
@NotBlank(message = "클라이언트 식별자를 입력해주세요.")
@Size(max = 100, message = "클라이언트 식별자는 100자 이하여야 합니다.")
@Schema(description = "클라이언트 식별자", example = "a-campus-client", requiredMode = Schema.RequiredMode.REQUIRED)
private String clientId;
@NotBlank(message = "클라이언트명을 입력해주세요.")
@Size(max = 200, message = "클라이언트명은 200자 이하여야 합니다.")
@Schema(description = "클라이언트명", example = "a-campus.co.kr", requiredMode = Schema.RequiredMode.REQUIRED)
private String clientName;
@NotBlank(message = "Redirect URI를 입력해주세요.")
@Size(max = 500, message = "Redirect URI는 500자 이하여야 합니다.")
@Schema(description = "허용 Redirect URI", example = "https://a-campus.co.kr/auth/callback", requiredMode = Schema.RequiredMode.REQUIRED)
private String redirectUri;
@Size(max = 500, message = "비고는 500자 이하여야 합니다.")
@Schema(description = "비고", example = "운영")
private String memo;
@Pattern(regexp = "^(Y|N)$", message = "사용 여부는 Y 또는 N만 가능합니다.")
@Schema(description = "사용 여부. Y: 사용, N: 미사용. 미입력 시 Y", example = "Y")
private String useYn;
}
@@ -0,0 +1,15 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "SSO 클라이언트 삭제 요청")
@Getter
@Setter
public class SsoDeleteForm {
@NotNull(message = "SSO 클라이언트 PK를 입력해주세요.")
@Schema(description = "SSO 클라이언트 PK", example = "19", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer ssoClientIdx;
}
@@ -0,0 +1,17 @@
package com.alist.api.admin.modules.backOffice.form;
import com.alist.api.core.common.paging.PagingRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "SSO 클라이언트 목록 검색 조건")
@Getter
@Setter
public class SsoListForm extends PagingRequest {
@Schema(description = "검색어. 클라이언트 ID, 클라이언트명, Redirect URI, 비고 검색", example = "a-campus")
private String keyword;
@Schema(description = "사용 여부. Y: 사용, N: 미사용", example = "Y")
private String useYn;
}
@@ -0,0 +1,41 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "SSO 클라이언트 수정 요청")
@Getter
@Setter
public class SsoModifyForm {
@NotNull(message = "SSO 클라이언트 PK를 입력해주세요.")
@Schema(description = "SSO 클라이언트 PK", example = "19", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer ssoClientIdx;
@NotBlank(message = "클라이언트 식별자를 입력해주세요.")
@Size(max = 100, message = "클라이언트 식별자는 100자 이하여야 합니다.")
@Schema(description = "클라이언트 식별자", example = "a-campus-client", requiredMode = Schema.RequiredMode.REQUIRED)
private String clientId;
@NotBlank(message = "클라이언트명을 입력해주세요.")
@Size(max = 200, message = "클라이언트명은 200자 이하여야 합니다.")
@Schema(description = "클라이언트명", example = "a-campus.co.kr", requiredMode = Schema.RequiredMode.REQUIRED)
private String clientName;
@NotBlank(message = "Redirect URI를 입력해주세요.")
@Size(max = 500, message = "Redirect URI는 500자 이하여야 합니다.")
@Schema(description = "허용 Redirect URI", example = "https://a-campus.co.kr/auth/callback", requiredMode = Schema.RequiredMode.REQUIRED)
private String redirectUri;
@Size(max = 500, message = "비고는 500자 이하여야 합니다.")
@Schema(description = "비고", example = "운영")
private String memo;
@Pattern(regexp = "^(Y|N)$", message = "사용 여부는 Y 또는 N만 가능합니다.")
@Schema(description = "사용 여부. Y: 사용, N: 미사용. 미입력 시 Y", example = "Y")
private String useYn;
}
@@ -0,0 +1,15 @@
package com.alist.api.admin.modules.backOffice.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "SSO 클라이언트 상세 조회 요청")
@Getter
@Setter
public class SsoViewForm {
@NotNull(message = "SSO 클라이언트 PK를 입력해주세요.")
@Schema(description = "SSO 클라이언트 PK", example = "19", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer ssoClientIdx;
}
@@ -0,0 +1,310 @@
package com.alist.api.admin.modules.backOffice.service;
import com.alist.api.admin.modules.backOffice.form.AdminMemberAddForm;
import com.alist.api.admin.modules.backOffice.form.AdminMemberDeleteForm;
import com.alist.api.admin.modules.backOffice.form.AdminMemberDormantModifyForm;
import com.alist.api.admin.modules.backOffice.form.AdminMemberListForm;
import com.alist.api.admin.modules.backOffice.form.AdminMemberModifyForm;
import com.alist.api.admin.modules.backOffice.form.AdminMemberViewForm;
import com.alist.api.admin.modules.backOffice.form.AdminMemberWithdrawModifyForm;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberAddVo;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberDeleteVo;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberDormantModifyVo;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberListItemVo;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberListVo;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberModifyVo;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberViewVo;
import com.alist.api.admin.modules.backOffice.vo.AdminMemberWithdrawModifyVo;
import com.alist.api.core.common.utils.ApiKeyGenerator;
import com.alist.api.core.modules.bespoke.testUser.dto.TestUserAdminMemberListDto;
import com.alist.api.core.modules.bespoke.testUser.dto.TestUserAdminMemberViewDto;
import com.alist.api.core.modules.bespoke.testUser.service.TestUserAdminMemberService;
import com.alist.api.core.modules.bespoke.testUser.vo.TestUserAdminMemberListItemVo;
import com.alist.api.core.modules.bespoke.testUser.vo.TestUserAdminMemberViewVo;
import com.alist.api.core.modules.standard.testUser.dto.TestUserAddDto;
import com.alist.api.core.modules.standard.testUser.dto.TestUserDeleteDto;
import com.alist.api.core.modules.standard.testUser.dto.TestUserModifyDto;
import com.alist.api.core.modules.standard.testUser.service.TestUserService;
import com.alist.api.core.modules.standard.testUser.vo.TestUserAddVo;
import com.alist.api.core.modules.standard.testUser.vo.TestUserDeleteVo;
import com.alist.api.core.modules.standard.testUser.vo.TestUserModifyVo;
import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenAddDto;
import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenModifyDto;
import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService;
import com.alist.api.core.modules.standard.testUserToken.vo.TestUserTokenAddVo;
import com.alist.api.core.modules.standard.testUserToken.vo.TestUserTokenModifyVo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Service
public class AdminMemberService {
private final TestUserAdminMemberService testUserAdminMemberService;
private final TestUserService testUserService;
private final TestUserTokenService testUserTokenService;
public AdminMemberService(TestUserAdminMemberService testUserAdminMemberService, TestUserService testUserService, TestUserTokenService testUserTokenService) {
this.testUserAdminMemberService = testUserAdminMemberService;
this.testUserService = testUserService;
this.testUserTokenService = testUserTokenService;
}
@Transactional(readOnly = true)
public AdminMemberListVo selectAdminMemberList(AdminMemberListForm adminMemberListForm) {
TestUserAdminMemberListDto testUserAdminMemberListDto = new TestUserAdminMemberListDto();
testUserAdminMemberListDto.setPage(adminMemberListForm.getPage());
testUserAdminMemberListDto.setSize(adminMemberListForm.getSize());
testUserAdminMemberListDto.setKeyword(adminMemberListForm.getKeyword() == null || adminMemberListForm.getKeyword().isBlank() ? null : adminMemberListForm.getKeyword().trim());
testUserAdminMemberListDto.setUserRole(adminMemberListForm.getUserRole() == null || adminMemberListForm.getUserRole().isBlank() ? null : adminMemberListForm.getUserRole().trim());
testUserAdminMemberListDto.setUserType(adminMemberListForm.getUserType() == null || adminMemberListForm.getUserType().isBlank() ? null : adminMemberListForm.getUserType().trim());
testUserAdminMemberListDto.setDormantYn(adminMemberListForm.getDormantYn() == null || adminMemberListForm.getDormantYn().isBlank() ? null : adminMemberListForm.getDormantYn().trim());
testUserAdminMemberListDto.setWithdrawStatus(adminMemberListForm.getWithdrawStatus() == null || adminMemberListForm.getWithdrawStatus().isBlank() ? null : adminMemberListForm.getWithdrawStatus().trim());
int totalCount = testUserAdminMemberService.testUserAdminMemberCount(testUserAdminMemberListDto);
List<TestUserAdminMemberListItemVo> testUserAdminMemberListItemVoList = testUserAdminMemberService.testUserAdminMemberList(testUserAdminMemberListDto);
List<AdminMemberListItemVo> adminMemberList = new ArrayList<>();
for (TestUserAdminMemberListItemVo testUserAdminMemberListItemVo : testUserAdminMemberListItemVoList) {
AdminMemberListItemVo adminMemberListItemVo = new AdminMemberListItemVo();
adminMemberListItemVo.setUserIdx(testUserAdminMemberListItemVo.getUserIdx());
adminMemberListItemVo.setUserTokenIdx(testUserAdminMemberListItemVo.getUserTokenIdx());
adminMemberListItemVo.setId(testUserAdminMemberListItemVo.getId());
adminMemberListItemVo.setEmail(testUserAdminMemberListItemVo.getEmail());
adminMemberListItemVo.setHp(testUserAdminMemberListItemVo.getHp());
adminMemberListItemVo.setUserRole(testUserAdminMemberListItemVo.getUserRole());
adminMemberListItemVo.setUserType(testUserAdminMemberListItemVo.getUserType());
adminMemberListItemVo.setDormantYn(testUserAdminMemberListItemVo.getDormantYn());
adminMemberListItemVo.setDormantAt(testUserAdminMemberListItemVo.getDormantAt());
adminMemberListItemVo.setWithdrawStatus(testUserAdminMemberListItemVo.getWithdrawStatus());
adminMemberListItemVo.setWithdrawAt(testUserAdminMemberListItemVo.getWithdrawAt());
adminMemberListItemVo.setCreateAt(testUserAdminMemberListItemVo.getCreateAt());
adminMemberListItemVo.setUpdateAt(testUserAdminMemberListItemVo.getUpdateAt());
adminMemberList.add(adminMemberListItemVo);
}
AdminMemberListVo adminMemberListVo = new AdminMemberListVo();
adminMemberListVo.setAdminMemberList(adminMemberList);
adminMemberListVo.setPaging(testUserAdminMemberListDto, totalCount);
return adminMemberListVo;
}
@Transactional(readOnly = true)
public AdminMemberViewVo selectAdminMemberView(AdminMemberViewForm adminMemberViewForm) {
TestUserAdminMemberViewDto testUserAdminMemberViewDto = new TestUserAdminMemberViewDto();
testUserAdminMemberViewDto.setUserIdx(adminMemberViewForm.getUserIdx());
TestUserAdminMemberViewVo testUserAdminMemberViewVo = testUserAdminMemberService.testUserAdminMemberView(testUserAdminMemberViewDto);
if (testUserAdminMemberViewVo == null) {
return null;
}
AdminMemberViewVo adminMemberViewVo = new AdminMemberViewVo();
adminMemberViewVo.setUserIdx(testUserAdminMemberViewVo.getUserIdx());
adminMemberViewVo.setUserTokenIdx(testUserAdminMemberViewVo.getUserTokenIdx());
adminMemberViewVo.setId(testUserAdminMemberViewVo.getId());
adminMemberViewVo.setEmail(testUserAdminMemberViewVo.getEmail());
adminMemberViewVo.setHp(testUserAdminMemberViewVo.getHp());
adminMemberViewVo.setUserRole(testUserAdminMemberViewVo.getUserRole());
adminMemberViewVo.setUserType(testUserAdminMemberViewVo.getUserType());
adminMemberViewVo.setDormantYn(testUserAdminMemberViewVo.getDormantYn());
adminMemberViewVo.setDormantAt(testUserAdminMemberViewVo.getDormantAt());
adminMemberViewVo.setWithdrawStatus(testUserAdminMemberViewVo.getWithdrawStatus());
adminMemberViewVo.setWithdrawAt(testUserAdminMemberViewVo.getWithdrawAt());
adminMemberViewVo.setCreateAt(testUserAdminMemberViewVo.getCreateAt());
adminMemberViewVo.setUpdateAt(testUserAdminMemberViewVo.getUpdateAt());
return adminMemberViewVo;
}
@Transactional
public AdminMemberAddVo insertAdminMemberAdd(AdminMemberAddForm adminMemberAddForm) {
String id = adminMemberAddForm.getId().trim();
AdminMemberAddVo adminMemberAddVo = new AdminMemberAddVo();
adminMemberAddVo.setId(id);
adminMemberAddVo.setUserRole(adminMemberAddForm.getUserRole());
adminMemberAddVo.setUserType(adminMemberAddForm.getUserType());
if (testUserService.selectTestUserDuplicateCount(id) > 0) {
adminMemberAddVo.setAdded(false);
adminMemberAddVo.setResultCode(2004);
return adminMemberAddVo;
}
TestUserAddDto testUserAddDto = new TestUserAddDto();
testUserAddDto.setId(id);
testUserAddDto.setPassword(adminMemberAddForm.getPassword());
testUserAddDto.setEmail("");
testUserAddDto.setHp("");
TestUserAddVo testUserAddVo = testUserService.insertTestUserAdd(testUserAddDto);
if (!testUserAddVo.isAdded()) {
adminMemberAddVo.setAdded(false);
adminMemberAddVo.setResultCode(testUserAddVo.getResultCode());
return adminMemberAddVo;
}
TestUserTokenAddVo testUserTokenAddVo = null;
for (int retryCount = 0; retryCount < 5; retryCount++) {
TestUserTokenAddDto testUserTokenAddDto = new TestUserTokenAddDto();
testUserTokenAddDto.setUserIdx(testUserAddVo.getUserIdx());
testUserTokenAddDto.setUserApiKey(ApiKeyGenerator.userApiKeyProc());
testUserTokenAddDto.setUserRole(adminMemberAddForm.getUserRole());
testUserTokenAddDto.setUserType(adminMemberAddForm.getUserType());
testUserTokenAddVo = testUserTokenService.insertTestUserTokenAdd(testUserTokenAddDto);
if (testUserTokenAddVo.isAdded()) {
break;
}
if (testUserTokenAddVo.getResultCode() != 2004) {
break;
}
}
if (testUserTokenAddVo == null || !testUserTokenAddVo.isAdded()) {
throw new IllegalStateException("관리자 계정 토큰 정보 등록에 실패했습니다.");
}
adminMemberAddVo.setAdded(true);
adminMemberAddVo.setUserIdx(testUserAddVo.getUserIdx());
adminMemberAddVo.setUserTokenIdx(testUserTokenAddVo.getUserTokenIdx());
adminMemberAddVo.setResultCode(2002);
return adminMemberAddVo;
}
@Transactional
public AdminMemberModifyVo updateAdminMemberModify(AdminMemberModifyForm adminMemberModifyForm) {
AdminMemberModifyVo adminMemberModifyVo = new AdminMemberModifyVo();
adminMemberModifyVo.setUserIdx(adminMemberModifyForm.getUserIdx());
TestUserAdminMemberViewDto testUserAdminMemberViewDto = new TestUserAdminMemberViewDto();
testUserAdminMemberViewDto.setUserIdx(adminMemberModifyForm.getUserIdx());
TestUserAdminMemberViewVo testUserAdminMemberViewVo = testUserAdminMemberService.testUserAdminMemberView(testUserAdminMemberViewDto);
if (testUserAdminMemberViewVo == null) {
adminMemberModifyVo.setUpdated(false);
adminMemberModifyVo.setResultCode(2003);
return adminMemberModifyVo;
}
String email = adminMemberModifyForm.getEmail() == null || adminMemberModifyForm.getEmail().isBlank() ? null : adminMemberModifyForm.getEmail().trim();
String hp = adminMemberModifyForm.getHp() == null || adminMemberModifyForm.getHp().isBlank() ? null : adminMemberModifyForm.getHp().trim();
String userRole = adminMemberModifyForm.getUserRole() == null || adminMemberModifyForm.getUserRole().isBlank() ? null : adminMemberModifyForm.getUserRole().trim();
String userType = adminMemberModifyForm.getUserType() == null || adminMemberModifyForm.getUserType().isBlank() ? null : adminMemberModifyForm.getUserType().trim();
if (email == null && hp == null && userRole == null && userType == null) {
adminMemberModifyVo.setUpdated(false);
adminMemberModifyVo.setResultCode(4001);
return adminMemberModifyVo;
}
if (email != null || hp != null) {
TestUserModifyDto testUserModifyDto = new TestUserModifyDto();
testUserModifyDto.setUserIdx(adminMemberModifyForm.getUserIdx());
testUserModifyDto.setEmail(email);
testUserModifyDto.setHp(hp);
TestUserModifyVo testUserModifyVo = testUserService.updateTestUserModify(testUserModifyDto);
if (!testUserModifyVo.isUpdated()) {
adminMemberModifyVo.setUpdated(false);
adminMemberModifyVo.setResultCode(testUserModifyVo.getResultCode());
return adminMemberModifyVo;
}
}
if (userRole != null || userType != null) {
TestUserTokenModifyDto testUserTokenModifyDto = new TestUserTokenModifyDto();
testUserTokenModifyDto.setUserTokenIdx(testUserAdminMemberViewVo.getUserTokenIdx());
testUserTokenModifyDto.setUserRole(userRole);
testUserTokenModifyDto.setUserType(userType);
TestUserTokenModifyVo testUserTokenModifyVo = testUserTokenService.updateTestUserTokenModify(testUserTokenModifyDto);
if (!testUserTokenModifyVo.isUpdated()) {
adminMemberModifyVo.setUpdated(false);
adminMemberModifyVo.setResultCode(testUserTokenModifyVo.getResultCode());
return adminMemberModifyVo;
}
}
adminMemberModifyVo.setUpdated(true);
adminMemberModifyVo.setResultCode(2005);
return adminMemberModifyVo;
}
@Transactional
public AdminMemberDormantModifyVo updateAdminMemberDormantModify(AdminMemberDormantModifyForm adminMemberDormantModifyForm) {
TestUserModifyDto testUserModifyDto = new TestUserModifyDto();
testUserModifyDto.setUserIdx(adminMemberDormantModifyForm.getUserIdx());
testUserModifyDto.setDormantYn(adminMemberDormantModifyForm.getDormantYn());
TestUserModifyVo testUserModifyVo = testUserService.updateTestUserModify(testUserModifyDto);
AdminMemberDormantModifyVo adminMemberDormantModifyVo = new AdminMemberDormantModifyVo();
adminMemberDormantModifyVo.setUserIdx(adminMemberDormantModifyForm.getUserIdx());
adminMemberDormantModifyVo.setUpdated(testUserModifyVo.isUpdated());
adminMemberDormantModifyVo.setResultCode(testUserModifyVo.getResultCode());
return adminMemberDormantModifyVo;
}
@Transactional
public AdminMemberWithdrawModifyVo updateAdminMemberWithdrawModify(AdminMemberWithdrawModifyForm adminMemberWithdrawModifyForm) {
TestUserModifyDto testUserModifyDto = new TestUserModifyDto();
testUserModifyDto.setUserIdx(adminMemberWithdrawModifyForm.getUserIdx());
testUserModifyDto.setWithdrawStatus(adminMemberWithdrawModifyForm.getWithdrawStatus());
TestUserModifyVo testUserModifyVo = testUserService.updateTestUserModify(testUserModifyDto);
AdminMemberWithdrawModifyVo adminMemberWithdrawModifyVo = new AdminMemberWithdrawModifyVo();
adminMemberWithdrawModifyVo.setUserIdx(adminMemberWithdrawModifyForm.getUserIdx());
adminMemberWithdrawModifyVo.setUpdated(testUserModifyVo.isUpdated());
adminMemberWithdrawModifyVo.setResultCode(testUserModifyVo.getResultCode());
return adminMemberWithdrawModifyVo;
}
@Transactional
public AdminMemberDeleteVo updateAdminMemberDelete(AdminMemberDeleteForm adminMemberDeleteForm) {
List<Integer> userIdxList = Arrays.stream(adminMemberDeleteForm.getUserIdxList().split(","))
.map(String::trim)
.map(Integer::valueOf)
.distinct()
.toList();
int deletedCount = 0;
for (Integer userIdx : userIdxList) {
TestUserDeleteDto testUserDeleteDto = new TestUserDeleteDto();
testUserDeleteDto.setUserIdx(userIdx);
TestUserDeleteVo testUserDeleteVo = testUserService.updateTestUserDelete(testUserDeleteDto);
if (testUserDeleteVo.isDeleted()) {
deletedCount++;
}
}
AdminMemberDeleteVo adminMemberDeleteVo = new AdminMemberDeleteVo();
adminMemberDeleteVo.setUserIdxList(userIdxList);
adminMemberDeleteVo.setDeleted(deletedCount > 0);
adminMemberDeleteVo.setDeletedCount(deletedCount);
adminMemberDeleteVo.setResultCode(deletedCount > 0 ? 2005 : 2003);
return adminMemberDeleteVo;
}
}
@@ -0,0 +1,142 @@
package com.alist.api.admin.modules.backOffice.service;
import com.alist.api.admin.modules.backOffice.form.CorsAddForm;
import com.alist.api.admin.modules.backOffice.form.CorsDeleteForm;
import com.alist.api.admin.modules.backOffice.form.CorsListForm;
import com.alist.api.admin.modules.backOffice.vo.CorsAddVo;
import com.alist.api.admin.modules.backOffice.vo.CorsDeleteVo;
import com.alist.api.admin.modules.backOffice.vo.CorsListItemVo;
import com.alist.api.admin.modules.backOffice.vo.CorsListVo;
import com.alist.api.admin.modules.backOffice.vo.CorsReloadVo;
import com.alist.api.core.common.utils.SecurityUtil;
import com.alist.api.core.config.cors.CorsCache;
import com.alist.api.core.config.cors.CorsCachePublisher;
import com.alist.api.core.config.cors.CorsCacheRefreshEvent;
import com.alist.api.core.modules.standard.testCorsAllowedList.dto.TestCorsAllowedListAddDto;
import com.alist.api.core.modules.standard.testCorsAllowedList.dto.TestCorsAllowedListDeleteDto;
import com.alist.api.core.modules.standard.testCorsAllowedList.dto.TestCorsAllowedListListDto;
import com.alist.api.core.modules.standard.testCorsAllowedList.service.TestCorsAllowedListService;
import com.alist.api.core.modules.standard.testCorsAllowedList.vo.TestCorsAllowedListAddVo;
import com.alist.api.core.modules.standard.testCorsAllowedList.vo.TestCorsAllowedListDeleteVo;
import com.alist.api.core.modules.standard.testCorsAllowedList.vo.TestCorsAllowedListListItemVo;
import com.alist.api.core.modules.standard.testCorsAllowedList.vo.TestCorsAllowedListListVo;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service
public class CorsService {
private final TestCorsAllowedListService testCorsAllowedListService;
private final ApplicationEventPublisher applicationEventPublisher;
private final CorsCache corsCache;
private final CorsCachePublisher corsCachePublisher;
public CorsService(TestCorsAllowedListService testCorsAllowedListService, ApplicationEventPublisher applicationEventPublisher, CorsCache corsCache, CorsCachePublisher corsCachePublisher) {
this.testCorsAllowedListService = testCorsAllowedListService;
this.applicationEventPublisher = applicationEventPublisher;
this.corsCache = corsCache;
this.corsCachePublisher = corsCachePublisher;
}
@Transactional(readOnly = true)
public CorsListVo selectCorsList(CorsListForm corsListForm) {
TestCorsAllowedListListDto testCorsAllowedListListDto = new TestCorsAllowedListListDto();
testCorsAllowedListListDto.setPage(corsListForm.getPage());
testCorsAllowedListListDto.setSize(corsListForm.getSize());
testCorsAllowedListListDto.setUserTokenIdx(corsListForm.getUserTokenIdx());
testCorsAllowedListListDto.setAllowedOrigin(corsListForm.getAllowedOrigin() == null || corsListForm.getAllowedOrigin().isBlank() ? null : corsListForm.getAllowedOrigin().trim());
TestCorsAllowedListListVo testCorsAllowedListListVo = testCorsAllowedListService.selectTestCorsAllowedListList(testCorsAllowedListListDto);
List<CorsListItemVo> corsList = new ArrayList<>();
for (TestCorsAllowedListListItemVo testCorsAllowedListListItemVo : testCorsAllowedListListVo.getTestCorsAllowedList()) {
CorsListItemVo corsListItemVo = new CorsListItemVo();
corsListItemVo.setCorsIdx(testCorsAllowedListListItemVo.getCorsIdx());
corsListItemVo.setUserTokenIdx(testCorsAllowedListListItemVo.getUserTokenIdx());
corsListItemVo.setAllowedOrigin(testCorsAllowedListListItemVo.getAllowedOrigin());
corsListItemVo.setCreatedAt(testCorsAllowedListListItemVo.getCreatedAt());
corsList.add(corsListItemVo);
}
CorsListVo corsListVo = new CorsListVo();
corsListVo.setCorsList(corsList);
corsListVo.setPaging(testCorsAllowedListListDto, testCorsAllowedListListVo.getTotalCount());
return corsListVo;
}
@Transactional
public CorsAddVo insertCorsAdd(CorsAddForm corsAddForm) {
CorsAddVo corsAddVo = new CorsAddVo();
if (SecurityUtil.getLoginUserTokenIdx() == null) {
corsAddVo.setResultCode(401);
return corsAddVo;
}
String allowedOrigin = corsAddForm.getAllowedOrigin().trim();
if (testCorsAllowedListService.selectTestCorsAllowedListAllowedOriginCount(allowedOrigin) > 0) {
corsAddVo.setResultCode(2004);
return corsAddVo;
}
TestCorsAllowedListAddDto testCorsAllowedListAddDto = new TestCorsAllowedListAddDto();
testCorsAllowedListAddDto.setUserTokenIdx(corsAddForm.getUserTokenIdx() == null ? 0 : corsAddForm.getUserTokenIdx());
testCorsAllowedListAddDto.setAllowedOrigin(allowedOrigin);
TestCorsAllowedListAddVo testCorsAllowedListAddVo = testCorsAllowedListService.insertTestCorsAllowedListAdd(testCorsAllowedListAddDto);
corsAddVo.setAdded(testCorsAllowedListAddVo.isAdded());
corsAddVo.setCorsIdx(testCorsAllowedListAddVo.getCorsIdx());
corsAddVo.setResultCode(testCorsAllowedListAddVo.getResultCode());
if (corsAddVo.isAdded()) {
applicationEventPublisher.publishEvent(new CorsCacheRefreshEvent());
}
return corsAddVo;
}
@Transactional
public CorsDeleteVo updateCorsDelete(CorsDeleteForm corsDeleteForm) {
CorsDeleteVo corsDeleteVo = new CorsDeleteVo();
corsDeleteVo.setCorsIdx(corsDeleteForm.getCorsIdx());
if (SecurityUtil.getLoginUserTokenIdx() == null) {
corsDeleteVo.setResultCode(401);
return corsDeleteVo;
}
TestCorsAllowedListDeleteDto testCorsAllowedListDeleteDto = new TestCorsAllowedListDeleteDto();
testCorsAllowedListDeleteDto.setCorsIdx(corsDeleteForm.getCorsIdx());
TestCorsAllowedListDeleteVo testCorsAllowedListDeleteVo = testCorsAllowedListService.updateTestCorsAllowedListDelete(testCorsAllowedListDeleteDto);
corsDeleteVo.setDeleted(testCorsAllowedListDeleteVo.isDeleted());
corsDeleteVo.setResultCode(testCorsAllowedListDeleteVo.getResultCode());
if (corsDeleteVo.isDeleted()) {
applicationEventPublisher.publishEvent(new CorsCacheRefreshEvent());
}
return corsDeleteVo;
}
@Transactional(readOnly = true)
public CorsReloadVo updateCorsReload() {
CorsReloadVo corsReloadVo = new CorsReloadVo();
if (SecurityUtil.getLoginUserTokenIdx() == null) {
corsReloadVo.setResultCode(401);
return corsReloadVo;
}
corsCache.syncFromDatabase();
corsCachePublisher.publishReload();
corsReloadVo.setReloaded(true);
corsReloadVo.setAllowedOriginList(corsCache.getAllowedOriginList());
corsReloadVo.setResultCode(2005);
return corsReloadVo;
}
}
@@ -0,0 +1,281 @@
package com.alist.api.admin.modules.backOffice.service;
import com.alist.api.admin.modules.backOffice.form.ScheduleAddForm;
import com.alist.api.admin.modules.backOffice.form.ScheduleDeleteForm;
import com.alist.api.admin.modules.backOffice.form.ScheduleListForm;
import com.alist.api.admin.modules.backOffice.form.ScheduleLogListForm;
import com.alist.api.admin.modules.backOffice.form.ScheduleModifyForm;
import com.alist.api.admin.modules.backOffice.form.ScheduleViewForm;
import com.alist.api.admin.modules.backOffice.vo.ScheduleAddVo;
import com.alist.api.admin.modules.backOffice.vo.ScheduleDeleteVo;
import com.alist.api.admin.modules.backOffice.vo.ScheduleListItemVo;
import com.alist.api.admin.modules.backOffice.vo.ScheduleListVo;
import com.alist.api.admin.modules.backOffice.vo.ScheduleLogListItemVo;
import com.alist.api.admin.modules.backOffice.vo.ScheduleLogListVo;
import com.alist.api.admin.modules.backOffice.vo.ScheduleModifyVo;
import com.alist.api.admin.modules.backOffice.vo.ScheduleViewVo;
import com.alist.api.core.common.utils.SecurityUtil;
import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobAddDto;
import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobDeleteDto;
import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobListDto;
import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobModifyDto;
import com.alist.api.core.modules.standard.scheduleJob.dto.ScheduleJobViewDto;
import com.alist.api.core.modules.standard.scheduleJob.service.ScheduleJobService;
import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobAddVo;
import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobDeleteVo;
import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobListItemVo;
import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobListVo;
import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobModifyVo;
import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobViewVo;
import com.alist.api.core.modules.standard.scheduleJobLog.dto.ScheduleJobLogListDto;
import com.alist.api.core.modules.standard.scheduleJobLog.service.ScheduleJobLogService;
import com.alist.api.core.modules.standard.scheduleJobLog.vo.ScheduleJobLogListItemVo;
import com.alist.api.core.modules.standard.scheduleJobLog.vo.ScheduleJobLogListVo;
import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenViewDto;
import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService;
import com.alist.api.core.modules.standard.testUserToken.vo.TestUserTokenViewVo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service
public class ScheduleService {
private final ScheduleJobService scheduleJobService;
private final ScheduleJobLogService scheduleJobLogService;
private final TestUserTokenService testUserTokenService;
public ScheduleService(ScheduleJobService scheduleJobService, ScheduleJobLogService scheduleJobLogService, TestUserTokenService testUserTokenService) {
this.scheduleJobService = scheduleJobService;
this.scheduleJobLogService = scheduleJobLogService;
this.testUserTokenService = testUserTokenService;
}
@Transactional(readOnly = true)
public ScheduleListVo selectScheduleList(ScheduleListForm scheduleListForm) {
ScheduleJobListDto scheduleJobListDto = new ScheduleJobListDto();
scheduleJobListDto.setPage(scheduleListForm.getPage());
scheduleJobListDto.setSize(scheduleListForm.getSize());
scheduleJobListDto.setKeyword(scheduleListForm.getKeyword() == null || scheduleListForm.getKeyword().isBlank() ? null : scheduleListForm.getKeyword().trim());
scheduleJobListDto.setJobGroup(scheduleListForm.getJobGroup() == null || scheduleListForm.getJobGroup().isBlank() ? null : scheduleListForm.getJobGroup().trim());
scheduleJobListDto.setLastStatus(scheduleListForm.getLastStatus());
scheduleJobListDto.setUseYn(scheduleListForm.getUseYn() == null || scheduleListForm.getUseYn().isBlank() ? null : scheduleListForm.getUseYn().trim());
ScheduleJobListVo scheduleJobListVo = scheduleJobService.selectScheduleJobList(scheduleJobListDto);
List<ScheduleListItemVo> scheduleList = new ArrayList<>();
for (ScheduleJobListItemVo scheduleJobListItemVo : scheduleJobListVo.getScheduleJobList()) {
ScheduleListItemVo scheduleListItemVo = new ScheduleListItemVo();
scheduleListItemVo.setScheduleJobIdx(scheduleJobListItemVo.getScheduleJobIdx());
scheduleListItemVo.setJobName(scheduleJobListItemVo.getJobName());
scheduleListItemVo.setJobGroup(scheduleJobListItemVo.getJobGroup());
scheduleListItemVo.setJobDescription(scheduleJobListItemVo.getJobDescription());
scheduleListItemVo.setCronExpr(scheduleJobListItemVo.getCronExpr());
scheduleListItemVo.setTimezoneId(scheduleJobListItemVo.getTimezoneId());
scheduleListItemVo.setMaxRunningSeconds(scheduleJobListItemVo.getMaxRunningSeconds());
scheduleListItemVo.setLastRunDate(scheduleJobListItemVo.getLastRunDate());
scheduleListItemVo.setLastEndDate(scheduleJobListItemVo.getLastEndDate());
scheduleListItemVo.setLastSuccessDate(scheduleJobListItemVo.getLastSuccessDate());
scheduleListItemVo.setLastStatus(scheduleJobListItemVo.getLastStatus());
scheduleListItemVo.setLastMessage(scheduleJobListItemVo.getLastMessage());
scheduleListItemVo.setRunLockDate(scheduleJobListItemVo.getRunLockDate());
scheduleListItemVo.setRunLockOwner(scheduleJobListItemVo.getRunLockOwner());
scheduleListItemVo.setUseYn(scheduleJobListItemVo.getUseYn());
scheduleListItemVo.setCreateDate(scheduleJobListItemVo.getCreateDate());
scheduleListItemVo.setUpdateDate(scheduleJobListItemVo.getUpdateDate());
scheduleList.add(scheduleListItemVo);
}
ScheduleListVo scheduleListVo = new ScheduleListVo();
scheduleListVo.setScheduleList(scheduleList);
scheduleListVo.setPaging(scheduleJobListDto, scheduleJobListVo.getTotalCount());
return scheduleListVo;
}
@Transactional(readOnly = true)
public ScheduleViewVo selectScheduleView(ScheduleViewForm scheduleViewForm) {
ScheduleJobViewDto scheduleJobViewDto = new ScheduleJobViewDto();
scheduleJobViewDto.setScheduleJobIdx(scheduleViewForm.getScheduleJobIdx());
ScheduleJobViewVo scheduleJobViewVo = scheduleJobService.selectScheduleJobView(scheduleJobViewDto);
if (scheduleJobViewVo == null) {
return null;
}
ScheduleViewVo scheduleViewVo = new ScheduleViewVo();
scheduleViewVo.setScheduleJobIdx(scheduleJobViewVo.getScheduleJobIdx());
scheduleViewVo.setJobName(scheduleJobViewVo.getJobName());
scheduleViewVo.setJobGroup(scheduleJobViewVo.getJobGroup());
scheduleViewVo.setJobDescription(scheduleJobViewVo.getJobDescription());
scheduleViewVo.setCronExpr(scheduleJobViewVo.getCronExpr());
scheduleViewVo.setTimezoneId(scheduleJobViewVo.getTimezoneId());
scheduleViewVo.setMaxRunningSeconds(scheduleJobViewVo.getMaxRunningSeconds());
scheduleViewVo.setLastRunDate(scheduleJobViewVo.getLastRunDate());
scheduleViewVo.setLastEndDate(scheduleJobViewVo.getLastEndDate());
scheduleViewVo.setLastSuccessDate(scheduleJobViewVo.getLastSuccessDate());
scheduleViewVo.setLastStatus(scheduleJobViewVo.getLastStatus());
scheduleViewVo.setLastMessage(scheduleJobViewVo.getLastMessage());
scheduleViewVo.setRunLockDate(scheduleJobViewVo.getRunLockDate());
scheduleViewVo.setRunLockOwner(scheduleJobViewVo.getRunLockOwner());
scheduleViewVo.setUseYn(scheduleJobViewVo.getUseYn());
scheduleViewVo.setCreateDate(scheduleJobViewVo.getCreateDate());
scheduleViewVo.setCreateMember(scheduleJobViewVo.getCreateMember());
scheduleViewVo.setUpdateDate(scheduleJobViewVo.getUpdateDate());
scheduleViewVo.setUpdateMember(scheduleJobViewVo.getUpdateMember());
return scheduleViewVo;
}
@Transactional
public ScheduleAddVo insertScheduleAdd(ScheduleAddForm scheduleAddForm) {
ScheduleAddVo scheduleAddVo = new ScheduleAddVo();
Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx();
if (userTokenIdx == null) {
scheduleAddVo.setResultCode(401);
return scheduleAddVo;
}
TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto();
testUserTokenViewDto.setUserTokenIdx(userTokenIdx);
TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto);
if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) {
scheduleAddVo.setResultCode(401);
return scheduleAddVo;
}
ScheduleJobAddDto scheduleJobAddDto = new ScheduleJobAddDto();
scheduleJobAddDto.setJobName(scheduleAddForm.getJobName().trim());
scheduleJobAddDto.setJobGroup(scheduleAddForm.getJobGroup() == null || scheduleAddForm.getJobGroup().isBlank() ? "DEFAULT" : scheduleAddForm.getJobGroup().trim());
scheduleJobAddDto.setJobDescription(scheduleAddForm.getJobDescription() == null ? null : scheduleAddForm.getJobDescription().trim());
scheduleJobAddDto.setCronExpr(scheduleAddForm.getCronExpr().trim());
scheduleJobAddDto.setTimezoneId(scheduleAddForm.getTimezoneId() == null || scheduleAddForm.getTimezoneId().isBlank() ? "Asia/Seoul" : scheduleAddForm.getTimezoneId().trim());
scheduleJobAddDto.setMaxRunningSeconds(scheduleAddForm.getMaxRunningSeconds() == null ? 1800 : scheduleAddForm.getMaxRunningSeconds());
scheduleJobAddDto.setUseYn(scheduleAddForm.getUseYn() == null || scheduleAddForm.getUseYn().isBlank() ? "Y" : scheduleAddForm.getUseYn().trim());
scheduleJobAddDto.setCreateMember(testUserTokenViewVo.getUserIdx());
scheduleJobAddDto.setUpdateMember(testUserTokenViewVo.getUserIdx());
if (scheduleJobService.selectScheduleJobDuplicateCount(scheduleJobAddDto) > 0) {
scheduleAddVo.setResultCode(2004);
return scheduleAddVo;
}
ScheduleJobAddVo scheduleJobAddVo = scheduleJobService.insertScheduleJobAdd(scheduleJobAddDto);
scheduleAddVo.setAdded(scheduleJobAddVo.isAdded());
scheduleAddVo.setScheduleJobIdx(scheduleJobAddVo.getScheduleJobIdx());
scheduleAddVo.setResultCode(scheduleJobAddVo.getResultCode());
return scheduleAddVo;
}
@Transactional
public ScheduleModifyVo updateScheduleModify(ScheduleModifyForm scheduleModifyForm) {
ScheduleModifyVo scheduleModifyVo = new ScheduleModifyVo();
scheduleModifyVo.setScheduleJobIdx(scheduleModifyForm.getScheduleJobIdx());
Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx();
if (userTokenIdx == null) {
scheduleModifyVo.setResultCode(401);
return scheduleModifyVo;
}
TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto();
testUserTokenViewDto.setUserTokenIdx(userTokenIdx);
TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto);
if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) {
scheduleModifyVo.setResultCode(401);
return scheduleModifyVo;
}
ScheduleJobModifyDto scheduleJobModifyDto = new ScheduleJobModifyDto();
scheduleJobModifyDto.setScheduleJobIdx(scheduleModifyForm.getScheduleJobIdx());
scheduleJobModifyDto.setJobName(scheduleModifyForm.getJobName().trim());
scheduleJobModifyDto.setJobGroup(scheduleModifyForm.getJobGroup() == null || scheduleModifyForm.getJobGroup().isBlank() ? "DEFAULT" : scheduleModifyForm.getJobGroup().trim());
scheduleJobModifyDto.setJobDescription(scheduleModifyForm.getJobDescription() == null ? null : scheduleModifyForm.getJobDescription().trim());
scheduleJobModifyDto.setCronExpr(scheduleModifyForm.getCronExpr().trim());
scheduleJobModifyDto.setTimezoneId(scheduleModifyForm.getTimezoneId() == null || scheduleModifyForm.getTimezoneId().isBlank() ? "Asia/Seoul" : scheduleModifyForm.getTimezoneId().trim());
scheduleJobModifyDto.setMaxRunningSeconds(scheduleModifyForm.getMaxRunningSeconds() == null ? 1800 : scheduleModifyForm.getMaxRunningSeconds());
scheduleJobModifyDto.setUseYn(scheduleModifyForm.getUseYn() == null || scheduleModifyForm.getUseYn().isBlank() ? "Y" : scheduleModifyForm.getUseYn().trim());
scheduleJobModifyDto.setUpdateMember(testUserTokenViewVo.getUserIdx());
if (scheduleJobService.selectScheduleJobModifyDuplicateCount(scheduleJobModifyDto) > 0) {
scheduleModifyVo.setResultCode(2004);
return scheduleModifyVo;
}
ScheduleJobModifyVo scheduleJobModifyVo = scheduleJobService.updateScheduleJobModify(scheduleJobModifyDto);
scheduleModifyVo.setUpdated(scheduleJobModifyVo.isUpdated());
scheduleModifyVo.setResultCode(scheduleJobModifyVo.getResultCode());
return scheduleModifyVo;
}
@Transactional
public ScheduleDeleteVo updateScheduleDelete(ScheduleDeleteForm scheduleDeleteForm) {
ScheduleDeleteVo scheduleDeleteVo = new ScheduleDeleteVo();
scheduleDeleteVo.setScheduleJobIdx(scheduleDeleteForm.getScheduleJobIdx());
Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx();
if (userTokenIdx == null) {
scheduleDeleteVo.setResultCode(401);
return scheduleDeleteVo;
}
TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto();
testUserTokenViewDto.setUserTokenIdx(userTokenIdx);
TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto);
if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) {
scheduleDeleteVo.setResultCode(401);
return scheduleDeleteVo;
}
ScheduleJobDeleteDto scheduleJobDeleteDto = new ScheduleJobDeleteDto();
scheduleJobDeleteDto.setScheduleJobIdx(scheduleDeleteForm.getScheduleJobIdx());
scheduleJobDeleteDto.setUpdateMember(testUserTokenViewVo.getUserIdx());
ScheduleJobDeleteVo scheduleJobDeleteVo = scheduleJobService.updateScheduleJobDelete(scheduleJobDeleteDto);
scheduleDeleteVo.setDeleted(scheduleJobDeleteVo.isDeleted());
scheduleDeleteVo.setResultCode(scheduleJobDeleteVo.getResultCode());
return scheduleDeleteVo;
}
@Transactional(readOnly = true)
public ScheduleLogListVo selectScheduleLogList(ScheduleLogListForm scheduleLogListForm) {
ScheduleJobLogListDto scheduleJobLogListDto = new ScheduleJobLogListDto();
scheduleJobLogListDto.setPage(scheduleLogListForm.getPage());
scheduleJobLogListDto.setSize(scheduleLogListForm.getSize());
scheduleJobLogListDto.setScheduleJobIdx(scheduleLogListForm.getScheduleJobIdx());
scheduleJobLogListDto.setStatus(scheduleLogListForm.getStatus());
scheduleJobLogListDto.setStartDateFrom(scheduleLogListForm.getStartDateFrom() == null || scheduleLogListForm.getStartDateFrom().isBlank() ? null : scheduleLogListForm.getStartDateFrom().trim());
scheduleJobLogListDto.setStartDateTo(scheduleLogListForm.getStartDateTo() == null || scheduleLogListForm.getStartDateTo().isBlank() ? null : scheduleLogListForm.getStartDateTo().trim());
ScheduleJobLogListVo scheduleJobLogListVo = scheduleJobLogService.selectScheduleJobLogList(scheduleJobLogListDto);
List<ScheduleLogListItemVo> scheduleLogList = new ArrayList<>();
for (ScheduleJobLogListItemVo scheduleJobLogListItemVo : scheduleJobLogListVo.getScheduleJobLogList()) {
ScheduleLogListItemVo scheduleLogListItemVo = new ScheduleLogListItemVo();
scheduleLogListItemVo.setScheduleJobLogIdx(scheduleJobLogListItemVo.getScheduleJobLogIdx());
scheduleLogListItemVo.setScheduleJobIdx(scheduleJobLogListItemVo.getScheduleJobIdx());
scheduleLogListItemVo.setRunId(scheduleJobLogListItemVo.getRunId());
scheduleLogListItemVo.setJobName(scheduleJobLogListItemVo.getJobName());
scheduleLogListItemVo.setStartDate(scheduleJobLogListItemVo.getStartDate());
scheduleLogListItemVo.setEndDate(scheduleJobLogListItemVo.getEndDate());
scheduleLogListItemVo.setTargetCount(scheduleJobLogListItemVo.getTargetCount());
scheduleLogListItemVo.setSuccessCount(scheduleJobLogListItemVo.getSuccessCount());
scheduleLogListItemVo.setFailCount(scheduleJobLogListItemVo.getFailCount());
scheduleLogListItemVo.setMessage(scheduleJobLogListItemVo.getMessage());
scheduleLogListItemVo.setErrorMessage(scheduleJobLogListItemVo.getErrorMessage());
scheduleLogListItemVo.setLockOwner(scheduleJobLogListItemVo.getLockOwner());
scheduleLogListItemVo.setStatus(scheduleJobLogListItemVo.getStatus());
scheduleLogListItemVo.setCreateDate(scheduleJobLogListItemVo.getCreateDate());
scheduleLogList.add(scheduleLogListItemVo);
}
ScheduleLogListVo scheduleLogListVo = new ScheduleLogListVo();
scheduleLogListVo.setScheduleLogList(scheduleLogList);
scheduleLogListVo.setPaging(scheduleJobLogListDto, scheduleJobLogListVo.getTotalCount());
return scheduleLogListVo;
}
}
@@ -0,0 +1,211 @@
package com.alist.api.admin.modules.backOffice.service;
import com.alist.api.admin.modules.backOffice.form.SsoAddForm;
import com.alist.api.admin.modules.backOffice.form.SsoDeleteForm;
import com.alist.api.admin.modules.backOffice.form.SsoListForm;
import com.alist.api.admin.modules.backOffice.form.SsoModifyForm;
import com.alist.api.admin.modules.backOffice.form.SsoViewForm;
import com.alist.api.admin.modules.backOffice.vo.SsoAddVo;
import com.alist.api.admin.modules.backOffice.vo.SsoDeleteVo;
import com.alist.api.admin.modules.backOffice.vo.SsoListItemVo;
import com.alist.api.admin.modules.backOffice.vo.SsoListVo;
import com.alist.api.admin.modules.backOffice.vo.SsoModifyVo;
import com.alist.api.admin.modules.backOffice.vo.SsoViewVo;
import com.alist.api.core.common.utils.SecurityUtil;
import com.alist.api.core.modules.standard.ssoClient.dto.SsoClientAddDto;
import com.alist.api.core.modules.standard.ssoClient.dto.SsoClientDeleteDto;
import com.alist.api.core.modules.standard.ssoClient.dto.SsoClientListDto;
import com.alist.api.core.modules.standard.ssoClient.dto.SsoClientModifyDto;
import com.alist.api.core.modules.standard.ssoClient.dto.SsoClientViewDto;
import com.alist.api.core.modules.standard.ssoClient.service.SsoClientService;
import com.alist.api.core.modules.standard.ssoClient.vo.SsoClientAddVo;
import com.alist.api.core.modules.standard.ssoClient.vo.SsoClientDeleteVo;
import com.alist.api.core.modules.standard.ssoClient.vo.SsoClientListItemVo;
import com.alist.api.core.modules.standard.ssoClient.vo.SsoClientListVo;
import com.alist.api.core.modules.standard.ssoClient.vo.SsoClientModifyVo;
import com.alist.api.core.modules.standard.ssoClient.vo.SsoClientViewVo;
import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenViewDto;
import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService;
import com.alist.api.core.modules.standard.testUserToken.vo.TestUserTokenViewVo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service
public class SsoService {
private final SsoClientService ssoClientService;
private final TestUserTokenService testUserTokenService;
public SsoService(SsoClientService ssoClientService, TestUserTokenService testUserTokenService) {
this.ssoClientService = ssoClientService;
this.testUserTokenService = testUserTokenService;
}
@Transactional(readOnly = true)
public SsoListVo selectSsoList(SsoListForm ssoListForm) {
SsoClientListDto ssoClientListDto = new SsoClientListDto();
ssoClientListDto.setPage(ssoListForm.getPage());
ssoClientListDto.setSize(ssoListForm.getSize());
ssoClientListDto.setKeyword(ssoListForm.getKeyword() == null || ssoListForm.getKeyword().isBlank() ? null : ssoListForm.getKeyword().trim());
ssoClientListDto.setUseYn(ssoListForm.getUseYn() == null || ssoListForm.getUseYn().isBlank() ? null : ssoListForm.getUseYn().trim());
SsoClientListVo ssoClientListVo = ssoClientService.selectSsoClientList(ssoClientListDto);
List<SsoListItemVo> ssoList = new ArrayList<>();
for (SsoClientListItemVo ssoClientListItemVo : ssoClientListVo.getSsoClientList()) {
SsoListItemVo ssoListItemVo = new SsoListItemVo();
ssoListItemVo.setSsoClientIdx(ssoClientListItemVo.getSsoClientIdx());
ssoListItemVo.setClientId(ssoClientListItemVo.getClientId());
ssoListItemVo.setClientName(ssoClientListItemVo.getClientName());
ssoListItemVo.setRedirectUri(ssoClientListItemVo.getRedirectUri());
ssoListItemVo.setMemo(ssoClientListItemVo.getMemo());
ssoListItemVo.setUseYn(ssoClientListItemVo.getUseYn());
ssoListItemVo.setCreateDate(ssoClientListItemVo.getCreateDate());
ssoListItemVo.setUpdateDate(ssoClientListItemVo.getUpdateDate());
ssoList.add(ssoListItemVo);
}
SsoListVo ssoListVo = new SsoListVo();
ssoListVo.setSsoList(ssoList);
ssoListVo.setPaging(ssoClientListDto, ssoClientListVo.getTotalCount());
return ssoListVo;
}
@Transactional(readOnly = true)
public SsoViewVo selectSsoView(SsoViewForm ssoViewForm) {
SsoClientViewDto ssoClientViewDto = new SsoClientViewDto();
ssoClientViewDto.setSsoClientIdx(ssoViewForm.getSsoClientIdx());
SsoClientViewVo ssoClientViewVo = ssoClientService.selectSsoClientView(ssoClientViewDto);
if (ssoClientViewVo == null) {
return null;
}
SsoViewVo ssoViewVo = new SsoViewVo();
ssoViewVo.setSsoClientIdx(ssoClientViewVo.getSsoClientIdx());
ssoViewVo.setClientId(ssoClientViewVo.getClientId());
ssoViewVo.setClientName(ssoClientViewVo.getClientName());
ssoViewVo.setRedirectUri(ssoClientViewVo.getRedirectUri());
ssoViewVo.setMemo(ssoClientViewVo.getMemo());
ssoViewVo.setUseYn(ssoClientViewVo.getUseYn());
ssoViewVo.setCreateDate(ssoClientViewVo.getCreateDate());
ssoViewVo.setCreateMember(ssoClientViewVo.getCreateMember());
ssoViewVo.setUpdateDate(ssoClientViewVo.getUpdateDate());
ssoViewVo.setUpdateMember(ssoClientViewVo.getUpdateMember());
return ssoViewVo;
}
@Transactional
public SsoAddVo insertSsoAdd(SsoAddForm ssoAddForm) {
SsoAddVo ssoAddVo = new SsoAddVo();
Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx();
if (userTokenIdx == null) {
ssoAddVo.setAdded(false);
ssoAddVo.setResultCode(401);
return ssoAddVo;
}
TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto();
testUserTokenViewDto.setUserTokenIdx(userTokenIdx);
TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto);
if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) {
ssoAddVo.setAdded(false);
ssoAddVo.setResultCode(401);
return ssoAddVo;
}
SsoClientAddDto ssoClientAddDto = new SsoClientAddDto();
ssoClientAddDto.setClientId(ssoAddForm.getClientId().trim());
ssoClientAddDto.setClientName(ssoAddForm.getClientName().trim());
ssoClientAddDto.setRedirectUri(ssoAddForm.getRedirectUri().trim());
ssoClientAddDto.setMemo(ssoAddForm.getMemo() == null ? null : ssoAddForm.getMemo().trim());
ssoClientAddDto.setUseYn(ssoAddForm.getUseYn() == null || ssoAddForm.getUseYn().isBlank() ? "Y" : ssoAddForm.getUseYn().trim());
ssoClientAddDto.setCreateMember(testUserTokenViewVo.getUserIdx());
ssoClientAddDto.setUpdateMember(testUserTokenViewVo.getUserIdx());
if (ssoClientService.selectSsoClientDuplicateCount(ssoClientAddDto.getClientId()) > 0) {
ssoAddVo.setAdded(false);
ssoAddVo.setResultCode(2004);
return ssoAddVo;
}
SsoClientAddVo ssoClientAddVo = ssoClientService.insertSsoClientAdd(ssoClientAddDto);
ssoAddVo.setAdded(ssoClientAddVo.isAdded());
ssoAddVo.setSsoClientIdx(ssoClientAddVo.getSsoClientIdx());
ssoAddVo.setResultCode(ssoClientAddVo.getResultCode());
return ssoAddVo;
}
@Transactional
public SsoModifyVo updateSsoModify(SsoModifyForm ssoModifyForm) {
SsoModifyVo ssoModifyVo = new SsoModifyVo();
ssoModifyVo.setSsoClientIdx(ssoModifyForm.getSsoClientIdx());
Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx();
if (userTokenIdx == null) {
ssoModifyVo.setUpdated(false);
ssoModifyVo.setResultCode(401);
return ssoModifyVo;
}
TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto();
testUserTokenViewDto.setUserTokenIdx(userTokenIdx);
TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto);
if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) {
ssoModifyVo.setUpdated(false);
ssoModifyVo.setResultCode(401);
return ssoModifyVo;
}
SsoClientModifyDto ssoClientModifyDto = new SsoClientModifyDto();
ssoClientModifyDto.setSsoClientIdx(ssoModifyForm.getSsoClientIdx());
ssoClientModifyDto.setClientId(ssoModifyForm.getClientId().trim());
ssoClientModifyDto.setClientName(ssoModifyForm.getClientName().trim());
ssoClientModifyDto.setRedirectUri(ssoModifyForm.getRedirectUri().trim());
ssoClientModifyDto.setMemo(ssoModifyForm.getMemo() == null ? null : ssoModifyForm.getMemo().trim());
ssoClientModifyDto.setUseYn(ssoModifyForm.getUseYn() == null || ssoModifyForm.getUseYn().isBlank() ? "Y" : ssoModifyForm.getUseYn().trim());
ssoClientModifyDto.setUpdateMember(testUserTokenViewVo.getUserIdx());
if (ssoClientService.selectSsoClientModifyDuplicateCount(ssoClientModifyDto) > 0) {
ssoModifyVo.setUpdated(false);
ssoModifyVo.setResultCode(2004);
return ssoModifyVo;
}
SsoClientModifyVo ssoClientModifyVo = ssoClientService.updateSsoClientModify(ssoClientModifyDto);
ssoModifyVo.setUpdated(ssoClientModifyVo.isUpdated());
ssoModifyVo.setResultCode(ssoClientModifyVo.getResultCode());
return ssoModifyVo;
}
@Transactional
public SsoDeleteVo updateSsoDelete(SsoDeleteForm ssoDeleteForm) {
SsoDeleteVo ssoDeleteVo = new SsoDeleteVo();
ssoDeleteVo.setSsoClientIdx(ssoDeleteForm.getSsoClientIdx());
Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx();
if (userTokenIdx == null) {
ssoDeleteVo.setDeleted(false);
ssoDeleteVo.setResultCode(401);
return ssoDeleteVo;
}
TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto();
testUserTokenViewDto.setUserTokenIdx(userTokenIdx);
TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto);
if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) {
ssoDeleteVo.setDeleted(false);
ssoDeleteVo.setResultCode(401);
return ssoDeleteVo;
}
SsoClientDeleteDto ssoClientDeleteDto = new SsoClientDeleteDto();
ssoClientDeleteDto.setSsoClientIdx(ssoDeleteForm.getSsoClientIdx());
ssoClientDeleteDto.setUpdateMember(testUserTokenViewVo.getUserIdx());
SsoClientDeleteVo ssoClientDeleteVo = ssoClientService.updateSsoClientDelete(ssoClientDeleteDto);
ssoDeleteVo.setDeleted(ssoClientDeleteVo.isDeleted());
ssoDeleteVo.setResultCode(ssoClientDeleteVo.getResultCode());
return ssoDeleteVo;
}
}
@@ -0,0 +1,28 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 계정 등록 응답")
@Getter
@Setter
public class AdminMemberAddVo {
@Schema(description = "등록 성공 여부", example = "true")
private boolean added;
@Schema(description = "회원 PK", example = "16")
private Integer userIdx;
@Schema(description = "회원 토큰 PK", example = "16")
private Integer userTokenIdx;
@Schema(description = "아이디", example = "admin01")
private String id;
@Schema(description = "회원 권한", example = "A")
private String userRole;
@Schema(description = "회원 유형", example = "A")
private String userType;
@JsonIgnore
@Schema(hidden = true)
private int resultCode;
}
@@ -0,0 +1,24 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Schema(description = "관리자 계정 삭제 응답")
@Getter
@Setter
public class AdminMemberDeleteVo {
@Schema(description = "삭제 성공 여부", example = "true")
private boolean deleted;
@Schema(description = "삭제 요청 회원 PK 목록", example = "[16, 17]")
private List<Integer> userIdxList;
@Schema(description = "실제 삭제 처리 수", example = "2")
private int deletedCount;
@JsonIgnore
@Schema(hidden = true)
private int resultCode;
}
@@ -0,0 +1,20 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 계정 휴면 상태 변경 응답")
@Getter
@Setter
public class AdminMemberDormantModifyVo {
@Schema(description = "수정 성공 여부", example = "true")
private boolean updated;
@Schema(description = "회원 PK", example = "16")
private Integer userIdx;
@JsonIgnore
@Schema(hidden = true)
private int resultCode;
}
@@ -0,0 +1,51 @@
package com.alist.api.admin.modules.backOffice.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Schema(description = "관리자 계정 목록 항목")
@Getter
@Setter
public class AdminMemberListItemVo {
@Schema(description = "회원 PK", example = "16")
private Integer userIdx;
@Schema(description = "회원 토큰 PK", example = "16")
private Integer userTokenIdx;
@Schema(description = "아이디", example = "admin01")
private String id;
@Schema(description = "이메일", example = "admin@alist.co.kr")
private String email;
@Schema(description = "휴대폰 번호", example = "01012345678")
private String hp;
@Schema(description = "회원 권한", example = "A")
private String userRole;
@Schema(description = "회원 유형. A: 관리자, T: 교사, S: 학생", example = "A")
private String userType;
@Schema(description = "휴면 여부. N: 정상, Y: 휴면", example = "N")
private String dormantYn;
@Schema(description = "휴면 처리일")
private LocalDateTime dormantAt;
@Schema(description = "탈퇴 상태. N: 정상, P: 대기, Y: 완료", example = "N")
private String withdrawStatus;
@Schema(description = "탈퇴 처리일")
private LocalDateTime withdrawAt;
@Schema(description = "생성일")
private LocalDateTime createAt;
@Schema(description = "수정일")
private LocalDateTime updateAt;
}
@@ -0,0 +1,16 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.alist.api.core.common.paging.PagingResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Schema(description = "관리자 계정 목록 조회 응답")
@Getter
@Setter
public class AdminMemberListVo extends PagingResponse {
@Schema(description = "관리자 계정 목록")
private List<AdminMemberListItemVo> adminMemberList;
}
@@ -0,0 +1,20 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 계정 수정 응답")
@Getter
@Setter
public class AdminMemberModifyVo {
@Schema(description = "수정 성공 여부", example = "true")
private boolean updated;
@Schema(description = "회원 PK", example = "16")
private Integer userIdx;
@JsonIgnore
@Schema(hidden = true)
private int resultCode;
}
@@ -0,0 +1,51 @@
package com.alist.api.admin.modules.backOffice.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Schema(description = "관리자 계정 상세 조회 응답")
@Getter
@Setter
public class AdminMemberViewVo {
@Schema(description = "회원 PK", example = "16")
private Integer userIdx;
@Schema(description = "회원 토큰 PK", example = "16")
private Integer userTokenIdx;
@Schema(description = "아이디", example = "admin01")
private String id;
@Schema(description = "이메일", example = "admin@alist.co.kr")
private String email;
@Schema(description = "휴대폰 번호", example = "01012345678")
private String hp;
@Schema(description = "회원 권한", example = "A")
private String userRole;
@Schema(description = "회원 유형. A: 관리자, T: 교사, S: 학생", example = "A")
private String userType;
@Schema(description = "휴면 여부. N: 정상, Y: 휴면", example = "N")
private String dormantYn;
@Schema(description = "휴면 처리일")
private LocalDateTime dormantAt;
@Schema(description = "탈퇴 상태. N: 정상, P: 대기, Y: 완료", example = "N")
private String withdrawStatus;
@Schema(description = "탈퇴 처리일")
private LocalDateTime withdrawAt;
@Schema(description = "생성일")
private LocalDateTime createAt;
@Schema(description = "수정일")
private LocalDateTime updateAt;
}
@@ -0,0 +1,20 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "관리자 계정 탈퇴 상태 변경 응답")
@Getter
@Setter
public class AdminMemberWithdrawModifyVo {
@Schema(description = "수정 성공 여부", example = "true")
private boolean updated;
@Schema(description = "회원 PK", example = "16")
private Integer userIdx;
@JsonIgnore
@Schema(hidden = true)
private int resultCode;
}
@@ -0,0 +1,21 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "CORS 허용 Origin 등록 응답")
@Getter
@Setter
public class CorsAddVo {
@Schema(description = "등록 성공 여부", example = "true")
private boolean added;
@Schema(description = "등록된 CORS 설정 PK", example = "1")
private Integer corsIdx;
@JsonIgnore
@Schema(hidden = true)
private int resultCode;
}
@@ -0,0 +1,21 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "CORS 허용 Origin 삭제 응답")
@Getter
@Setter
public class CorsDeleteVo {
@Schema(description = "삭제 성공 여부", example = "true")
private boolean deleted;
@Schema(description = "CORS 설정 PK", example = "1")
private Integer corsIdx;
@JsonIgnore
@Schema(hidden = true)
private int resultCode;
}
@@ -0,0 +1,24 @@
package com.alist.api.admin.modules.backOffice.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Schema(description = "CORS 허용 Origin 목록 항목")
@Getter
@Setter
public class CorsListItemVo {
@Schema(description = "CORS 설정 PK", example = "1")
private Integer corsIdx;
@Schema(description = "API 토큰 PK. 0은 전체 적용", example = "0")
private Integer userTokenIdx;
@Schema(description = "허용 Origin", example = "https://admin.alist.co.kr")
private String allowedOrigin;
@Schema(description = "등록일시")
private LocalDateTime createdAt;
}
@@ -0,0 +1,16 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.alist.api.core.common.paging.PagingResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Schema(description = "CORS 허용 Origin 목록 조회 응답")
@Getter
@Setter
public class CorsListVo extends PagingResponse {
@Schema(description = "CORS 허용 Origin 목록")
private List<CorsListItemVo> corsList;
}
@@ -0,0 +1,23 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Schema(description = "CORS Redis 목록 강제 갱신 응답")
@Getter
@Setter
public class CorsReloadVo {
@Schema(description = "갱신 성공 여부", example = "true")
private boolean reloaded;
@Schema(description = "Redis에 반영된 허용 Origin 목록")
private List<String> allowedOriginList;
@JsonIgnore
@Schema(hidden = true)
private int resultCode;
}
@@ -0,0 +1,15 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "스케줄 작업 등록 응답")
@Getter
@Setter
public class ScheduleAddVo {
@Schema(description = "등록 성공 여부", example = "true") private boolean added;
@Schema(description = "스케줄 작업 PK", example = "9") private Long scheduleJobIdx;
@JsonIgnore @Schema(hidden = true) private int resultCode;
}
@@ -0,0 +1,15 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "스케줄 작업 삭제 응답")
@Getter
@Setter
public class ScheduleDeleteVo {
@Schema(description = "삭제 성공 여부", example = "true") private boolean deleted;
@Schema(description = "스케줄 작업 PK", example = "9") private Long scheduleJobIdx;
@JsonIgnore @Schema(hidden = true) private int resultCode;
}
@@ -0,0 +1,30 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.alist.api.core.modules.standard.scheduleJob.vo.ScheduleJobListItemVo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Schema(description = "스케줄 작업 목록 항목")
@Getter
@Setter
public class ScheduleListItemVo {
@Schema(description = "스케줄 작업 PK", example = "9") private Long scheduleJobIdx;
@Schema(description = "작업 코드", example = "DAILY_LEARNING_SUMMARY") private String jobName;
@Schema(description = "작업 그룹", example = "DEFAULT") private String jobGroup;
@Schema(description = "작업 설명") private String jobDescription;
@Schema(description = "크론식") private String cronExpr;
@Schema(description = "타임존") private String timezoneId;
@Schema(description = "최대 실행 허용 시간(초)") private Integer maxRunningSeconds;
@Schema(description = "마지막 실행 시작 시각") private LocalDateTime lastRunDate;
@Schema(description = "마지막 실행 종료 시각") private LocalDateTime lastEndDate;
@Schema(description = "마지막 성공 시각") private LocalDateTime lastSuccessDate;
@Schema(description = "마지막 상태") private Short lastStatus;
@Schema(description = "마지막 메시지") private String lastMessage;
@Schema(description = "실행 락 시각") private LocalDateTime runLockDate;
@Schema(description = "실행 락 소유자") private String runLockOwner;
@Schema(description = "사용 여부") private String useYn;
@Schema(description = "등록일시") private LocalDateTime createDate;
@Schema(description = "수정일시") private LocalDateTime updateDate;
}
@@ -0,0 +1,14 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.alist.api.core.common.paging.PagingResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Schema(description = "스케줄 작업 목록 조회 응답")
@Getter
@Setter
public class ScheduleListVo extends PagingResponse {
@Schema(description = "스케줄 작업 목록") private List<ScheduleListItemVo> scheduleList;
}
@@ -0,0 +1,26 @@
package com.alist.api.admin.modules.backOffice.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Schema(description = "스케줄 작업 실행 로그 목록 항목")
@Getter
@Setter
public class ScheduleLogListItemVo {
@Schema(description = "실행 로그 PK") private Long scheduleJobLogIdx;
@Schema(description = "스케줄 작업 PK") private Long scheduleJobIdx;
@Schema(description = "실행 식별자") private String runId;
@Schema(description = "작업 코드") private String jobName;
@Schema(description = "실행 시작 시각") private LocalDateTime startDate;
@Schema(description = "실행 종료 시각") private LocalDateTime endDate;
@Schema(description = "대상 건수") private Integer targetCount;
@Schema(description = "성공 건수") private Integer successCount;
@Schema(description = "실패 건수") private Integer failCount;
@Schema(description = "요약 메시지") private String message;
@Schema(description = "오류 메시지") private String errorMessage;
@Schema(description = "실행 서버") private String lockOwner;
@Schema(description = "실행 상태") private Short status;
@Schema(description = "기록 시각") private LocalDateTime createDate;
}
@@ -0,0 +1,14 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.alist.api.core.common.paging.PagingResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Schema(description = "스케줄 작업 실행 로그 목록 조회 응답")
@Getter
@Setter
public class ScheduleLogListVo extends PagingResponse {
@Schema(description = "스케줄 작업 실행 로그 목록") private List<ScheduleLogListItemVo> scheduleLogList;
}
@@ -0,0 +1,15 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "스케줄 작업 수정 응답")
@Getter
@Setter
public class ScheduleModifyVo {
@Schema(description = "수정 성공 여부", example = "true") private boolean updated;
@Schema(description = "스케줄 작업 PK", example = "9") private Long scheduleJobIdx;
@JsonIgnore @Schema(hidden = true) private int resultCode;
}
@@ -0,0 +1,31 @@
package com.alist.api.admin.modules.backOffice.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Schema(description = "스케줄 작업 상세 조회 응답")
@Getter
@Setter
public class ScheduleViewVo {
@Schema(description = "스케줄 작업 PK") private Long scheduleJobIdx;
@Schema(description = "작업 코드") private String jobName;
@Schema(description = "작업 그룹") private String jobGroup;
@Schema(description = "작업 설명") private String jobDescription;
@Schema(description = "크론식") private String cronExpr;
@Schema(description = "타임존") private String timezoneId;
@Schema(description = "최대 실행 허용 시간(초)") private Integer maxRunningSeconds;
@Schema(description = "마지막 실행 시작 시각") private LocalDateTime lastRunDate;
@Schema(description = "마지막 실행 종료 시각") private LocalDateTime lastEndDate;
@Schema(description = "마지막 성공 시각") private LocalDateTime lastSuccessDate;
@Schema(description = "마지막 상태") private Short lastStatus;
@Schema(description = "마지막 메시지") private String lastMessage;
@Schema(description = "실행 락 시각") private LocalDateTime runLockDate;
@Schema(description = "실행 락 소유자") private String runLockOwner;
@Schema(description = "사용 여부") private String useYn;
@Schema(description = "등록일시") private LocalDateTime createDate;
@Schema(description = "등록자 회원 PK") private Integer createMember;
@Schema(description = "수정일시") private LocalDateTime updateDate;
@Schema(description = "마지막 수정자 회원 PK") private Integer updateMember;
}
@@ -0,0 +1,19 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "SSO 클라이언트 등록 응답")
@Getter
@Setter
public class SsoAddVo {
@Schema(description = "등록 성공 여부", example = "true")
private boolean added;
@Schema(description = "등록된 SSO 클라이언트 PK", example = "19")
private Integer ssoClientIdx;
@JsonIgnore
@Schema(hidden = true)
private int resultCode;
}
@@ -0,0 +1,19 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "SSO 클라이언트 삭제 응답")
@Getter
@Setter
public class SsoDeleteVo {
@Schema(description = "삭제 성공 여부", example = "true")
private boolean deleted;
@Schema(description = "SSO 클라이언트 PK", example = "19")
private Integer ssoClientIdx;
@JsonIgnore
@Schema(hidden = true)
private int resultCode;
}
@@ -0,0 +1,29 @@
package com.alist.api.admin.modules.backOffice.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Schema(description = "SSO 클라이언트 목록 항목")
@Getter
@Setter
public class SsoListItemVo {
@Schema(description = "SSO 클라이언트 PK", example = "19")
private Integer ssoClientIdx;
@Schema(description = "클라이언트 식별자", example = "a-campus-client")
private String clientId;
@Schema(description = "클라이언트명", example = "a-campus.co.kr")
private String clientName;
@Schema(description = "허용 Redirect URI", example = "https://a-campus.co.kr/auth/callback")
private String redirectUri;
@Schema(description = "비고", example = "운영")
private String memo;
@Schema(description = "사용 여부. Y: 사용, N: 미사용", example = "Y")
private String useYn;
@Schema(description = "등록일시")
private LocalDateTime createDate;
@Schema(description = "수정일시")
private LocalDateTime updateDate;
}
@@ -0,0 +1,16 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.alist.api.core.common.paging.PagingResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Schema(description = "SSO 클라이언트 목록 조회 응답")
@Getter
@Setter
public class SsoListVo extends PagingResponse {
@Schema(description = "SSO 클라이언트 목록")
private List<SsoListItemVo> ssoList;
}
@@ -0,0 +1,19 @@
package com.alist.api.admin.modules.backOffice.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "SSO 클라이언트 수정 응답")
@Getter
@Setter
public class SsoModifyVo {
@Schema(description = "수정 성공 여부", example = "true")
private boolean updated;
@Schema(description = "SSO 클라이언트 PK", example = "19")
private Integer ssoClientIdx;
@JsonIgnore
@Schema(hidden = true)
private int resultCode;
}
@@ -0,0 +1,33 @@
package com.alist.api.admin.modules.backOffice.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Schema(description = "SSO 클라이언트 상세 조회 응답")
@Getter
@Setter
public class SsoViewVo {
@Schema(description = "SSO 클라이언트 PK", example = "19")
private Integer ssoClientIdx;
@Schema(description = "클라이언트 식별자", example = "a-campus-client")
private String clientId;
@Schema(description = "클라이언트명", example = "a-campus.co.kr")
private String clientName;
@Schema(description = "허용 Redirect URI", example = "https://a-campus.co.kr/auth/callback")
private String redirectUri;
@Schema(description = "비고", example = "운영")
private String memo;
@Schema(description = "사용 여부. Y: 사용, N: 미사용", example = "Y")
private String useYn;
@Schema(description = "등록일시")
private LocalDateTime createDate;
@Schema(description = "등록자 회원 PK", example = "1")
private Integer createMember;
@Schema(description = "수정일시")
private LocalDateTime updateDate;
@Schema(description = "마지막 수정자 회원 PK", example = "1")
private Integer updateMember;
}
@@ -0,0 +1,78 @@
package com.alist.api.admin.modules.file;
import com.alist.api.admin.modules.file.service.FileService;
import com.alist.api.admin.modules.file.form.SunEditorUploadForm;
import com.alist.api.admin.modules.file.vo.FileUploadVo;
import com.alist.api.admin.modules.file.vo.SunEditorUploadVo;
import com.alist.api.core.common.response.ApiResponse;
import com.alist.api.core.common.response.ApiResponseCode;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
@Tag(name = "D-1. 파일 업로드", description = "공통 파일 저장, 보기, 다운로드 API")
@RestController
@RequestMapping("/file")
public class FileController {
private final FileService fileService;
public FileController(FileService fileService) {
this.fileService = fileService;
}
@Operation(summary = "단순 파일 업로드", description = "DB 기록 없이 파일을 저장하고 업로드 경로를 반환합니다.")
@PostMapping(
value = "/upload"
, consumes = MediaType.MULTIPART_FORM_DATA_VALUE
)
public ResponseEntity<ApiResponse<FileUploadVo>> fileUpload(
@Parameter(description = "저장 폴더 키", example = "notice", required = true) @RequestParam String folder
, @Parameter(description = "업로드 파일", required = true) @RequestPart("file") MultipartFile multipartFile
) {
FileUploadVo fileUploadVo = fileService.insertFileUpload(folder, multipartFile);
return ApiResponse.entity(fileUploadVo, ApiResponseCode.CODE_200, "파일 업로드");
}
@Operation(
summary = "SunEditor 이미지 업로드"
, description = "여러 이미지를 저장하고 SunEditor result 배열 형식으로 공개 파일 URL을 반환합니다."
, requestBody = @RequestBody(content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA_VALUE, schema = @Schema(implementation = SunEditorUploadForm.class)))
)
@PostMapping(
value = "/suneditor/upload"
, consumes = MediaType.MULTIPART_FORM_DATA_VALUE
)
public ResponseEntity<SunEditorUploadVo> fileSunEditorUpload(
@Parameter(description = "저장 폴더 키", example = "editor") @RequestParam(value = "folder", defaultValue = "editor") String folder
, MultipartHttpServletRequest multipartHttpServletRequest
) {
return ResponseEntity.ok(fileService.insertFileSunEditorUpload(folder, multipartHttpServletRequest));
}
@Operation(summary = "파일 보기", description = "uploadPath 기준으로 파일을 inline 응답합니다.")
@GetMapping("/view")
public ResponseEntity<Resource> fileView(@Parameter(description = "업로드 파일 경로", example = "/uploads/notice/2026/07/20/a1b2c3d4.png", required = true) @RequestParam String path) {
return fileService.selectFileView(path);
}
@Operation(summary = "파일 다운로드", description = "uploadPath 기준으로 파일을 attachment 응답합니다.")
@GetMapping("/download")
public ResponseEntity<Resource> fileDownload(@Parameter(description = "업로드 파일 경로", example = "/uploads/notice/2026/07/20/a1b2c3d4.png", required = true) @RequestParam String path) {
return fileService.selectFileDownload(path);
}
}
@@ -0,0 +1,17 @@
package com.alist.api.admin.modules.file.form;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@Schema(description = "SunEditor 이미지 업로드 요청")
@Getter
@Setter
public class SunEditorUploadForm {
@ArraySchema(schema = @Schema(type = "string", format = "binary", description = "업로드 이미지 파일"))
private List<MultipartFile> files;
}
@@ -0,0 +1,58 @@
package com.alist.api.admin.modules.file.service;
import com.alist.api.admin.modules.file.vo.FileUploadVo;
import com.alist.api.admin.modules.file.vo.SunEditorUploadVo;
import com.alist.api.core.common.file.service.FileStorageService;
import com.alist.api.core.common.file.vo.FileStorageUploadVo;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import java.util.ArrayList;
import java.util.List;
@Service
public class FileService {
private final FileStorageService fileStorageService;
public FileService(FileStorageService fileStorageService) {
this.fileStorageService = fileStorageService;
}
public FileUploadVo insertFileUpload(String folder, MultipartFile multipartFile) {
FileStorageUploadVo fileStorageUploadVo = fileStorageService.saveFileUpload(folder, multipartFile);
FileUploadVo fileUploadVo = new FileUploadVo();
fileUploadVo.setUploadPath(fileStorageUploadVo.getUploadPath());
fileUploadVo.setOriginalFileName(fileStorageUploadVo.getOriginalFileName());
fileUploadVo.setStoredFileName(fileStorageUploadVo.getStoredFileName());
fileUploadVo.setFileExtension(fileStorageUploadVo.getFileExtension());
fileUploadVo.setContentType(fileStorageUploadVo.getContentType());
fileUploadVo.setFileSize(fileStorageUploadVo.getFileSize());
fileUploadVo.setWidth(fileStorageUploadVo.getWidth());
fileUploadVo.setHeight(fileStorageUploadVo.getHeight());
return fileUploadVo;
}
public SunEditorUploadVo insertFileSunEditorUpload(String folder, MultipartHttpServletRequest multipartHttpServletRequest) {
List<SunEditorUploadVo.Item> sunEditorUploadItemList = new ArrayList<>();
for (MultipartFile multipartFile : multipartHttpServletRequest.getFileMap().values()) {
FileStorageUploadVo fileStorageUploadVo = fileStorageService.saveFileUpload(folder, multipartFile);
String fileViewUrl = fileStorageService.buildFileViewUrl(fileStorageUploadVo.getUploadPath());
sunEditorUploadItemList.add(new SunEditorUploadVo.Item(fileViewUrl, fileStorageUploadVo.getOriginalFileName(), fileStorageUploadVo.getFileSize()));
}
return new SunEditorUploadVo(sunEditorUploadItemList);
}
public ResponseEntity<Resource> selectFileView(String path) {
return fileStorageService.loadFileResource(path, true);
}
public ResponseEntity<Resource> selectFileDownload(String path) {
return fileStorageService.loadFileResource(path, false);
}
}
@@ -0,0 +1,34 @@
package com.alist.api.admin.modules.file.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "파일 업로드 응답")
@Getter
@Setter
public class FileUploadVo {
@Schema(description = "업로드 파일 접근 경로", example = "/uploads/notice/2026/07/20/a1b2c3d4.png")
private String uploadPath;
@Schema(description = "원본 파일명", example = "sample.png")
private String originalFileName;
@Schema(description = "저장 파일명", example = "a1b2c3d4.png")
private String storedFileName;
@Schema(description = "파일 확장자", example = "png")
private String fileExtension;
@Schema(description = "Content-Type", example = "image/png")
private String contentType;
@Schema(description = "파일 크기(byte)", example = "102400")
private Long fileSize;
@Schema(description = "이미지 너비(px)", example = "800")
private Integer width;
@Schema(description = "이미지 높이(px)", example = "600")
private Integer height;
}
@@ -0,0 +1,39 @@
package com.alist.api.admin.modules.file.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Schema(description = "SunEditor 이미지 업로드 응답")
@Getter
@Setter
public class SunEditorUploadVo {
@Schema(description = "업로드 결과 목록")
private List<Item> result;
public SunEditorUploadVo(List<Item> result) {
this.result = result;
}
@Schema(description = "SunEditor 업로드 파일 항목")
@Getter
@Setter
public static class Item {
@Schema(description = "업로드 파일 URL", example = "https://file-alist.pjt.kr/uploads/editor/2026/07/20/a1b2c3d4.png")
private String url;
@Schema(description = "원본 파일명", example = "sample.png")
private String name;
@Schema(description = "파일 크기(byte)", example = "102400")
private Long size;
public Item(String url, String name, Long size) {
this.url = url;
this.name = name;
this.size = size;
}
}
}
@@ -0,0 +1,15 @@
package com.alist.api.admin.modules.main;
import io.swagger.v3.oas.annotations.Hidden;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Hidden
@Controller
public class MainController {
@GetMapping(value = "/")
public String index() {
return "redirect:/swagger-ui/index.html";
}
}
@@ -0,0 +1,114 @@
package com.alist.api.admin.modules.support;
import com.alist.api.admin.modules.support.form.NoticeAddForm;
import com.alist.api.admin.modules.support.form.NoticeDeleteForm;
import com.alist.api.admin.modules.support.form.NoticeListForm;
import com.alist.api.admin.modules.support.form.NoticeModifyForm;
import com.alist.api.admin.modules.support.form.NoticeViewForm;
import com.alist.api.admin.modules.support.service.NoticeService;
import com.alist.api.admin.modules.support.vo.NoticeAddVo;
import com.alist.api.admin.modules.support.vo.NoticeDeleteVo;
import com.alist.api.admin.modules.support.vo.NoticeListVo;
import com.alist.api.admin.modules.support.vo.NoticeModifyVo;
import com.alist.api.admin.modules.support.vo.NoticeViewVo;
import com.alist.api.core.common.response.ApiResponse;
import com.alist.api.core.common.response.ApiResponseCode;
import io.swagger.v3.oas.annotations.Operation;
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.ModelAttribute;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "C-1. 공지사항 관리", description = "지원 기능 공지사항 관리 API")
@RestController
@RequestMapping("/support/notice")
public class NoticeController {
private final NoticeService noticeService;
public NoticeController(NoticeService noticeService) {
this.noticeService = noticeService;
}
@Operation(summary = "공지사항 목록 조회", description = "검색 조건과 페이징으로 공지사항 목록을 조회합니다.")
@GetMapping("/list")
public ResponseEntity<ApiResponse<NoticeListVo>> noticeList(@Valid @ModelAttribute NoticeListForm noticeListForm) {
NoticeListVo noticeListVo = noticeService.selectNoticeList(noticeListForm);
if (noticeListVo.getNoticeList().isEmpty()) {
return ApiResponse.entity(noticeListVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(noticeListVo, ApiResponseCode.CODE_2001, "공지사항");
}
@Operation(summary = "공지사항 상세 조회", description = "공지사항 PK 기준으로 상세 정보와 대상 캠퍼스, 첨부파일을 조회합니다.")
@GetMapping("/view")
public ResponseEntity<ApiResponse<NoticeViewVo>> noticeView(@Valid @ModelAttribute NoticeViewForm noticeViewForm) {
NoticeViewVo noticeViewVo = noticeService.selectNoticeView(noticeViewForm);
if (noticeViewVo == null) {
return ApiResponse.entity((NoticeViewVo) null, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(noticeViewVo, ApiResponseCode.CODE_2001, "공지사항");
}
@Operation(summary = "공지사항 등록", description = "공지사항과 대상 캠퍼스, 첨부파일을 등록합니다.")
@PostMapping("/add")
public ResponseEntity<ApiResponse<NoticeAddVo>> noticeAdd(@Valid @RequestBody NoticeAddForm noticeAddForm) {
NoticeAddVo noticeAddVo = noticeService.insertNoticeAdd(noticeAddForm);
if (noticeAddVo.getResultCode() == 401) {
return ApiResponse.entity(noticeAddVo, ApiResponseCode.CODE_401);
}
if (noticeAddVo.getResultCode() == 4001) {
return ApiResponse.entity(noticeAddVo, ApiResponseCode.CODE_4001);
}
return ApiResponse.entity(noticeAddVo, ApiResponseCode.CODE_2002, "공지사항");
}
@Operation(summary = "공지사항 수정", description = "공지사항 기본 정보와 대상 캠퍼스, 첨부파일을 수정합니다.")
@PutMapping("/modify")
public ResponseEntity<ApiResponse<NoticeModifyVo>> noticeModify(@Valid @RequestBody NoticeModifyForm noticeModifyForm) {
NoticeModifyVo noticeModifyVo = noticeService.updateNoticeModify(noticeModifyForm);
if (noticeModifyVo.getResultCode() == 401) {
return ApiResponse.entity(noticeModifyVo, ApiResponseCode.CODE_401);
}
if (noticeModifyVo.getResultCode() == 4001) {
return ApiResponse.entity(noticeModifyVo, ApiResponseCode.CODE_4001);
}
if (!noticeModifyVo.isUpdated()) {
return ApiResponse.entity(noticeModifyVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(noticeModifyVo, ApiResponseCode.CODE_2005, "공지사항");
}
@Operation(summary = "공지사항 삭제", description = "공지사항과 등록된 첨부파일을 논리 삭제 처리합니다.")
@PatchMapping("/delete")
public ResponseEntity<ApiResponse<NoticeDeleteVo>> noticeDelete(@Valid @RequestBody NoticeDeleteForm noticeDeleteForm) {
NoticeDeleteVo noticeDeleteVo = noticeService.updateNoticeDelete(noticeDeleteForm);
if (noticeDeleteVo.getResultCode() == 401) {
return ApiResponse.entity(noticeDeleteVo, ApiResponseCode.CODE_401);
}
if (!noticeDeleteVo.isDeleted()) {
return ApiResponse.entity(noticeDeleteVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(noticeDeleteVo, ApiResponseCode.CODE_2005, "공지사항");
}
}
@@ -0,0 +1,56 @@
package com.alist.api.admin.modules.support.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Schema(description = "공지사항 등록 요청")
@Getter
@Setter
public class NoticeAddForm {
@NotBlank
@Size(max = 30)
@Schema(description = "공지 유형. GENERAL, SYSTEM, EVENT", example = "GENERAL", requiredMode = Schema.RequiredMode.REQUIRED)
private String noticeType;
@NotBlank
@Size(max = 30)
@Schema(description = "노출 범위. ALL, ADMIN, TEACHER, STUDENT, CAMPUS", example = "CAMPUS", requiredMode = Schema.RequiredMode.REQUIRED)
private String targetScope;
@Schema(description = "대상 캠퍼스 PK 목록. targetScope가 CAMPUS일 때 필수", example = "[1, 2]")
private List<Long> campusIdxList;
@NotBlank
@Size(max = 200)
@Schema(description = "제목", example = "서비스 점검 안내", requiredMode = Schema.RequiredMode.REQUIRED)
private String title;
@NotBlank
@Schema(description = "내용", example = "서비스 점검이 예정되어 있습니다.", requiredMode = Schema.RequiredMode.REQUIRED)
private String content;
@Pattern(regexp = "^(Y|N)$")
@Schema(description = "상단 고정 여부. 미입력 시 N", example = "N")
private String pinYn;
@Schema(description = "노출 시작일", example = "2026-07-01T00:00:00")
private String startDate;
@Schema(description = "노출 종료일", example = "2026-07-31T23:59:59")
private String endDate;
@Pattern(regexp = "^(Y|N)$")
@Schema(description = "사용 여부. 미입력 시 Y", example = "Y")
private String useYn;
@Valid
@Schema(description = "첨부파일 목록")
private List<NoticeFileForm> fileList;
}
@@ -0,0 +1,15 @@
package com.alist.api.admin.modules.support.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "공지사항 삭제 요청")
@Getter
@Setter
public class NoticeDeleteForm {
@NotNull
@Schema(description = "공지사항 PK", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
private Long noticeIdx;
}
@@ -0,0 +1,40 @@
package com.alist.api.admin.modules.support.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "공지사항 첨부파일 요청")
@Getter
@Setter
public class NoticeFileForm {
@NotBlank(message = "원본 파일명을 입력해주세요.")
@Size(max = 255)
@Schema(description = "원본 파일명", example = "notice.pdf", requiredMode = Schema.RequiredMode.REQUIRED)
private String fileOriginalName;
@NotBlank(message = "저장 파일명을 입력해주세요.")
@Size(max = 255)
@Schema(description = "저장 파일명", example = "20260701120000_notice.pdf", requiredMode = Schema.RequiredMode.REQUIRED)
private String fileSaveName;
@NotBlank(message = "파일 경로를 입력해주세요.")
@Size(max = 500)
@Schema(description = "파일 경로", example = "/uploads/notice/2026/07/notice.pdf", requiredMode = Schema.RequiredMode.REQUIRED)
private String filePath;
@Min(0)
@Schema(description = "파일 크기", example = "2048")
private Long fileSize;
@Size(max = 20)
@Schema(description = "파일 확장자", example = "pdf")
private String fileExt;
@Min(0)
@Schema(description = "정렬 순서. 미입력 시 0", example = "0")
private Integer sortOrder;
}
@@ -0,0 +1,29 @@
package com.alist.api.admin.modules.support.form;
import com.alist.api.core.common.paging.PagingRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "공지사항 목록 검색 조건")
@Getter
@Setter
public class NoticeListForm extends PagingRequest {
@Schema(description = "검색어. 제목과 내용 검색", example = "점검")
private String keyword;
@Schema(description = "공지 유형. GENERAL, SYSTEM, EVENT", example = "GENERAL")
private String noticeType;
@Schema(description = "노출 범위. ALL, ADMIN, TEACHER, STUDENT, CAMPUS", example = "CAMPUS")
private String targetScope;
@Schema(description = "캠퍼스 PK", example = "1")
private Long campusIdx;
@Schema(description = "상단 고정 여부. Y: 고정, N: 미고정", example = "Y")
private String pinYn;
@Schema(description = "사용 여부. Y: 사용, N: 미사용", example = "Y")
private String useYn;
}
@@ -0,0 +1,20 @@
package com.alist.api.admin.modules.support.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Schema(description = "공지사항 수정 요청")
@Getter
@Setter
public class NoticeModifyForm extends NoticeAddForm {
@NotNull
@Schema(description = "공지사항 PK", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
private Long noticeIdx;
@Schema(description = "삭제할 기존 첨부파일 PK 목록", example = "[1, 2]")
private List<Long> deleteFileIdxList;
}
@@ -0,0 +1,15 @@
package com.alist.api.admin.modules.support.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "공지사항 상세 조회 요청")
@Getter
@Setter
public class NoticeViewForm {
@NotNull(message = "공지사항 PK를 입력해주세요.")
@Schema(description = "공지사항 PK", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
private Long noticeIdx;
}
@@ -0,0 +1,373 @@
package com.alist.api.admin.modules.support.service;
import com.alist.api.admin.modules.support.form.NoticeAddForm;
import com.alist.api.admin.modules.support.form.NoticeDeleteForm;
import com.alist.api.admin.modules.support.form.NoticeFileForm;
import com.alist.api.admin.modules.support.form.NoticeListForm;
import com.alist.api.admin.modules.support.form.NoticeModifyForm;
import com.alist.api.admin.modules.support.form.NoticeViewForm;
import com.alist.api.admin.modules.support.vo.NoticeAddVo;
import com.alist.api.admin.modules.support.vo.NoticeCampusVo;
import com.alist.api.admin.modules.support.vo.NoticeDeleteVo;
import com.alist.api.admin.modules.support.vo.NoticeFileVo;
import com.alist.api.admin.modules.support.vo.NoticeListItemVo;
import com.alist.api.admin.modules.support.vo.NoticeListVo;
import com.alist.api.admin.modules.support.vo.NoticeModifyVo;
import com.alist.api.admin.modules.support.vo.NoticeViewVo;
import com.alist.api.core.common.utils.SecurityUtil;
import com.alist.api.core.modules.bespoke.notice.dto.NoticeNoticeListDto;
import com.alist.api.core.modules.bespoke.notice.dto.NoticeNoticeViewDto;
import com.alist.api.core.modules.bespoke.notice.service.NoticeNoticeService;
import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeCampusListItemVo;
import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeFileListItemVo;
import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeListItemVo;
import com.alist.api.core.modules.bespoke.notice.vo.NoticeNoticeViewVo;
import com.alist.api.core.modules.standard.notice.dto.NoticeAddDto;
import com.alist.api.core.modules.standard.notice.dto.NoticeDeleteDto;
import com.alist.api.core.modules.standard.notice.dto.NoticeModifyDto;
import com.alist.api.core.modules.standard.noticeCampus.dto.NoticeCampusAddDto;
import com.alist.api.core.modules.standard.noticeCampus.dto.NoticeCampusDeleteDto;
import com.alist.api.core.modules.standard.noticeCampus.service.NoticeCampusService;
import com.alist.api.core.modules.standard.noticeFile.dto.NoticeFileAddDto;
import com.alist.api.core.modules.standard.noticeFile.dto.NoticeFileDeleteDto;
import com.alist.api.core.modules.standard.noticeFile.service.NoticeFileService;
import com.alist.api.core.modules.standard.testUserToken.dto.TestUserTokenViewDto;
import com.alist.api.core.modules.standard.testUserToken.service.TestUserTokenService;
import com.alist.api.core.modules.standard.testUserToken.vo.TestUserTokenViewVo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Service("supportNoticeService")
public class NoticeService {
private final com.alist.api.core.modules.standard.notice.service.NoticeService noticeStandardService;
private final NoticeCampusService noticeCampusService;
private final NoticeFileService noticeFileService;
private final NoticeNoticeService noticeNoticeService;
private final TestUserTokenService testUserTokenService;
public NoticeService(com.alist.api.core.modules.standard.notice.service.NoticeService noticeStandardService, NoticeCampusService noticeCampusService, NoticeFileService noticeFileService, NoticeNoticeService noticeNoticeService, TestUserTokenService testUserTokenService) {
this.noticeStandardService = noticeStandardService;
this.noticeCampusService = noticeCampusService;
this.noticeFileService = noticeFileService;
this.noticeNoticeService = noticeNoticeService;
this.testUserTokenService = testUserTokenService;
}
@Transactional(readOnly = true)
public NoticeListVo selectNoticeList(NoticeListForm noticeListForm) {
NoticeNoticeListDto noticeNoticeListDto = new NoticeNoticeListDto();
noticeNoticeListDto.setPage(noticeListForm.getPage());
noticeNoticeListDto.setSize(noticeListForm.getSize());
noticeNoticeListDto.setKeyword(noticeListForm.getKeyword() == null || noticeListForm.getKeyword().isBlank() ? null : noticeListForm.getKeyword().trim());
noticeNoticeListDto.setNoticeType(noticeListForm.getNoticeType() == null || noticeListForm.getNoticeType().isBlank() ? null : noticeListForm.getNoticeType().trim());
noticeNoticeListDto.setTargetScope(noticeListForm.getTargetScope() == null || noticeListForm.getTargetScope().isBlank() ? null : noticeListForm.getTargetScope().trim());
noticeNoticeListDto.setCampusIdx(noticeListForm.getCampusIdx());
noticeNoticeListDto.setPinYn(noticeListForm.getPinYn() == null || noticeListForm.getPinYn().isBlank() ? null : noticeListForm.getPinYn().trim());
noticeNoticeListDto.setUseYn(noticeListForm.getUseYn() == null || noticeListForm.getUseYn().isBlank() ? null : noticeListForm.getUseYn().trim());
int totalCount = noticeNoticeService.noticeNoticeListCount(noticeNoticeListDto);
List<NoticeNoticeListItemVo> noticeNoticeList = noticeNoticeService.noticeNoticeList(noticeNoticeListDto);
List<NoticeListItemVo> noticeList = new ArrayList<>();
for (NoticeNoticeListItemVo noticeNoticeListItemVo : noticeNoticeList) {
NoticeListItemVo noticeListItemVo = new NoticeListItemVo();
noticeListItemVo.setNoticeIdx(noticeNoticeListItemVo.getNoticeIdx());
noticeListItemVo.setNoticeType(noticeNoticeListItemVo.getNoticeType());
noticeListItemVo.setTargetScope(noticeNoticeListItemVo.getTargetScope());
noticeListItemVo.setTitle(noticeNoticeListItemVo.getTitle());
noticeListItemVo.setPinYn(noticeNoticeListItemVo.getPinYn());
noticeListItemVo.setStartDate(noticeNoticeListItemVo.getStartDate());
noticeListItemVo.setEndDate(noticeNoticeListItemVo.getEndDate());
noticeListItemVo.setViewCount(noticeNoticeListItemVo.getViewCount());
noticeListItemVo.setUseYn(noticeNoticeListItemVo.getUseYn());
noticeListItemVo.setCreateDate(noticeNoticeListItemVo.getCreateDate());
noticeListItemVo.setUpdateDate(noticeNoticeListItemVo.getUpdateDate());
noticeList.add(noticeListItemVo);
}
NoticeListVo noticeListVo = new NoticeListVo();
noticeListVo.setNoticeList(noticeList);
noticeListVo.setPaging(noticeNoticeListDto, totalCount);
return noticeListVo;
}
@Transactional(readOnly = true)
public NoticeViewVo selectNoticeView(NoticeViewForm noticeViewForm) {
NoticeNoticeViewDto noticeNoticeViewDto = new NoticeNoticeViewDto();
noticeNoticeViewDto.setNoticeIdx(noticeViewForm.getNoticeIdx());
NoticeNoticeViewVo noticeNoticeViewVo = noticeNoticeService.noticeNoticeView(noticeNoticeViewDto);
if (noticeNoticeViewVo == null) {
return null;
}
NoticeViewVo noticeViewVo = new NoticeViewVo();
noticeViewVo.setNoticeIdx(noticeNoticeViewVo.getNoticeIdx());
noticeViewVo.setNoticeType(noticeNoticeViewVo.getNoticeType());
noticeViewVo.setTargetScope(noticeNoticeViewVo.getTargetScope());
noticeViewVo.setTitle(noticeNoticeViewVo.getTitle());
noticeViewVo.setContent(noticeNoticeViewVo.getContent());
noticeViewVo.setPinYn(noticeNoticeViewVo.getPinYn());
noticeViewVo.setStartDate(noticeNoticeViewVo.getStartDate());
noticeViewVo.setEndDate(noticeNoticeViewVo.getEndDate());
noticeViewVo.setViewCount(noticeNoticeViewVo.getViewCount());
noticeViewVo.setUseYn(noticeNoticeViewVo.getUseYn());
noticeViewVo.setCreateDate(noticeNoticeViewVo.getCreateDate());
noticeViewVo.setCreateMember(noticeNoticeViewVo.getCreateMember());
noticeViewVo.setUpdateDate(noticeNoticeViewVo.getUpdateDate());
noticeViewVo.setUpdateMember(noticeNoticeViewVo.getUpdateMember());
List<NoticeCampusVo> campusList = new ArrayList<>();
List<NoticeNoticeCampusListItemVo> noticeNoticeCampusList = noticeNoticeService.noticeNoticeCampusList(noticeNoticeViewDto);
for (NoticeNoticeCampusListItemVo noticeNoticeCampusListItemVo : noticeNoticeCampusList) {
NoticeCampusVo noticeCampusVo = new NoticeCampusVo();
noticeCampusVo.setCampusIdx(noticeNoticeCampusListItemVo.getCampusIdx());
campusList.add(noticeCampusVo);
}
noticeViewVo.setCampusList(campusList);
List<NoticeFileVo> fileList = new ArrayList<>();
List<NoticeNoticeFileListItemVo> noticeNoticeFileList = noticeNoticeService.noticeNoticeFileList(noticeNoticeViewDto);
for (NoticeNoticeFileListItemVo noticeNoticeFileListItemVo : noticeNoticeFileList) {
NoticeFileVo noticeFileVo = new NoticeFileVo();
noticeFileVo.setNoticeFileIdx(noticeNoticeFileListItemVo.getNoticeFileIdx());
noticeFileVo.setFileOriginalName(noticeNoticeFileListItemVo.getFileOriginalName());
noticeFileVo.setFileSaveName(noticeNoticeFileListItemVo.getFileSaveName());
noticeFileVo.setFilePath(noticeNoticeFileListItemVo.getFilePath());
noticeFileVo.setFileSize(noticeNoticeFileListItemVo.getFileSize());
noticeFileVo.setFileExt(noticeNoticeFileListItemVo.getFileExt());
noticeFileVo.setSortOrder(noticeNoticeFileListItemVo.getSortOrder());
noticeFileVo.setCreateDate(noticeNoticeFileListItemVo.getCreateDate());
fileList.add(noticeFileVo);
}
noticeViewVo.setFileList(fileList);
return noticeViewVo;
}
@Transactional
public NoticeAddVo insertNoticeAdd(NoticeAddForm noticeAddForm) {
NoticeAddVo noticeAddVo = new NoticeAddVo();
if ("CAMPUS".equals(noticeAddForm.getTargetScope()) && (noticeAddForm.getCampusIdxList() == null || noticeAddForm.getCampusIdxList().isEmpty())) {
noticeAddVo.setResultCode(4001);
return noticeAddVo;
}
Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx();
if (userTokenIdx == null) {
noticeAddVo.setResultCode(401);
return noticeAddVo;
}
TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto();
testUserTokenViewDto.setUserTokenIdx(userTokenIdx);
TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto);
if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) {
noticeAddVo.setResultCode(401);
return noticeAddVo;
}
Long userIdx = Long.valueOf(testUserTokenViewVo.getUserIdx());
NoticeAddDto noticeAddDto = new NoticeAddDto();
noticeAddDto.setNoticeType(noticeAddForm.getNoticeType().trim());
noticeAddDto.setTargetScope(noticeAddForm.getTargetScope().trim());
noticeAddDto.setTitle(noticeAddForm.getTitle().trim());
noticeAddDto.setContent(noticeAddForm.getContent().trim());
noticeAddDto.setPinYn(noticeAddForm.getPinYn() == null || noticeAddForm.getPinYn().isBlank() ? "N" : noticeAddForm.getPinYn().trim());
noticeAddDto.setStartDate(noticeAddForm.getStartDate() == null || noticeAddForm.getStartDate().isBlank() ? null : LocalDateTime.parse(noticeAddForm.getStartDate().trim()));
noticeAddDto.setEndDate(noticeAddForm.getEndDate() == null || noticeAddForm.getEndDate().isBlank() ? null : LocalDateTime.parse(noticeAddForm.getEndDate().trim()));
noticeAddDto.setUseYn(noticeAddForm.getUseYn() == null || noticeAddForm.getUseYn().isBlank() ? "Y" : noticeAddForm.getUseYn().trim());
noticeAddDto.setCreateMember(userIdx);
noticeAddDto.setUpdateMember(userIdx);
com.alist.api.core.modules.standard.notice.vo.NoticeAddVo noticeStandardAddVo = noticeStandardService.insertNoticeAdd(noticeAddDto);
noticeAddVo.setAdded(noticeStandardAddVo.isAdded());
noticeAddVo.setNoticeIdx(noticeStandardAddVo.getNoticeIdx());
noticeAddVo.setResultCode(noticeStandardAddVo.getResultCode());
if (!noticeAddVo.isAdded()) {
return noticeAddVo;
}
if (noticeAddForm.getCampusIdxList() != null) {
for (Long campusIdx : noticeAddForm.getCampusIdxList()) {
NoticeCampusAddDto noticeCampusAddDto = new NoticeCampusAddDto();
noticeCampusAddDto.setNoticeIdx(noticeAddVo.getNoticeIdx());
noticeCampusAddDto.setCampusIdx(campusIdx);
noticeCampusAddDto.setCreateMember(userIdx);
noticeCampusService.insertNoticeCampusAdd(noticeCampusAddDto);
}
}
if (noticeAddForm.getFileList() != null) {
for (NoticeFileForm noticeFileForm : noticeAddForm.getFileList()) {
NoticeFileAddDto noticeFileAddDto = new NoticeFileAddDto();
noticeFileAddDto.setNoticeIdx(noticeAddVo.getNoticeIdx());
noticeFileAddDto.setFileOriginalName(noticeFileForm.getFileOriginalName().trim());
noticeFileAddDto.setFileSaveName(noticeFileForm.getFileSaveName().trim());
noticeFileAddDto.setFilePath(noticeFileForm.getFilePath().trim());
noticeFileAddDto.setFileSize(noticeFileForm.getFileSize());
noticeFileAddDto.setFileExt(noticeFileForm.getFileExt() == null ? null : noticeFileForm.getFileExt().trim());
noticeFileAddDto.setSortOrder(noticeFileForm.getSortOrder());
noticeFileAddDto.setCreateMember(userIdx);
noticeFileAddDto.setUpdateMember(userIdx);
noticeFileService.insertNoticeFileAdd(noticeFileAddDto);
}
}
return noticeAddVo;
}
@Transactional
public NoticeModifyVo updateNoticeModify(NoticeModifyForm noticeModifyForm) {
NoticeModifyVo noticeModifyVo = new NoticeModifyVo();
noticeModifyVo.setNoticeIdx(noticeModifyForm.getNoticeIdx());
if ("CAMPUS".equals(noticeModifyForm.getTargetScope()) && (noticeModifyForm.getCampusIdxList() == null || noticeModifyForm.getCampusIdxList().isEmpty())) {
noticeModifyVo.setResultCode(4001);
return noticeModifyVo;
}
Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx();
if (userTokenIdx == null) {
noticeModifyVo.setResultCode(401);
return noticeModifyVo;
}
TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto();
testUserTokenViewDto.setUserTokenIdx(userTokenIdx);
TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto);
if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) {
noticeModifyVo.setResultCode(401);
return noticeModifyVo;
}
Long userIdx = Long.valueOf(testUserTokenViewVo.getUserIdx());
NoticeModifyDto noticeModifyDto = new NoticeModifyDto();
noticeModifyDto.setNoticeIdx(noticeModifyForm.getNoticeIdx());
noticeModifyDto.setNoticeType(noticeModifyForm.getNoticeType().trim());
noticeModifyDto.setTargetScope(noticeModifyForm.getTargetScope().trim());
noticeModifyDto.setTitle(noticeModifyForm.getTitle().trim());
noticeModifyDto.setContent(noticeModifyForm.getContent().trim());
noticeModifyDto.setPinYn(noticeModifyForm.getPinYn() == null || noticeModifyForm.getPinYn().isBlank() ? "N" : noticeModifyForm.getPinYn().trim());
noticeModifyDto.setStartDate(noticeModifyForm.getStartDate() == null || noticeModifyForm.getStartDate().isBlank() ? null : LocalDateTime.parse(noticeModifyForm.getStartDate().trim()));
noticeModifyDto.setEndDate(noticeModifyForm.getEndDate() == null || noticeModifyForm.getEndDate().isBlank() ? null : LocalDateTime.parse(noticeModifyForm.getEndDate().trim()));
noticeModifyDto.setUseYn(noticeModifyForm.getUseYn() == null || noticeModifyForm.getUseYn().isBlank() ? "Y" : noticeModifyForm.getUseYn().trim());
noticeModifyDto.setUpdateMember(userIdx);
com.alist.api.core.modules.standard.notice.vo.NoticeModifyVo noticeStandardModifyVo = noticeStandardService.updateNoticeModify(noticeModifyDto);
noticeModifyVo.setUpdated(noticeStandardModifyVo.isUpdated());
noticeModifyVo.setResultCode(noticeStandardModifyVo.getResultCode());
if (!noticeModifyVo.isUpdated()) {
return noticeModifyVo;
}
NoticeNoticeViewDto noticeNoticeViewDto = new NoticeNoticeViewDto();
noticeNoticeViewDto.setNoticeIdx(noticeModifyForm.getNoticeIdx());
List<NoticeNoticeCampusListItemVo> noticeNoticeCampusList = noticeNoticeService.noticeNoticeCampusList(noticeNoticeViewDto);
for (NoticeNoticeCampusListItemVo noticeNoticeCampusListItemVo : noticeNoticeCampusList) {
NoticeCampusDeleteDto noticeCampusDeleteDto = new NoticeCampusDeleteDto();
noticeCampusDeleteDto.setNoticeCampusIdx(noticeNoticeCampusListItemVo.getNoticeCampusIdx());
noticeCampusService.deleteNoticeCampusDelete(noticeCampusDeleteDto);
}
if (noticeModifyForm.getCampusIdxList() != null) {
for (Long campusIdx : noticeModifyForm.getCampusIdxList()) {
NoticeCampusAddDto noticeCampusAddDto = new NoticeCampusAddDto();
noticeCampusAddDto.setNoticeIdx(noticeModifyForm.getNoticeIdx());
noticeCampusAddDto.setCampusIdx(campusIdx);
noticeCampusAddDto.setCreateMember(userIdx);
noticeCampusService.insertNoticeCampusAdd(noticeCampusAddDto);
}
}
if (noticeModifyForm.getDeleteFileIdxList() != null) {
for (Long noticeFileIdx : noticeModifyForm.getDeleteFileIdxList()) {
NoticeFileDeleteDto noticeFileDeleteDto = new NoticeFileDeleteDto();
noticeFileDeleteDto.setNoticeFileIdx(noticeFileIdx);
noticeFileDeleteDto.setUpdateMember(userIdx);
noticeFileService.updateNoticeFileDelete(noticeFileDeleteDto);
}
}
if (noticeModifyForm.getFileList() != null) {
for (NoticeFileForm noticeFileForm : noticeModifyForm.getFileList()) {
NoticeFileAddDto noticeFileAddDto = new NoticeFileAddDto();
noticeFileAddDto.setNoticeIdx(noticeModifyForm.getNoticeIdx());
noticeFileAddDto.setFileOriginalName(noticeFileForm.getFileOriginalName().trim());
noticeFileAddDto.setFileSaveName(noticeFileForm.getFileSaveName().trim());
noticeFileAddDto.setFilePath(noticeFileForm.getFilePath().trim());
noticeFileAddDto.setFileSize(noticeFileForm.getFileSize());
noticeFileAddDto.setFileExt(noticeFileForm.getFileExt() == null ? null : noticeFileForm.getFileExt().trim());
noticeFileAddDto.setSortOrder(noticeFileForm.getSortOrder());
noticeFileAddDto.setCreateMember(userIdx);
noticeFileAddDto.setUpdateMember(userIdx);
noticeFileService.insertNoticeFileAdd(noticeFileAddDto);
}
}
return noticeModifyVo;
}
@Transactional
public NoticeDeleteVo updateNoticeDelete(NoticeDeleteForm noticeDeleteForm) {
NoticeDeleteVo noticeDeleteVo = new NoticeDeleteVo();
noticeDeleteVo.setNoticeIdx(noticeDeleteForm.getNoticeIdx());
Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx();
if (userTokenIdx == null) {
noticeDeleteVo.setResultCode(401);
return noticeDeleteVo;
}
TestUserTokenViewDto testUserTokenViewDto = new TestUserTokenViewDto();
testUserTokenViewDto.setUserTokenIdx(userTokenIdx);
TestUserTokenViewVo testUserTokenViewVo = testUserTokenService.selectTestUserTokenView(testUserTokenViewDto);
if (testUserTokenViewVo == null || testUserTokenViewVo.getUserIdx() == null) {
noticeDeleteVo.setResultCode(401);
return noticeDeleteVo;
}
Long userIdx = Long.valueOf(testUserTokenViewVo.getUserIdx());
NoticeDeleteDto noticeDeleteDto = new NoticeDeleteDto();
noticeDeleteDto.setNoticeIdx(noticeDeleteForm.getNoticeIdx());
noticeDeleteDto.setUpdateMember(userIdx);
com.alist.api.core.modules.standard.notice.vo.NoticeDeleteVo noticeStandardDeleteVo = noticeStandardService.updateNoticeDelete(noticeDeleteDto);
noticeDeleteVo.setDeleted(noticeStandardDeleteVo.isDeleted());
noticeDeleteVo.setResultCode(noticeStandardDeleteVo.getResultCode());
if (!noticeDeleteVo.isDeleted()) {
return noticeDeleteVo;
}
NoticeNoticeViewDto noticeNoticeViewDto = new NoticeNoticeViewDto();
noticeNoticeViewDto.setNoticeIdx(noticeDeleteForm.getNoticeIdx());
List<NoticeNoticeFileListItemVo> noticeNoticeFileList = noticeNoticeService.noticeNoticeFileList(noticeNoticeViewDto);
for (NoticeNoticeFileListItemVo noticeNoticeFileListItemVo : noticeNoticeFileList) {
NoticeFileDeleteDto noticeFileDeleteDto = new NoticeFileDeleteDto();
noticeFileDeleteDto.setNoticeFileIdx(noticeNoticeFileListItemVo.getNoticeFileIdx());
noticeFileDeleteDto.setUpdateMember(userIdx);
noticeFileService.updateNoticeFileDelete(noticeFileDeleteDto);
}
return noticeDeleteVo;
}
}
@@ -0,0 +1,21 @@
package com.alist.api.admin.modules.support.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "공지사항 등록 응답")
@Getter
@Setter
public class NoticeAddVo {
@Schema(description = "등록 성공 여부", example = "true")
private boolean added;
@Schema(description = "등록된 공지사항 PK", example = "1")
private Long noticeIdx;
@JsonIgnore
@Schema(hidden = true)
private int resultCode;
}
@@ -0,0 +1,13 @@
package com.alist.api.admin.modules.support.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "공지사항 대상 캠퍼스")
@Getter
@Setter
public class NoticeCampusVo {
@Schema(description = "캠퍼스 PK", example = "1")
private Long campusIdx;
}
@@ -0,0 +1,21 @@
package com.alist.api.admin.modules.support.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "공지사항 삭제 응답")
@Getter
@Setter
public class NoticeDeleteVo {
@Schema(description = "삭제 성공 여부", example = "true")
private boolean deleted;
@Schema(description = "공지사항 PK", example = "1")
private Long noticeIdx;
@JsonIgnore
@Schema(hidden = true)
private int resultCode;
}
@@ -0,0 +1,36 @@
package com.alist.api.admin.modules.support.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Schema(description = "공지사항 첨부파일 응답")
@Getter
@Setter
public class NoticeFileVo {
@Schema(description = "공지사항 첨부파일 PK", example = "1")
private Long noticeFileIdx;
@Schema(description = "원본 파일명", example = "notice.pdf")
private String fileOriginalName;
@Schema(description = "저장 파일명", example = "20260701120000_notice.pdf")
private String fileSaveName;
@Schema(description = "파일 경로", example = "/uploads/notice/2026/07/notice.pdf")
private String filePath;
@Schema(description = "파일 크기", example = "2048")
private Long fileSize;
@Schema(description = "파일 확장자", example = "pdf")
private String fileExt;
@Schema(description = "정렬 순서", example = "0")
private Integer sortOrder;
@Schema(description = "생성일시")
private LocalDateTime createDate;
}
@@ -0,0 +1,45 @@
package com.alist.api.admin.modules.support.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Schema(description = "공지사항 목록 항목")
@Getter
@Setter
public class NoticeListItemVo {
@Schema(description = "공지사항 PK", example = "1")
private Long noticeIdx;
@Schema(description = "공지 유형", example = "GENERAL")
private String noticeType;
@Schema(description = "노출 범위", example = "CAMPUS")
private String targetScope;
@Schema(description = "제목", example = "서비스 점검 안내")
private String title;
@Schema(description = "상단 고정 여부", example = "N")
private String pinYn;
@Schema(description = "노출 시작일")
private LocalDateTime startDate;
@Schema(description = "노출 종료일")
private LocalDateTime endDate;
@Schema(description = "조회수", example = "0")
private Integer viewCount;
@Schema(description = "사용 여부", example = "Y")
private String useYn;
@Schema(description = "생성일시")
private LocalDateTime createDate;
@Schema(description = "수정일시")
private LocalDateTime updateDate;
}
@@ -0,0 +1,16 @@
package com.alist.api.admin.modules.support.vo;
import com.alist.api.core.common.paging.PagingResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Schema(description = "공지사항 목록 조회 응답")
@Getter
@Setter
public class NoticeListVo extends PagingResponse {
@Schema(description = "공지사항 목록")
private List<NoticeListItemVo> noticeList;
}
@@ -0,0 +1,21 @@
package com.alist.api.admin.modules.support.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "공지사항 수정 응답")
@Getter
@Setter
public class NoticeModifyVo {
@Schema(description = "수정 성공 여부", example = "true")
private boolean updated;
@Schema(description = "공지사항 PK", example = "1")
private Long noticeIdx;
@JsonIgnore
@Schema(hidden = true)
private int resultCode;
}
@@ -0,0 +1,61 @@
package com.alist.api.admin.modules.support.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "공지사항 상세 조회 응답")
@Getter
@Setter
public class NoticeViewVo {
@Schema(description = "공지사항 PK", example = "1")
private Long noticeIdx;
@Schema(description = "공지 유형", example = "GENERAL")
private String noticeType;
@Schema(description = "노출 범위", example = "CAMPUS")
private String targetScope;
@Schema(description = "제목", example = "서비스 점검 안내")
private String title;
@Schema(description = "내용", example = "서비스 점검이 예정되어 있습니다.")
private String content;
@Schema(description = "상단 고정 여부", example = "N")
private String pinYn;
@Schema(description = "노출 시작일")
private LocalDateTime startDate;
@Schema(description = "노출 종료일")
private LocalDateTime endDate;
@Schema(description = "조회수", example = "0")
private Integer viewCount;
@Schema(description = "사용 여부", example = "Y")
private String useYn;
@Schema(description = "생성일시")
private LocalDateTime createDate;
@Schema(description = "생성 회원 PK", example = "1")
private Long createMember;
@Schema(description = "수정일시")
private LocalDateTime updateDate;
@Schema(description = "수정 회원 PK", example = "1")
private Long updateMember;
@Schema(description = "대상 캠퍼스 목록")
private List<NoticeCampusVo> campusList;
@Schema(description = "첨부파일 목록")
private List<NoticeFileVo> fileList;
}
@@ -0,0 +1,127 @@
package com.alist.api.admin.modules.tus;
import com.alist.api.admin.modules.tus.form.TusFileDeleteForm;
import com.alist.api.admin.modules.tus.form.TusFileHookForm;
import com.alist.api.admin.modules.tus.form.TusFileUploadCancelForm;
import com.alist.api.admin.modules.tus.form.TusFileUploadInitForm;
import com.alist.api.admin.modules.tus.form.TusFileUploadStatusForm;
import com.alist.api.admin.modules.tus.service.TusFileService;
import com.alist.api.admin.modules.tus.vo.TusFileDeleteVo;
import com.alist.api.admin.modules.tus.vo.TusFileListVo;
import com.alist.api.admin.modules.tus.vo.TusFileUploadCancelVo;
import com.alist.api.admin.modules.tus.vo.TusFileUploadInitVo;
import com.alist.api.admin.modules.tus.vo.TusFileUploadStatusVo;
import com.alist.api.core.common.response.ApiResponse;
import com.alist.api.core.common.response.ApiResponseCode;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "D-2. TUS 업로드", description = "관리자 TUS 기반 대용량 파일 업로드 API")
@RestController
@RequestMapping("/tus/file")
public class TusFileController {
private final TusFileService tusFileService;
public TusFileController(TusFileService tusFileService) {
this.tusFileService = tusFileService;
}
@Operation(summary = "파일 업로드 초기화", description = "파일 마스터와 상세를 등록하고 TUS 업로드 토큰을 발급합니다.")
@PostMapping("/upload/init")
public ResponseEntity<ApiResponse<TusFileUploadInitVo>> tusFileUploadInit(@Valid @RequestBody TusFileUploadInitForm tusFileUploadInitForm) {
TusFileUploadInitVo tusFileUploadInitVo = tusFileService.insertTusFileUploadInit(tusFileUploadInitForm);
if (tusFileUploadInitVo.getFileMasterIdx() == null) {
return ApiResponse.entity(tusFileUploadInitVo, ApiResponseCode.CODE_401);
}
return ApiResponse.entity(tusFileUploadInitVo, ApiResponseCode.CODE_2002, "파일 업로드");
}
@Operation(summary = "업로드 권한 검증", description = "tusd가 업로드 전용 Bearer 토큰과 TUS 메타데이터를 검증합니다.")
@GetMapping("/upload/auth")
public ResponseEntity<ApiResponse<String>> tusFileUploadAuth(@RequestHeader(value = "Authorization", required = false) String authorization, @RequestHeader(value = "x-File-Uuid", required = false) String fileUuid, @RequestHeader(value = "Upload-Metadata", required = false) String uploadMetadata, @RequestHeader(value = "Upload-Length", required = false) String uploadLength) {
int resultCode = tusFileService.selectTusFileUploadAuth(authorization, fileUuid, uploadMetadata, uploadLength);
if (resultCode == 401) {
return ApiResponse.entity("", ApiResponseCode.CODE_401);
}
if (resultCode == 403) {
return ApiResponse.entity("", ApiResponseCode.CODE_403);
}
return ApiResponse.entity("", ApiResponseCode.CODE_200);
}
@Operation(summary = "업로드 상태 조회", description = "Redis에 저장된 TUS 업로드 진행 상태를 조회합니다.")
@PostMapping("/upload/status")
public ResponseEntity<ApiResponse<TusFileUploadStatusVo>> tusFileUploadStatus(@Valid @RequestBody TusFileUploadStatusForm tusFileUploadStatusForm) {
TusFileUploadStatusVo tusFileUploadStatusVo = tusFileService.selectTusFileUploadStatus(tusFileUploadStatusForm);
if (tusFileUploadStatusVo == null) {
return ApiResponse.entity(new TusFileUploadStatusVo(), ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(tusFileUploadStatusVo, ApiResponseCode.CODE_2001, "업로드 상태");
}
@Operation(summary = "TUS Hook 수신", description = "tusd의 업로드 이벤트를 파일 상세, 마스터 집계, Redis 상태에 반영합니다.")
@PostMapping("/hook")
public ResponseEntity<ApiResponse<String>> tusFileHook(@RequestBody TusFileHookForm tusFileHookForm) {
tusFileService.updateTusFileHook(tusFileHookForm);
return ApiResponse.entity("", ApiResponseCode.CODE_200);
}
@Operation(summary = "업로드 취소", description = "진행 중인 파일을 CANCELED 상태로 변경합니다.")
@PostMapping("/upload/cancel")
public ResponseEntity<ApiResponse<TusFileUploadCancelVo>> tusFileUploadCancel(@Valid @RequestBody TusFileUploadCancelForm tusFileUploadCancelForm) {
TusFileUploadCancelVo tusFileUploadCancelVo = tusFileService.updateTusFileUploadCancel(tusFileUploadCancelForm);
if (tusFileUploadCancelVo.getResultCode() == 401) {
return ApiResponse.entity(tusFileUploadCancelVo, ApiResponseCode.CODE_401);
}
if (!tusFileUploadCancelVo.isCanceled()) {
return ApiResponse.entity(tusFileUploadCancelVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(tusFileUploadCancelVo, ApiResponseCode.CODE_2005, "업로드 취소");
}
@Operation(summary = "파일 목록 조회", description = "파일 마스터 기준으로 완료된 파일 목록을 조회합니다.")
@GetMapping("/list/{fileMasterIdx}")
public ResponseEntity<ApiResponse<TusFileListVo>> tusFileList(@PathVariable Long fileMasterIdx) {
TusFileListVo tusFileListVo = tusFileService.selectTusFileList(fileMasterIdx);
if (tusFileListVo.getResultCode() == 401) {
return ApiResponse.entity(tusFileListVo, ApiResponseCode.CODE_401);
}
return ApiResponse.entity(tusFileListVo, ApiResponseCode.CODE_2001, "파일");
}
@Operation(summary = "파일 보기", description = "완료된 파일을 inline으로 반환하고 조회 로그를 남깁니다.")
@GetMapping("/view/{fileUuid}")
public ResponseEntity<Resource> tusFileView(@PathVariable String fileUuid, HttpServletRequest request) {
return tusFileService.selectTusFileResource(fileUuid, "VIEW", request.getRemoteAddr(), request.getHeader("User-Agent"), request.getHeader("Referer"), true);
}
@Operation(summary = "파일 다운로드", description = "완료된 파일을 attachment로 반환하고 다운로드 로그를 남깁니다.")
@GetMapping("/download/{fileUuid}")
public ResponseEntity<Resource> tusFileDownload(@PathVariable String fileUuid, HttpServletRequest request) {
return tusFileService.selectTusFileResource(fileUuid, "DOWNLOAD", request.getRemoteAddr(), request.getHeader("User-Agent"), request.getHeader("Referer"), false);
}
@Operation(summary = "파일 삭제", description = "완료된 파일을 논리 삭제합니다.")
@PostMapping("/delete")
public ResponseEntity<ApiResponse<TusFileDeleteVo>> tusFileDelete(@Valid @RequestBody TusFileDeleteForm tusFileDeleteForm) {
TusFileDeleteVo tusFileDeleteVo = tusFileService.updateTusFileDelete(tusFileDeleteForm);
if (tusFileDeleteVo.getResultCode() == 401) {
return ApiResponse.entity(tusFileDeleteVo, ApiResponseCode.CODE_401);
}
if (!tusFileDeleteVo.isDeleted()) {
return ApiResponse.entity(tusFileDeleteVo, ApiResponseCode.CODE_2003);
}
return ApiResponse.entity(tusFileDeleteVo, ApiResponseCode.CODE_2005, "파일 삭제");
}
}
@@ -0,0 +1,15 @@
package com.alist.api.admin.modules.tus.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "TUS 파일 삭제 요청")
@Getter
@Setter
public class TusFileDeleteForm {
@Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "파일 UUID는 필수입니다.")
private String fileUuid;
}
@@ -0,0 +1,47 @@
package com.alist.api.admin.modules.tus.form;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.Map;
@Schema(description = "tusd Hook 요청")
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public class TusFileHookForm {
@Schema(description = "Hook 이벤트 유형", example = "post-finish")
@JsonProperty("Type")
private String type;
@JsonProperty("Event")
private Event event;
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Event {
@JsonProperty("Upload")
private Upload upload;
}
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Upload {
@JsonProperty("ID")
private String id;
@JsonProperty("Size")
private Long size;
@JsonProperty("Offset")
private Long offset;
@JsonProperty("MetaData")
private Map<String, String> metaData;
}
}
@@ -0,0 +1,15 @@
package com.alist.api.admin.modules.tus.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
@Schema(description = "TUS 업로드 취소 요청")
@Getter
@Setter
public class TusFileUploadCancelForm {
@Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "파일 UUID는 필수입니다.")
private String fileUuid;
}
@@ -0,0 +1,46 @@
package com.alist.api.admin.modules.tus.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Schema(description = "TUS 파일 업로드 초기화 요청")
@Getter
@Setter
public class TusFileUploadInitForm {
@Schema(description = "파일 업무 구분", example = "BOARD", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "파일 카테고리는 필수입니다.")
private String fileCategory;
@Schema(description = "논리 폴더 경로", example = "/board")
private String folderPath;
@Schema(description = "업로드 파일 목록", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "업로드 파일 목록은 필수입니다.")
@Valid
private List<Item> itemList;
@Schema(description = "TUS 업로드 파일 항목")
@Getter
@Setter
public static class Item {
@Schema(description = "원본 파일명", example = "sample.png", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "원본 파일명은 필수입니다.")
private String originName;
@Schema(description = "파일 크기(byte)", example = "102400", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "파일 크기는 필수입니다.")
@Positive(message = "파일 크기는 0보다 커야 합니다.")
private Long sizeBytes;
@Schema(description = "Content-Type", example = "image/png")
private String contentType;
}
}

Some files were not shown because too many files have changed in this diff Show More