관리자 관리 CRUD Sample 추가
This commit is contained in:
parent
e3c4fa132f
commit
2edb4c831f
|
|
@ -16,7 +16,10 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
// spring
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
|
|
||||||
// thymeleaf
|
// thymeleaf
|
||||||
|
|
@ -24,8 +27,11 @@ dependencies {
|
||||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
|
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
|
||||||
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.3.0'
|
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.3.0'
|
||||||
|
|
||||||
// jpa
|
//querydsl
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
implementation "com.querydsl:querydsl-jpa:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
|
||||||
|
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
|
||||||
|
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
|
||||||
|
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
|
||||||
|
|
||||||
// security
|
// security
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
package com.bpgroup.poc.admin.app.login;
|
package com.bpgroup.poc.admin.app.login;
|
||||||
|
|
||||||
import com.bpgroup.poc.admin.app.login.exception.AdministratorNotFoundException;
|
import com.bpgroup.poc.admin.app.login.exception.AdministratorNotFoundException;
|
||||||
|
import com.bpgroup.poc.admin.app.login.exception.DoNotHaveAnyMenuException;
|
||||||
import com.bpgroup.poc.admin.app.login.exception.InvalidPasswordException;
|
import com.bpgroup.poc.admin.app.login.exception.InvalidPasswordException;
|
||||||
import com.bpgroup.poc.admin.domain.admin.Administrator;
|
import com.bpgroup.poc.admin.domain.admin.entity.Administrator;
|
||||||
import com.bpgroup.poc.admin.domain.admin.AdministratorRepository;
|
import com.bpgroup.poc.admin.domain.admin.entity.AdministratorRepository;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
@ -22,7 +23,7 @@ public class LoginService {
|
||||||
private final AdministratorRepository loginRepository;
|
private final AdministratorRepository loginRepository;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
public LoginResult login(String loginId, String pwd) throws AdministratorNotFoundException, InvalidPasswordException {
|
public LoginResult login(String loginId, String pwd) throws AdministratorNotFoundException, InvalidPasswordException, DoNotHaveAnyMenuException {
|
||||||
|
|
||||||
Optional<Administrator> administrator = loginRepository.findByLoginId(loginId);
|
Optional<Administrator> administrator = loginRepository.findByLoginId(loginId);
|
||||||
if (administrator.isEmpty()) {
|
if (administrator.isEmpty()) {
|
||||||
|
|
@ -42,7 +43,8 @@ public class LoginService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LinkedHashSet<LoginResult.MenuInfo> getMenus(Administrator administrator) {
|
private static LinkedHashSet<LoginResult.MenuInfo> getMenus(Administrator administrator) throws DoNotHaveAnyMenuException {
|
||||||
|
try {
|
||||||
return administrator.getAdministratorRole().getRole().getRoleMenus().stream()
|
return administrator.getAdministratorRole().getRole().getRoleMenus().stream()
|
||||||
.map(roleMenu -> LoginResult.MenuInfo.of(
|
.map(roleMenu -> LoginResult.MenuInfo.of(
|
||||||
roleMenu.getMenu().getMenuGroup().getUri(),
|
roleMenu.getMenu().getMenuGroup().getUri(),
|
||||||
|
|
@ -58,6 +60,9 @@ public class LoginService {
|
||||||
.thenComparingInt(LoginResult.MenuInfo::getMenuSortOrder)
|
.thenComparingInt(LoginResult.MenuInfo::getMenuSortOrder)
|
||||||
)
|
)
|
||||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
throw new DoNotHaveAnyMenuException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.bpgroup.poc.admin.app.login.exception;
|
||||||
|
|
||||||
|
public class DoNotHaveAnyMenuException extends Exception {
|
||||||
|
public DoNotHaveAnyMenuException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.bpgroup.poc.admin.config;
|
||||||
|
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory;
|
||||||
|
import jakarta.persistence.EntityManager;
|
||||||
|
import jakarta.persistence.PersistenceContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class QuerydslConfig {
|
||||||
|
|
||||||
|
@PersistenceContext
|
||||||
|
private EntityManager entityManager;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JPAQueryFactory jpaQueryFactory() {
|
||||||
|
return new JPAQueryFactory(entityManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.bpgroup.poc.admin.domain;
|
||||||
|
|
||||||
|
public class DomainException extends RuntimeException {
|
||||||
|
public DomainException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -39,4 +39,10 @@ public class Administrator extends BaseEntity {
|
||||||
return administrator;
|
return administrator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void update(Administrator administrator) {
|
||||||
|
this.password = administrator.password;
|
||||||
|
this.email = administrator.email;
|
||||||
|
this.name = administrator.name;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.bpgroup.poc.admin.domain.admin.service;
|
||||||
|
|
||||||
|
import com.bpgroup.poc.admin.domain.admin.entity.Administrator;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ToString
|
||||||
|
public class AdministratorCreateCommand {
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String loginId;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public static AdministratorCreateCommand of(String loginId, String password, String email, String name) {
|
||||||
|
AdministratorCreateCommand command = new AdministratorCreateCommand();
|
||||||
|
command.loginId = loginId;
|
||||||
|
command.password = password;
|
||||||
|
command.email = email;
|
||||||
|
command.name = name;
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Administrator toEntity() {
|
||||||
|
return Administrator.builder()
|
||||||
|
.loginId(loginId)
|
||||||
|
.password(password)
|
||||||
|
.email(email)
|
||||||
|
.name(name)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.bpgroup.poc.admin.domain.admin.service;
|
||||||
|
|
||||||
|
import com.bpgroup.poc.admin.domain.admin.entity.Administrator;
|
||||||
|
import com.bpgroup.poc.admin.domain.admin.entity.AdministratorRepository;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Validated
|
||||||
|
@Transactional
|
||||||
|
public class AdministratorService {
|
||||||
|
|
||||||
|
private final AdministratorRepository administratorRepository;
|
||||||
|
|
||||||
|
public Long create(
|
||||||
|
@NotNull @Valid AdministratorCreateCommand command
|
||||||
|
) {
|
||||||
|
// 대소문자 구별이 필요한 경우 인증 부분과 유효성 검사 부분 모두 변경이 필요하다.
|
||||||
|
Optional<Administrator> administrator = administratorRepository.findByLoginId(command.getLoginId());
|
||||||
|
if (administrator.isPresent()) {
|
||||||
|
throw new DuplicationAdministratorException(command.getLoginId());
|
||||||
|
}
|
||||||
|
|
||||||
|
Administrator createAdministrator = administratorRepository.save(command.toEntity());
|
||||||
|
return createAdministrator.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(
|
||||||
|
@NotNull @Valid AdministratorUpdateCommand command
|
||||||
|
) {
|
||||||
|
Optional<Administrator> administrator = administratorRepository.findById(command.getId());
|
||||||
|
if (administrator.isEmpty()) {
|
||||||
|
throw new NotFoundAdministratorException();
|
||||||
|
}
|
||||||
|
|
||||||
|
administrator.get().update(command.toEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(
|
||||||
|
@NotNull Long id
|
||||||
|
) {
|
||||||
|
administratorRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.bpgroup.poc.admin.domain.admin.service;
|
||||||
|
|
||||||
|
import com.bpgroup.poc.admin.domain.admin.entity.Administrator;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ToString
|
||||||
|
public class AdministratorUpdateCommand {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public static AdministratorUpdateCommand of(Long id, String password, String email, String name) {
|
||||||
|
AdministratorUpdateCommand command = new AdministratorUpdateCommand();
|
||||||
|
command.id = id;
|
||||||
|
command.password = password;
|
||||||
|
command.email = email;
|
||||||
|
command.name = name;
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Administrator toEntity() {
|
||||||
|
return Administrator.builder()
|
||||||
|
.password(password)
|
||||||
|
.email(email)
|
||||||
|
.name(name)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.bpgroup.poc.admin.domain.admin.service;
|
||||||
|
|
||||||
|
public class DuplicationAdministratorException extends RuntimeException {
|
||||||
|
public DuplicationAdministratorException(String loginId) {
|
||||||
|
super("이미 존재하는 아이디 입니다. : " + loginId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.bpgroup.poc.admin.domain.admin.service;
|
||||||
|
|
||||||
|
import com.bpgroup.poc.admin.domain.DomainException;
|
||||||
|
|
||||||
|
public class NotFoundAdministratorException extends DomainException {
|
||||||
|
public NotFoundAdministratorException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,12 +37,11 @@ public class SecurityConfig {
|
||||||
}).addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class); // 로그인이 완료된 후 CSRF Filter 를 실행
|
}).addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class); // 로그인이 완료된 후 CSRF Filter 를 실행
|
||||||
|
|
||||||
// 인증 설정
|
// 인증 설정
|
||||||
http.authorizeHttpRequests(c -> {
|
http.authorizeHttpRequests(c -> c
|
||||||
c.requestMatchers("/css/**", "/images/**", "/js/**").permitAll()
|
.requestMatchers("/css/**", "/images/**", "/js/**").permitAll()
|
||||||
.requestMatchers("/common/modal/**").permitAll()
|
.requestMatchers("/common/modal/**").permitAll()
|
||||||
.requestMatchers(LOGIN_PATH, LOGOUT_PATH, ERROR_PATH).permitAll()
|
.requestMatchers(LOGIN_PATH, LOGOUT_PATH, ERROR_PATH).permitAll()
|
||||||
.anyRequest().authenticated();
|
.anyRequest().authenticated());
|
||||||
});
|
|
||||||
|
|
||||||
http.formLogin(c -> c
|
http.formLogin(c -> c
|
||||||
.loginPage(LOGIN_PATH)
|
.loginPage(LOGIN_PATH)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.bpgroup.poc.admin.security.authentication;
|
package com.bpgroup.poc.admin.security.authentication;
|
||||||
|
|
||||||
import com.bpgroup.poc.admin.app.login.exception.AdministratorNotFoundException;
|
import com.bpgroup.poc.admin.app.login.exception.AdministratorNotFoundException;
|
||||||
|
import com.bpgroup.poc.admin.app.login.exception.DoNotHaveAnyMenuException;
|
||||||
import com.bpgroup.poc.admin.app.login.exception.InvalidPasswordException;
|
import com.bpgroup.poc.admin.app.login.exception.InvalidPasswordException;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
|
@ -8,11 +9,14 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
public enum AuthenticationFailReason {
|
public enum AuthenticationFailReason {
|
||||||
WRONG_LOGIN_ID,
|
WRONG_LOGIN_ID,
|
||||||
WRONG_PASSWORD,
|
WRONG_PASSWORD,
|
||||||
|
HAVE_NO_MENU,
|
||||||
INTERNAL_ERROR;
|
INTERNAL_ERROR;
|
||||||
|
|
||||||
public static AuthenticationFailReason from(Exception e) {
|
public static AuthenticationFailReason from(Exception e) {
|
||||||
if (e instanceof AdministratorNotFoundException || e instanceof InvalidPasswordException) {
|
if (e instanceof AdministratorNotFoundException || e instanceof InvalidPasswordException) {
|
||||||
return WRONG_LOGIN_ID;
|
return WRONG_LOGIN_ID;
|
||||||
|
} else if (e instanceof DoNotHaveAnyMenuException) {
|
||||||
|
return HAVE_NO_MENU;
|
||||||
} else {
|
} else {
|
||||||
return INTERNAL_ERROR;
|
return INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.bpgroup.poc.admin.web.common;
|
||||||
|
|
||||||
|
public class CommonResponse {
|
||||||
|
|
||||||
|
protected String resultCode;
|
||||||
|
protected String resultMessage;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -26,7 +26,8 @@ public class LoginController {
|
||||||
private String getMessage(AuthenticationFailReason error) {
|
private String getMessage(AuthenticationFailReason error) {
|
||||||
return switch (error) {
|
return switch (error) {
|
||||||
case WRONG_LOGIN_ID, WRONG_PASSWORD -> "아이디 또는 비밀번호가 일치하지 않습니다.";
|
case WRONG_LOGIN_ID, WRONG_PASSWORD -> "아이디 또는 비밀번호가 일치하지 않습니다.";
|
||||||
case INTERNAL_ERROR -> "서버에 오류가 발생했습니다.";
|
case HAVE_NO_MENU -> "등록된 메뉴가 없습니다.\n 메뉴 등록 후 이용해주세요.";
|
||||||
|
default -> "서버에 오류가 발생했습니다.";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.bpgroup.poc.admin.web.main.admin.management;
|
||||||
|
|
||||||
|
import com.bpgroup.poc.admin.web.main.admin.management.reqres.AdministratorFind;
|
||||||
|
import com.querydsl.core.types.Projections;
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.bpgroup.poc.admin.domain.admin.entity.QAdministrator.administrator;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AdministratorManagementQueryRepository {
|
||||||
|
|
||||||
|
private final JPAQueryFactory queryFactory;
|
||||||
|
|
||||||
|
public List<AdministratorFind.Response> findAll() {
|
||||||
|
return queryFactory.select(Projections.fields(AdministratorFind.Response.class,
|
||||||
|
administrator.id,
|
||||||
|
administrator.loginId,
|
||||||
|
administrator.email,
|
||||||
|
administrator.name))
|
||||||
|
.from(administrator)
|
||||||
|
.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AdministratorFind.Response findByLoginId(String loginId) {
|
||||||
|
return queryFactory.select(Projections.fields(AdministratorFind.Response.class,
|
||||||
|
administrator.id,
|
||||||
|
administrator.loginId,
|
||||||
|
administrator.email,
|
||||||
|
administrator.name))
|
||||||
|
.from(administrator)
|
||||||
|
.where(administrator.loginId.eq(loginId))
|
||||||
|
.fetchOne();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
package com.bpgroup.poc.admin.web.main.admin.management;
|
||||||
|
|
||||||
|
import com.bpgroup.poc.admin.web.main.admin.management.reqres.AdministratorCreate;
|
||||||
|
import com.bpgroup.poc.admin.web.main.admin.management.reqres.AdministratorDelete;
|
||||||
|
import com.bpgroup.poc.admin.web.main.admin.management.reqres.AdministratorFind;
|
||||||
|
import com.bpgroup.poc.admin.web.main.admin.management.reqres.AdministratorUpdate;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.validation.BindingResult;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/admin/management")
|
||||||
|
public class AdministratorManagementRestController {
|
||||||
|
|
||||||
|
private final AdministratorManagementWebService administratorManagementWebService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 전체 조회
|
||||||
|
* @return 응답
|
||||||
|
*/
|
||||||
|
@GetMapping("/list")
|
||||||
|
public ResponseEntity<?> getAdministrators() {
|
||||||
|
List<AdministratorFind.Response> response = administratorManagementWebService.findAll();
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 조회
|
||||||
|
* @param loginId 관리자 ID
|
||||||
|
* @return 응답
|
||||||
|
*/
|
||||||
|
@GetMapping("/{loginId}")
|
||||||
|
public ResponseEntity<?> getAdministrator(@PathVariable @NotBlank String loginId) {
|
||||||
|
AdministratorFind.Response response = administratorManagementWebService.find(loginId);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 관리자 등록
|
||||||
|
* @param request 요청
|
||||||
|
* @param bindingResult Validation 결과
|
||||||
|
* @return 응답
|
||||||
|
*/
|
||||||
|
@PostMapping("/create")
|
||||||
|
public ResponseEntity<?> createAdministrator(
|
||||||
|
@RequestBody @Validated AdministratorCreate.Request request,
|
||||||
|
BindingResult bindingResult
|
||||||
|
) {
|
||||||
|
AdministratorCreate.Response response = administratorManagementWebService.create(request);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/update")
|
||||||
|
public ResponseEntity<?> updateAdministrator(
|
||||||
|
@RequestBody @Validated AdministratorUpdate.Request request,
|
||||||
|
BindingResult bindingResult
|
||||||
|
) {
|
||||||
|
administratorManagementWebService.update(request);
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 관리자 삭제
|
||||||
|
* @param request 요청
|
||||||
|
* @param bindingResult Validation 결과
|
||||||
|
* @return 응답
|
||||||
|
*/
|
||||||
|
@PostMapping("/delete")
|
||||||
|
public ResponseEntity<?> deleteAdministrator(
|
||||||
|
@RequestBody @Validated AdministratorDelete.Request request,
|
||||||
|
BindingResult bindingResult
|
||||||
|
) {
|
||||||
|
administratorManagementWebService.delete(request);
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.bpgroup.poc.admin.web.main.admin.management;
|
||||||
|
|
||||||
|
import com.bpgroup.poc.admin.domain.admin.service.AdministratorCreateCommand;
|
||||||
|
import com.bpgroup.poc.admin.domain.admin.service.AdministratorService;
|
||||||
|
import com.bpgroup.poc.admin.domain.admin.service.AdministratorUpdateCommand;
|
||||||
|
import com.bpgroup.poc.admin.web.main.admin.management.reqres.AdministratorCreate;
|
||||||
|
import com.bpgroup.poc.admin.web.main.admin.management.reqres.AdministratorDelete;
|
||||||
|
import com.bpgroup.poc.admin.web.main.admin.management.reqres.AdministratorFind;
|
||||||
|
import com.bpgroup.poc.admin.web.main.admin.management.reqres.AdministratorUpdate;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Transactional
|
||||||
|
public class AdministratorManagementWebService {
|
||||||
|
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
private final AdministratorService administratorService;
|
||||||
|
private final AdministratorManagementQueryRepository administratorManagementQueryRepository;
|
||||||
|
|
||||||
|
public AdministratorCreate.Response create(AdministratorCreate.Request request) {
|
||||||
|
Long id = administratorService.create(AdministratorCreateCommand.of(
|
||||||
|
request.getLoginId(),
|
||||||
|
passwordEncoder.encode(request.getPassword()),
|
||||||
|
request.getEmail(),
|
||||||
|
request.getName()
|
||||||
|
));
|
||||||
|
|
||||||
|
return AdministratorCreate.Response.builder()
|
||||||
|
.id(id)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AdministratorFind.Response find(@NotBlank String loginId) {
|
||||||
|
return administratorManagementQueryRepository.findByLoginId(loginId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(AdministratorDelete.Request request) {
|
||||||
|
administratorService.delete(request.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(AdministratorUpdate.Request request) {
|
||||||
|
administratorService.update(AdministratorUpdateCommand.of(
|
||||||
|
request.getId(),
|
||||||
|
passwordEncoder.encode(request.getPassword()),
|
||||||
|
request.getEmail(),
|
||||||
|
request.getName()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AdministratorFind.Response> findAll() {
|
||||||
|
return administratorManagementQueryRepository.findAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.bpgroup.poc.admin.web.main.admin.management.reqres;
|
||||||
|
|
||||||
|
import com.bpgroup.poc.admin.web.common.CommonResponse;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
public class AdministratorCreate {
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Request {
|
||||||
|
@NotBlank
|
||||||
|
private String loginId;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ToString
|
||||||
|
public static class Response extends CommonResponse {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
public static Response success(Long id) {
|
||||||
|
Response response = new Response();
|
||||||
|
response.resultCode = "0000";
|
||||||
|
response.resultMessage = "Success";
|
||||||
|
response.id = id;
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
public static Response fail() {
|
||||||
|
Response response = new Response();
|
||||||
|
response.resultCode = "9999";
|
||||||
|
response.resultMessage = "Fail";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.bpgroup.poc.admin.web.main.admin.management.reqres;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
public class AdministratorDelete {
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Request {
|
||||||
|
@NotNull
|
||||||
|
private Long id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.bpgroup.poc.admin.web.main.admin.management.reqres;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
public class AdministratorFind {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@ToString
|
||||||
|
public static class Response {
|
||||||
|
private Long id;
|
||||||
|
private String loginId;
|
||||||
|
private String email;
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.bpgroup.poc.admin.web.main.admin.management.reqres;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
public class AdministratorUpdate {
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Request {
|
||||||
|
@NotNull
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -17,39 +17,17 @@ VALUES ('/admin', '관리자 관리', 2, CURDATE(), CURDATE()),
|
||||||
|
|
||||||
INSERT INTO `menu` (`uri`, `name`, `sort_order`, `menu_group_id`, `create_date`, `update_date`)
|
INSERT INTO `menu` (`uri`, `name`, `sort_order`, `menu_group_id`, `create_date`, `update_date`)
|
||||||
VALUES ('/admin/management', '관리자 관리', 1, 1, CURDATE(), CURDATE()),
|
VALUES ('/admin/management', '관리자 관리', 1, 1, CURDATE(), CURDATE()),
|
||||||
('/admin/role', '권한 관리', 2, 1, CURDATE(), CURDATE()),
|
('/admin/role', '권한 관리', 2, 1, CURDATE(), CURDATE())
|
||||||
('/admin/temp01', '관리자 임시 1', 4, 1, CURDATE(), CURDATE()),
|
|
||||||
('/admin/temp02', '관리자 임시 2', 3, 1, CURDATE(), CURDATE())
|
|
||||||
;
|
;
|
||||||
|
|
||||||
INSERT INTO `menu` (`uri`, `name`, `sort_order`, `menu_group_id`, `create_date`, `update_date`)
|
INSERT INTO `menu` (`uri`, `name`, `sort_order`, `menu_group_id`, `create_date`, `update_date`)
|
||||||
VALUES ('/temp/temp01', '임시 01', 4, 3, CURDATE(), CURDATE()),
|
VALUES ('/settings/code', '코드 관리', 1, 2, CURDATE(), CURDATE())
|
||||||
('/temp/temp02', '임시 02', 2, 3, CURDATE(), CURDATE()),
|
|
||||||
('/temp/temp03', '임시 03', 1, 3, CURDATE(), CURDATE()),
|
|
||||||
('/temp/temp04', '임시 04', 3, 3, CURDATE(), CURDATE())
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
INSERT INTO `menu` (`uri`, `name`, `sort_order`, `menu_group_id`, `create_date`, `update_date`)
|
|
||||||
VALUES ('/settings/code', '코드 관리', 1, 2, CURDATE(), CURDATE()),
|
|
||||||
('/settings/component', '컴포넌트', 1, 2, CURDATE(), CURDATE()),
|
|
||||||
('/settings/temp01', '세팅 임시 1', 4, 2, CURDATE(), CURDATE()),
|
|
||||||
('/settings/temp02', '세팅 임시 2', 3, 2, CURDATE(), CURDATE())
|
|
||||||
;
|
;
|
||||||
|
|
||||||
INSERT INTO `role_menu` (`role_id`, `menu_id`, `create_date`, `update_date`)
|
INSERT INTO `role_menu` (`role_id`, `menu_id`, `create_date`, `update_date`)
|
||||||
VALUES ('1', '1', CURDATE(), CURDATE()),
|
VALUES ('1', '1', CURDATE(), CURDATE()),
|
||||||
('1', '2', CURDATE(), CURDATE()),
|
('1', '2', CURDATE(), CURDATE()),
|
||||||
('1', '3', CURDATE(), CURDATE()),
|
('1', '3', CURDATE(), CURDATE())
|
||||||
('1', '4', CURDATE(), CURDATE()),
|
|
||||||
('1', '5', CURDATE(), CURDATE()),
|
|
||||||
('1', '6', CURDATE(), CURDATE()),
|
|
||||||
('1', '7', CURDATE(), CURDATE()),
|
|
||||||
('1', '8', CURDATE(), CURDATE()),
|
|
||||||
('1', '9', CURDATE(), CURDATE()),
|
|
||||||
('1', '10', CURDATE(), CURDATE()),
|
|
||||||
('1', '11', CURDATE(), CURDATE()),
|
|
||||||
('1', '12', CURDATE(), CURDATE())
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,31 @@
|
||||||
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
|
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
|
||||||
<meta name="_csrf" th:content="${_csrf.token}"/>
|
<meta name="_csrf" th:content="${_csrf.token}"/>
|
||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
// 모든 AJAX 요청에 자동으로 CSRF 토큰이 포함
|
// CSRF 토큰
|
||||||
|
// fetch ajax 사용 시 헤더에 추가 필요
|
||||||
const csrfToken = $('meta[name="_csrf"]').attr('content');
|
const csrfToken = $('meta[name="_csrf"]').attr('content');
|
||||||
const csrfHeader = $('meta[name="_csrf_header"]').attr('content');
|
const csrfHeader = $('meta[name="_csrf_header"]').attr('content');
|
||||||
|
|
||||||
$.ajaxSetup({
|
function fetchPost(requestUri, body) {
|
||||||
beforeSend: function (xhr) {
|
return fetch(requestUri, {
|
||||||
xhr.setRequestHeader(csrfHeader, csrfToken);
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
[csrfHeader]: csrfToken,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchGet(requestUri) {
|
||||||
|
return fetch(requestUri, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
[csrfHeader]: csrfToken,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</th:block>
|
</th:block>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,171 @@
|
||||||
layout:decorate="~{main/admin/management/root.html}">
|
layout:decorate="~{main/admin/management/root.html}">
|
||||||
<body>
|
<body>
|
||||||
<th:block layout:fragment="innerContents">
|
<th:block layout:fragment="innerContents">
|
||||||
|
<div class="table_wrap mt-20">
|
||||||
|
<h4>관리자 등록</h4>
|
||||||
|
<div class="tb_wrapper">
|
||||||
|
<label for="iptCreateLoginId">아이디: </label><input type="text" id="iptCreateLoginId" name="loginId">
|
||||||
|
<label for="iptCreatePassword">비밀번호: </label><input type="password" id="iptCreatePassword" name="password">
|
||||||
|
<label for="iptCreateEmail">이메일: </label><input type="text" id="iptCreateEmail" name="email">
|
||||||
|
<label for="iptCreateNAme">이름: </label><input type="text" id="iptCreateNAme" name="name">
|
||||||
|
<button type="button" id="btnCreate" class="btn_confirm" data-bs-dismiss="modal">등록</button>
|
||||||
|
<button type="button" id="btnCheckDuplication" class="btn_confirm" data-bs-dismiss="modal">아이디 중복체크</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4>관리자 조회</h4>
|
||||||
|
<div class="tb_wrapper">
|
||||||
|
<label for="selAdministrator">관리자: </label>
|
||||||
|
<select id="selAdministrator" class="table_select_box">
|
||||||
|
<option value="">선택</option>
|
||||||
|
</select>
|
||||||
|
<button type="button" id="btnFindAll" class="btn_confirm" data-bs-dismiss="modal">조회</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4>관리자 수정</h4>
|
||||||
|
<div class="tb_wrapper">
|
||||||
|
<input type="hidden" id="iptUpdateId" name="id">
|
||||||
|
<label for="iptUpdateLoginId">아이디: </label><input type="text" id="iptUpdateLoginId" name="loginId" readonly>
|
||||||
|
<label for="iptUpdatePassword">비밀번호: </label ><input type="password" id="iptUpdatePassword" name="password">
|
||||||
|
<label for="iptUpdateEmail">이메일: </label><input type="text" id="iptUpdateEmail" name="email">
|
||||||
|
<label for="iptUpdateName">이름: </label><input type="text" id="iptUpdateName" name="name">
|
||||||
|
<button type="button" id="btnUpdate" class="btn_confirm" data-bs-dismiss="modal">수정</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4>관리자 삭제</h4>
|
||||||
|
<div class="tb_wrapper">
|
||||||
|
<input type="hidden" id="iptDeleteId" name="id">
|
||||||
|
<label for="iptUpdateLoginId">아이디: </label><input type="text" id="iptDeleteLoginId" name="loginId" readonly>
|
||||||
|
<button type="button" id="btnDelete" class="btn_confirm" data-bs-dismiss="modal">삭제</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</th:block>
|
</th:block>
|
||||||
|
|
||||||
<th:block layout:fragment="innerScript">
|
<th:block layout:fragment="innerScript">
|
||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
|
function clearInput() {
|
||||||
|
document.getElementById('iptCreateLoginId').value = '';
|
||||||
|
document.getElementById('iptCreatePassword').value = '';
|
||||||
|
document.getElementById('iptCreateEmail').value = '';
|
||||||
|
document.getElementById('iptCreateNAme').value = '';
|
||||||
|
document.getElementById('iptUpdateId').value = '';
|
||||||
|
document.getElementById('iptUpdateLoginId').value = '';
|
||||||
|
document.getElementById('iptUpdatePassword').value = '';
|
||||||
|
document.getElementById('iptUpdateEmail').value = '';
|
||||||
|
document.getElementById('iptUpdateName').value = '';
|
||||||
|
document.getElementById('iptDeleteId').value = '';
|
||||||
|
document.getElementById('iptDeleteLoginId').value = '';
|
||||||
|
document.getElementById('selAdministrator').innerHTML = '<option value="">선택</option>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 아이디 중복 체크
|
||||||
|
*/
|
||||||
|
document.getElementById('btnCheckDuplication').addEventListener(('click'), function () {
|
||||||
|
const loginId = document.getElementById('iptCreateLoginId').value;
|
||||||
|
const requestUri = /*[[@{/admin/management}]]*/ '';
|
||||||
|
fetchGet(requestUri.concat('/', loginId))
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.text().then(text => text ? JSON.parse(text) : null);
|
||||||
|
} else {
|
||||||
|
PageHelper.showErrorModal('데이터 조회에 실패했습니다.');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
if (!data) {
|
||||||
|
PageHelper.showAlertModal({title: '아이디 중복', message: '사용 가능한 아이디입니다.'});
|
||||||
|
} else {
|
||||||
|
PageHelper.showAlertModal({title: '아이디 중복', message: '사용할 수 없는 아이디입니다.'});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 관리자 등록
|
||||||
|
*/
|
||||||
|
document.getElementById('btnCreate').addEventListener(('click'), function () {
|
||||||
|
const requestUri = /*[[@{/admin/management/create}]]*/ '';
|
||||||
|
fetchPost(requestUri, {
|
||||||
|
loginId: document.getElementById('iptCreateLoginId').value,
|
||||||
|
password: document.getElementById('iptCreatePassword').value,
|
||||||
|
email: document.getElementById('iptCreateEmail').value,
|
||||||
|
name: document.getElementById('iptCreateEmail').value
|
||||||
|
}).then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
PageHelper.showAlertModal({title: '등록 완료', message: '등록이 완료되었습니다.'});
|
||||||
|
|
||||||
|
clearInput();
|
||||||
|
} else {
|
||||||
|
PageHelper.showErrorModal('등록에 실패했습니다.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 전체 조회
|
||||||
|
*/
|
||||||
|
document.getElementById('btnFindAll').addEventListener(('click'), function () {
|
||||||
|
const requestUri = /*[[@{/admin/management/list}]]*/ '';
|
||||||
|
fetchGet(requestUri)
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json();
|
||||||
|
} else {
|
||||||
|
PageHelper.showErrorModal('데이터 조회에 실패했습니다.');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
const selAdministrator = document.getElementById('selAdministrator');
|
||||||
|
selAdministrator.innerHTML = '<option value="">선택</option>';
|
||||||
|
data.forEach(item => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = item.id;
|
||||||
|
option.text = item.loginId;
|
||||||
|
selAdministrator.appendChild(option);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('selAdministrator').addEventListener(('change'), function () {
|
||||||
|
document.getElementById('iptUpdateId').value = this.value;
|
||||||
|
document.getElementById('iptUpdateLoginId').value = this.options[this.selectedIndex].text;
|
||||||
|
|
||||||
|
document.getElementById('iptDeleteId').value = this.value;
|
||||||
|
document.getElementById('iptDeleteLoginId').value = this.options[this.selectedIndex].text;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('btnUpdate').addEventListener(('click'), function () {
|
||||||
|
const requestUri = /*[[@{/admin/management/update}]]*/ '';
|
||||||
|
fetchPost(requestUri, {
|
||||||
|
id: document.getElementById('iptUpdateId').value,
|
||||||
|
password: document.getElementById('iptUpdatePassword').value,
|
||||||
|
email: document.getElementById('iptUpdateEmail').value,
|
||||||
|
name: document.getElementById('iptUpdateEmail').value
|
||||||
|
}).then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
PageHelper.showAlertModal({title: '수정 완료', message: '수정이 완료되었습니다.'});
|
||||||
|
} else {
|
||||||
|
PageHelper.showErrorModal('수정에 실패했습니다.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('btnDelete').addEventListener(('click'), function () {
|
||||||
|
const requestUri = /*[[@{/admin/management/delete}]]*/ '';
|
||||||
|
fetchPost(requestUri, {
|
||||||
|
id: document.getElementById('iptDeleteId').value
|
||||||
|
}).then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
PageHelper.showAlertModal({title: '삭제 완료', message: '삭제가 완료되었습니다.'});
|
||||||
|
|
||||||
|
clearInput();
|
||||||
|
} else {
|
||||||
|
PageHelper.showErrorModal('삭제에 실패했습니다.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</th:block>
|
</th:block>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue