Initial commit
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
= My Menu 관리
|
||||
|
||||
== My Menu 관리
|
||||
사용자가 자주 사용하는 메뉴를 등록하여 사용자 임의로 만들어서 빠른 이동을 목적으로 하는 Menu 목록.
|
||||
|
||||
TopMenu - My Menu 에 구현되어 있다.
|
||||
|
||||
image::MyMenu.png[]
|
||||
|
||||
=== 기능별 설명
|
||||
|
||||
image::MyMenu_add.png[]
|
||||
메뉴의 BreadCrumb 영역 내 메뉴명 우측 별 모양을 클릭하여 MyMenu에 등록 또는 해제
|
||||
|
||||
|
||||
NOTE: SideMenu(Right Side) 이용 시 MainOffsider.vue 내 'QuickMenu' 에 구현되어 있다.
|
||||
@@ -0,0 +1,13 @@
|
||||
= Sitemap
|
||||
|
||||
== 개요
|
||||
로그인한 사용자의 권한에 맞는 전체메뉴를 볼 수 있다.
|
||||
|
||||
TopMenu - 사이트맵 부분에 구현되어 있다.
|
||||
|
||||
== 화면
|
||||
권한에 따른 전체 메뉴 목록을 나열한 화면으로 메뉴 이동 가능
|
||||
|
||||
image::siteMap.png[]
|
||||
|
||||
NOTE: SideMenu(Right Side) 이용 시 MainOffsider.vue 내 'sitemap' 부분에 구현되어 있다.
|
||||
@@ -0,0 +1,129 @@
|
||||
= 권한 관리
|
||||
|
||||
== 권한 체크 로직
|
||||
|
||||
권한 체크는 AuthorizationInterceptor에서 사용자가 가지고 있는 메뉴의 권한 목록을 기반으로 URL Base 로 체크가 되고 있다.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
-- 생략 --
|
||||
String httpMethod = request.getMethod();
|
||||
String decodedUrl = WebUtil.getDecodedRequestUrl(request, request.getRequestURI().substring(request.getContextPath().length()));
|
||||
|
||||
String queryString = "";
|
||||
if (decodedUrl.indexOf('?') > -1) {
|
||||
String[] uri = decodedUrl.split("\\?");
|
||||
decodedUrl = uri[0];
|
||||
queryString = uri[1];
|
||||
}
|
||||
|
||||
for (Api api : authMenuList) {
|
||||
if (api.getHttpMethod().equals(httpMethod) && checkUriMatch(api.getApiPath(), api.getApiParameters(), decodedUrl, queryString)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
throw new AuthorizationException("API 권한 없음");
|
||||
}
|
||||
----
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public boolean checkUriMatch(String apiPath, String apiParameters, String decodedUrl, String queryString){
|
||||
Map<String, String> requiredParamMap = new HashMap<>();
|
||||
if( StringUtils.isNotEmpty(apiParameters)){
|
||||
requiredParamMap = Splitter.on('&').trimResults().withKeyValueSeparator('=').split(apiParameters);
|
||||
}
|
||||
|
||||
UriTemplate uriTemplate = new UriTemplate(apiPath);
|
||||
if ( uriTemplate.matches(decodedUrl) ){
|
||||
if( requiredParamMap.size() == 0 ) return true;
|
||||
for (Map.Entry<String, String> entry : requiredParamMap.entrySet()) {
|
||||
log.debug("Required Param : " + entry.getKey() + " Value : " + entry.getValue());
|
||||
Map<String, String> requestParamMap = getRequestParam(queryString);
|
||||
if( !requestParamMap.containsKey(entry.getKey()) || !(entry.getValue()).equals(requestParamMap.get(entry.getKey()))){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
== 권한별 메뉴 가져오기
|
||||
|
||||
SPA(Single Page Application) 방식으로 최초 메인화면 진입시에 사용자의 권한에 맞는 메뉴 목록을 조회하여 보여준다. +
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@RestController
|
||||
@RequestMapping("/auth")
|
||||
public class MenuController {
|
||||
--생략--
|
||||
@Operation(summary = "사용자의 권한이 있는 메뉴 조회")
|
||||
@GetMapping("/menus-user-auth")
|
||||
public List<Menu> getMenuListByAuth() {
|
||||
return menuService.getMenuListByAuth(Account.currentUser().getUserId());
|
||||
}
|
||||
--생략--
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
일반 사용자와 시스템 어드민 사용자의 권한 메뉴 조회 서비스가 분리 되어 있다.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Service
|
||||
public class MenuServiceImpl implements MenuService {
|
||||
--생략--
|
||||
@Override
|
||||
public List<Menu> getMenuListByAuth(String userId) {
|
||||
if (Account.currentUser().isSystemAdminUser()) return this.getMenuListByAdminAuth();
|
||||
else return this.getMenuListByUserAuth(userId);
|
||||
}
|
||||
--생략--
|
||||
}
|
||||
----
|
||||
|
||||
접속중인 상태에서 권한이 변경될 경우 화면 재진입(다시 로그인 또는 브라우저 새로고침)을 통해서 변경된 권한의 메뉴 목록을 다시 로드할 수 있다.
|
||||
|
||||
== 권한에 따른 UI Control
|
||||
|
||||
SDL에서는 역할관리에서 정의해놓은 역할별로 4개의 권한을 지정할 수 있다. +
|
||||
최초 화면 진입시 조회한 사용자가 접근 가능한 메뉴 목록에 해당 메뉴에 대한 권한 목록을 함께 가지고 있으며 화면에서 버튼에 directive로 지정함으로써 show/hide 처리를 자동으로 처리한다.
|
||||
|
||||
역할관리를 통한 메뉴별 권한 등록
|
||||
|
||||
image::front_01_03.png[]
|
||||
vue 내에서 authorization directive를 통해 버튼 별 권한을 정의한다.(배열로 정의할 경우 여러개 중에 하나만 매칭되도 show처리 됨)
|
||||
|
||||
* 여러개의 권한을 둘 경우
|
||||
|
||||
[source, html]
|
||||
----
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
v-authorization="['UPDATE', 'EXECUTE']"
|
||||
@click="onApprWrite"
|
||||
>
|
||||
{{ $t('sdl.approval.label.APPROVE') }}
|
||||
</button>
|
||||
----
|
||||
* 하나의 권한을 둘 경우
|
||||
|
||||
[source, html]
|
||||
----
|
||||
<button type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
v-authorization="'UPDATE'"
|
||||
@click="onApprWrite"
|
||||
>
|
||||
{{ $t('sdl.approval.label.APPROVE') }}
|
||||
</button>
|
||||
----
|
||||
* 관리자일 경우는 directive 지정 상관 없이 무조건 show 처리된다.
|
||||
@@ -0,0 +1,24 @@
|
||||
= 권한관리 기간 배치
|
||||
|
||||
== 만료 권한 삭제
|
||||
배치를 실행하여 사용자 및 역할에 매핑된 권한 중 만료된 권한을 삭제한다.
|
||||
|
||||
.config.properties
|
||||
[source,properties]
|
||||
----
|
||||
batch.user.auth-expired.cron=0 10 02 * * ?
|
||||
----
|
||||
|
||||
== 권한만료 예정 안내 메일 발송
|
||||
배치를 실행하여 권한만료 예정인 사용자를 대상으로 안내메일을 발송한다.
|
||||
|
||||
.config.properties
|
||||
[source,properties]
|
||||
----
|
||||
batch.user.auth-expired-mailing.cron=0 30 02 * * ?
|
||||
----
|
||||
|
||||
== Spring 환경설정
|
||||
* QuartzConfig.java: Scheduler 등록
|
||||
* UserBatchConfig.java: 배치 Job, Trigger 등록
|
||||
* UserBatchExecutor.java: 서비스 호출
|
||||
@@ -0,0 +1,185 @@
|
||||
= 로그인
|
||||
|
||||
SDL은 Knox EpTray, ID/PW, ADFS 로그인을 지원한다.
|
||||
|
||||
== EpTray
|
||||
Knox EpTray 로그인은 Chrome, Edge 등 멀티 브라우저에서 가능하다.
|
||||
사용자가 Knox에 로그인을 한 후 시스템에 접속을 하게 되면 EpTray값을 이용해 시스템 로그인을 한다.
|
||||
|
||||
NOTE: Knox EpTray SSO 를 위한 연계 신청이 필요하다. (스테이지/운영) +
|
||||
자세한 내용 안내 및 문의는 Knox Support (매뉴얼 > KnoxPortal NewEpTray 연계 가이드) 를 참조한다.
|
||||
|
||||
=== EpTray 적용
|
||||
연계신청 과정에서 생성되는 *rsaprivkey8.pem* 파일을 +
|
||||
config.properties의 login.sso.knox-tray-private-key-path 경로에 넣어준다.
|
||||
|
||||
CAUTION: *rsaprivkey8.pem* 파일이 없는 경우 아래와 같은 에러가 발생하므로 주의한다.
|
||||
|
||||
image::newEpTrayUtil.png[Can not load new EpTray Private Key.png]
|
||||
|
||||
=== LoginPage.vue
|
||||
|
||||
UI 에서 사용자 정보가 없을 경우 LoginPage.vue 페이지로 이동하게 되고 인증 절차를 시작한다.
|
||||
|
||||
EpTray 연계를 위한 메서드
|
||||
|
||||
[source,javascript]
|
||||
----
|
||||
async loginByNewEpTray() {
|
||||
let loginResult = false;
|
||||
SDLUtil.showLoadingBar(true);
|
||||
await this.socConnect().then((data) => {
|
||||
this.newEpTrayKey = data;
|
||||
this.websocket.close();
|
||||
}).catch((err) => {
|
||||
SDLUtil.alert(err);
|
||||
});
|
||||
SDLUtil.showLoadingBar(false);
|
||||
|
||||
if (this.newEpTrayKey) {
|
||||
if (this.newEpTrayKey.result === 'success') {
|
||||
const { userInfo, key } = this.newEpTrayKey;
|
||||
loginResult = await this.loginNewEpTray({ userInfo, key });
|
||||
} else {
|
||||
SDLUtil.alert(`sdl.epTray.error.${this.newEpTrayKey.errorCode}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!loginResult) {
|
||||
if (this.$route.query.token) {
|
||||
loginService.setToken(this.$route.query.token);
|
||||
document.location = `${SDLUtil.WEB_CONTEXT_PATH}/`;
|
||||
}
|
||||
}
|
||||
this.afterLoginProcess(loginResult);
|
||||
},
|
||||
----
|
||||
|
||||
[source,javascript]
|
||||
----
|
||||
socConnect() {
|
||||
return new Promise(((resolve, reject) => {
|
||||
const server = new WebSocket('wss://localhost:29283'); // <1>
|
||||
this.websocket = server;
|
||||
server.onopen = () => {
|
||||
server.send('{"rqtype":"getknoxsso","token":"","data":"KCC60TRAY0072"}'); // <2>
|
||||
server.onmessage = (event) => {
|
||||
const socketData = JSON.parse(event.data);
|
||||
if (socketData.rpcode === 'RETURN_SUCCESS') {
|
||||
resolve(JSON.parse(socketData.data));
|
||||
} else {
|
||||
this.websocket.close();
|
||||
let err;
|
||||
if (socketData.rpcode === 'EMPTY_BOX') err = 'sdl.epTray.rpCode.EMPTY_BOX';
|
||||
else err = `sdl.epTray.rpCode.${socketData.detail}`;
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
server.onerror = () => {
|
||||
const err = 'sdl.epTray.rpCode.CONNECTION_FAILED';
|
||||
reject(err);
|
||||
};
|
||||
}));
|
||||
},
|
||||
----
|
||||
<1> WebSocket HOST 정보
|
||||
<2> 발급받은 연계용 system ID
|
||||
|
||||
=== LoginController.java
|
||||
|
||||
LoginController 에서는 EpTray값의 정보를 이용해 사용자를 로그인 한다.
|
||||
이때 시스템 사용자가 아닐 경우, 등록 화면으로 이동하고, 정상적인 시스템 사용자일 경우
|
||||
Token을 발급한다.
|
||||
|
||||
GET /noauth/login/new-eptray
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@PostMapping("/noauth/login/new-eptray")
|
||||
@Operation(value = "", description = "knox new eptray 로그인")
|
||||
public JwtResponse loginByNewEptray(HttpServletRequest request, HttpServletResponse response, @RequestParam String encodeUserInfo, @RequestParam String encodeAesKey ) throws AccountException {
|
||||
loginInterceptorExecutor.applyPreLogin(request, response);
|
||||
User user = authenticationService.loginByNewEpTray(encodeUserInfo, encodeAesKey);
|
||||
user.setRecentLoginIp(webUtil.getClientIp(request));
|
||||
JwtResponse jwtResponse = new JwtResponse(jwtUtil.generateToken(user), user.getUserId());
|
||||
user.setJwt(jwtResponse.getJwtToken().split("\\.")[2]);
|
||||
loginInterceptorExecutor.applyPostLogin(request, response, user);
|
||||
return jwtResponse;
|
||||
}
|
||||
----
|
||||
|
||||
== ADFS 로그인
|
||||
|
||||
NOTE: ADFS 통합인증 사이트 (https://adsso.sec.samsung.net) 에서 신청 후 사용해야 한다. +
|
||||
자세한 내용은 해당 사이트를 참조하거나 전자통합인증3 (nextsso3@samsung.com) 으로 문의 한다.
|
||||
|
||||
SDL에서는 ADFS 통합인증 사이트의 가이드를 참조하여 AD 로그인 샘플을 제공하고 있으며, 각 프로젝트 환경에 맞게 수정하여 사용한다.
|
||||
|
||||
=== 참고할 샘플 파일
|
||||
* LoginPage.vue
|
||||
* AdLoginController.java
|
||||
* onelogin.saml.properties
|
||||
|
||||
== 로그인/아웃 전,후 처리
|
||||
|
||||
로그인 전과 후, 로그아웃 전과 후 비즈니스 로직이 필요한 경우 LoginInterceptor를 상속받아 구현한다.
|
||||
|
||||
LoginInterceptor 클래스는 아래와 같은 Interface를 제공한다.
|
||||
[source, java]
|
||||
----
|
||||
/**
|
||||
* 로그인 전
|
||||
*/
|
||||
default void preLogin(HttpServletRequest request, HttpServletResponse response) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 로그인 후
|
||||
*/
|
||||
default void postLogin(HttpServletRequest request, HttpServletResponse response, User user) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 로그아웃 전
|
||||
*/
|
||||
default void preLogout(HttpServletRequest request, HttpServletResponse response, User user) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 로그아웃 후
|
||||
*/
|
||||
default void postLogout(HttpServletRequest request, HttpServletResponse response, User user) {
|
||||
}
|
||||
----
|
||||
|
||||
LoginInterceptor를 구현한 클래스는 LoginInterceptorExecutor에 의해 실행되며 Spring Bean으로 등록하고 SpringWebCofig에 아래와 같이 LoginInterceptorExecutor에 등록한다.
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
public LoginInterceptorExecutor loginInterceptorExecutor() {
|
||||
List<LoginInterceptor> loginInterceptorList = new ArrayList<>();
|
||||
loginInterceptorList.add(userUpdateInterceptor());
|
||||
loginInterceptorList.add(loginOutLogInterceptor());
|
||||
return new LoginInterceptorExecutor(loginInterceptorList);
|
||||
}
|
||||
----
|
||||
|
||||
아래는 로그인 후 사용자 정보를 update 하는 UserUpdateInterceptor의 일부분이다.
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@Override
|
||||
public void postLogin(HttpServletRequest request, HttpServletResponse response, User user) {
|
||||
|
||||
String recentLoginIp = user.getRecentLoginIp();
|
||||
Date recentLoginDatetime = user.getRecentLoginDatetime();
|
||||
user.setLastLoginDate(recentLoginDatetime);
|
||||
user.setLastLoginIp(recentLoginIp);
|
||||
user.setRecentLoginDatetime(new Date());
|
||||
user.setRecentLoginIp(webUtil.getClientIp(request));
|
||||
user.setLastActivityTime(System.currentTimeMillis());
|
||||
userService.updateUser(user);
|
||||
}
|
||||
----
|
||||
@@ -0,0 +1,248 @@
|
||||
= 메뉴 관리
|
||||
|
||||
== 개요
|
||||
|
||||
메뉴와 하위 Page 및 API 목록을 관리하는 화면이다. +
|
||||
|
||||
NOTE: 신규 메뉴를 등록 하거나 메뉴정보를 수정 또는 삭제할 경우 콜백함수를 통해서 메뉴관련 된 모든 캐시가 초기화 된다.
|
||||
|
||||
== UI Design & Function
|
||||
|
||||
=== 메뉴 관리
|
||||
|
||||
메뉴를 등록, 수정 및 관리 할 수 있는 메뉴 트리를 제공한다.
|
||||
|
||||
image::menuManagement_01.png[]
|
||||
|
||||
* 기능 설명
|
||||
. 메뉴 트리 목록 조회
|
||||
. 메뉴 상세정보 조회 : 트리에서 메뉴 선택시 메뉴 상세정보를 조회 한다.
|
||||
. 메뉴 등록 : 현재 선택된 메뉴 하위에 새로운 메뉴를 추가 한다.
|
||||
. 메뉴 수정 / 삭제 : 현재 선택된 메뉴를 수정 또는 삭제 한다.
|
||||
. 메뉴 이동 : 트리에서 Drag&Drop 기능으로 선택한 메뉴를 이동할 수 있다.
|
||||
|
||||
=== 페이지 관리
|
||||
|
||||
image::menuManagement_02.png[]
|
||||
|
||||
* 기능 설명
|
||||
. Page 목록 조회 : 메뉴 트리에서 메뉴 선택 시 해당 메뉴에 맵핑되어 있는 페이지 목록 조회.
|
||||
. Page 등록, 수정, 삭제
|
||||
|
||||
NOTE: 권한 타입은 READ, UPDATE, EXECUTE, DOWNLOAD 로 구성되어 있으며 사용자가 가지고 있는 해당 메뉴에 대한 권한 종류로 UI의 권한 제어를 하고 있다. +
|
||||
자세한 내용은 <<_화면별_권한처리,화면별 권한처리>> 매뉴얼을 참고한다.
|
||||
|
||||
=== API 관리
|
||||
|
||||
image::menuManagement_03.png[]
|
||||
|
||||
* 기능 설명
|
||||
. API 목록 조회
|
||||
. API 등록
|
||||
. API 수정
|
||||
. API 삭제
|
||||
|
||||
=== SDL 게시판 Page, API 프리셋 추가
|
||||
|
||||
image::menuManagement_04.png[]
|
||||
|
||||
* 기능 설명
|
||||
. 게시판 선택 및 Page, API 자동 추가
|
||||
|
||||
== API & Service
|
||||
|
||||
=== API
|
||||
|
||||
MenuController.java +
|
||||
Menu와 해당 메뉴에 Mapping 되어 있는 Page, API의 등록, 수정, 삭제 및 조회하는 API를 포함하고 있다.
|
||||
|
||||
* 주요 기능 API 목록
|
||||
. 메뉴 트리 목록 조회 : GET /auth/menus/menu-mgmt/level
|
||||
. 메뉴 상세정보 조회 : GET /auth/menus/{menuId}
|
||||
. 메뉴 등록 : POST /auth/menus
|
||||
. 메뉴 수정 : PUT /auth/menus
|
||||
. 메뉴 삭제 : DELETE /auth/menus/{menuId}
|
||||
. 메뉴 이동 : PUT /auth/menus/move-menu
|
||||
. Page 목록 조회 : GET /auth/menus/{menuId}/pages
|
||||
. Page 등록 : POST /auth/menus/{menuId}/pages
|
||||
. Page 수정 : PUT /auth/menus/{menuId}/pages
|
||||
. Page 삭제 : DELETE /auth/menus/{menuId}/pages
|
||||
. API 목록 조회 : GET /auth/menus/{menuId}/apis
|
||||
. API 등록 : POST /auth/menus/{menuId}/apis
|
||||
. API 수정 : PUT /auth/menus/{menuId}/apis
|
||||
. API 삭제 : DELETE /auth/menus/{menuId}/apis
|
||||
|
||||
== Entity Table & SQL
|
||||
|
||||
=== Entity Table
|
||||
|
||||
* TN_CF_SYS_RESOURCE : 리소스
|
||||
* TN_CF_SYS_RESOURCE_MAPP : 리소스 맵핑
|
||||
* TN_CF_MENU : 메뉴
|
||||
* TN_CF_PAGE : Page
|
||||
* TN_CF_API : API
|
||||
|
||||
=== SQL
|
||||
|
||||
. 메뉴 트리 목록 조회
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<select id="selectMenuLevel" parameterType="java.util.HashMap" resultMap="menuTreeInfo">
|
||||
SELECT *
|
||||
FROM (
|
||||
<include refid="conditionMenuLevel"/>
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=2]
|
||||
. 메뉴 상세정보 조회
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<select id="selectMenu" parameterType="java.util.HashMap" resultMap="menuResult">
|
||||
SELECT M.MENU_ID, M.LABEL,
|
||||
M.MENU_LEVEL, M.MENU_SEQUENCE,
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=3]
|
||||
. 메뉴 등록
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<insert id="insertMenu" parameterType="menu">
|
||||
INSERT INTO <include refid="tableMenu" />
|
||||
(MENU_ID, LABEL,
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=4]
|
||||
. 메뉴 수정
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<update id="updateMenu" parameterType="menu">
|
||||
UPDATE <include refid="tableMenu" />
|
||||
SET LABEL = #{label},
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=5]
|
||||
. 메뉴 삭제
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<delete id="deleteMenu" parameterType="java.util.HashMap">
|
||||
UPDATE <include refid="tableMenu" />
|
||||
SET USE_YN = 0,
|
||||
DELETE_YN = 1
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=6]
|
||||
. 메뉴 이동
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<update id="updateParentMenuId" parameterType="java.util.HashMap">
|
||||
UPDATE <include refid="sysResource.tableSysResource" />
|
||||
SET UPPER_SYS_RESOURCE_ID = #{parentMenuId}
|
||||
--생략--
|
||||
----
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<update id="updateMenuSequence" parameterType="java.util.HashMap">
|
||||
UPDATE <include refid="tableMenu" />
|
||||
SET MENU_SEQUENCE = #{moveToMenuSequence}
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=7]
|
||||
. Page 목록 조회
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<select id="selectPageList" parameterType="java.util.HashMap" resultMap="pageResult">
|
||||
SELECT P.PAGE_ID, SA.AUTHORIZATION_ID, P.PAGE_NAME,
|
||||
P.PAGE_PATH, P.COMPONENT_NAME, P.DEFAULTED, P.USE_YN
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=8]
|
||||
. Page 등록
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<insert id="insertPage" parameterType="page">
|
||||
INSERT INTO <include refid="tablePage" />
|
||||
(PAGE_ID, PAGE_NAME, PAGE_PATH,
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=9]
|
||||
. Page 수정
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<update id="updatePage" parameterType="page">
|
||||
UPDATE <include refid="tablePage" />
|
||||
SET PAGE_NAME = #{pageName},
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=10]
|
||||
. Page 삭제
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<delete id="deletePage" parameterType="java.util.HashMap">
|
||||
UPDATE <include refid="tablePage" />
|
||||
SET USE_YN = 0,
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=11]
|
||||
. API 목록 조회
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<select id="selectApiList" parameterType="java.util.HashMap" resultMap="apiResult">
|
||||
SELECT A.API_ID, SA.AUTHORIZATION_ID, A.API_NAME,
|
||||
A.API_URL, A.HTTP_METHOD, A.SERVER_ID, A.USE_YN
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=12]
|
||||
. API 등록
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<insert id="insertApi" parameterType="api">
|
||||
INSERT INTO <include refid="tableApi" />
|
||||
(API_ID, API_NAME, API_URL, API_PATH, API_PARAMETERS, HTTP_METHOD, SERVER_ID, USE_YN, DELETE_YN)
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=13]
|
||||
. API 수정
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<update id="updateApi" parameterType="api">
|
||||
UPDATE <include refid="tableApi" />
|
||||
SET API_NAME = #{apiName},
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=14]
|
||||
. API 삭제
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<delete id="deleteApi" parameterType="java.util.HashMap">
|
||||
UPDATE <include refid="tableApi" />
|
||||
SET USE_YN = 0,
|
||||
--생략--
|
||||
----
|
||||
@@ -0,0 +1,83 @@
|
||||
= 부서관리(Knox)
|
||||
|
||||
NOTE: 해당 기능 사용 필요시 Knox API를 통해 연계 해야 한다.
|
||||
|
||||
== Table
|
||||
* 부서(Knox) : TN_CF_DEPT_LDAP
|
||||
|
||||
== API
|
||||
.KnoxDepartmentController.java
|
||||
|
||||
* 부서의 데이터가 많으므로 목록에 1레벨만 불러오고 1레벨의 부서를 클릭시 "부서 관리(Knox)(하위 레벨 부서)" API를 실행하여 하위 부서를 조회한다. +
|
||||
부서 관리(사용자정의) 화면에서 부서매핑을 추가할 때도 사용한다.
|
||||
|
||||
. 부서 관리(Knox)(1레벨 부서) +
|
||||
GET /admin/knox-department +
|
||||
Query ID : selectKnoxDepartmentList
|
||||
|
||||
. 부서 관리(Knox)(하위 레벨 부서) +
|
||||
GET /admin/knox-department/{upperDeptCode}
|
||||
Query ID : selectKnoxDepartmentListByUpperCode
|
||||
|
||||
== 부서 Tree 구조
|
||||
|
||||
부서관리를 tree 구조로 보여지게 하기 위해, DB의 데이터가 parent 관계를 구성해야 한다.
|
||||
LDAP 부서관리는 TN_CF_DEPT_LDAP 테이블의 dept_code(Child) 와 upper_dept_code(Parent) 관계로 tree 구조를 만들게 되는데 시스템의 최상위 dept code에는 ROOT를 입력해야 한다.
|
||||
내부적으로 LDAP 부서 코드의 Root 밑의 1 level을 찾는 query가 *ROOT* 라는 코드를 찾도록 되어 있다.
|
||||
|
||||
|===
|
||||
|DEPT_CODE |DEPT_NAME |DEPT_LEVEL |UPPER_DEPT_CODE
|
||||
|
||||
|C00001
|
||||
|정보전략
|
||||
|1
|
||||
|ROOT
|
||||
|
||||
|C000011
|
||||
|정보전략 부서1
|
||||
|2
|
||||
|C00001
|
||||
|
||||
|C00002
|
||||
|마케팅
|
||||
|1
|
||||
|ROOT
|
||||
|
||||
|C000021
|
||||
|마케팅 부서1
|
||||
|2
|
||||
|C00002
|
||||
|===
|
||||
|
||||
사용자정의 부서관리는 TN_CF_DEPT_SELF 테이블의 SELF_DEPT_CODE(Child) 와 UPPER_SELF_DEPT_CODE(Parent)
|
||||
관계로 tree 구조를 이루며, 시스템의 최상위 SELF_DEPT_CODE는 DEPT를 입력해야 한다.
|
||||
|
||||
|===
|
||||
|DEPT_CODE |DEPT_NAME |DEPT_LEVEL |UPPER_DEPT_CODE
|
||||
|
||||
|C00001
|
||||
|정보전략
|
||||
|1
|
||||
|DEPT
|
||||
|
||||
|C000011
|
||||
|정보전략 부서1
|
||||
|2
|
||||
|C00001
|
||||
|
||||
|C00002
|
||||
|마케팅
|
||||
|1
|
||||
|DEPT
|
||||
|
||||
|C000021
|
||||
|마케팅 부서1
|
||||
|2
|
||||
|C00002
|
||||
|===
|
||||
|
||||
== 부서 목록
|
||||
|
||||
TN_CF_DEPT_LDAP 테이블에 저장되어 있는 부서정보를 트리형태로 보여준다.
|
||||
|
||||
image::deptMgmt(Knox).png[deptMgmt(Knox),300,400]
|
||||
@@ -0,0 +1,79 @@
|
||||
= 부서관리(사용자정의)
|
||||
|
||||
== Table
|
||||
* 부서(사용자정의) : TN_CF_DEPT_SELF
|
||||
* 부서매핑 : TN_CF_DEPT_MAPPING
|
||||
|
||||
== API
|
||||
.CustomDepartmentController.java
|
||||
|
||||
. 부서관리(사용자 정의) 목록 조회 +
|
||||
GET /admin/department/custom +
|
||||
Query ID : selectCustomDepartmentList
|
||||
|
||||
. 부서 하위 레벨 조회 +
|
||||
GET /admin/department/custom/dept-level-sub +
|
||||
Query ID : selectCustomDepartmentSubList
|
||||
* 부서의 데이터가 많으므로 목록에 1레벨만 불러오고 1레벨의 부서를 클릭시 "부서 하위 레벨 조회" API를 실행하여 하위 부서를 조회한다.
|
||||
|
||||
. 부서 상세정보 조회 +
|
||||
GET /admin/department/custom/dept-infos +
|
||||
Query ID : selectCustomDepartmentList, selectDeptMapping
|
||||
* 부서 정보와 부서 매핑을 함께 불러온다.
|
||||
|
||||
. 부서 저장 +
|
||||
POST /admin/department/custom +
|
||||
Query ID : insertCustomDepartment
|
||||
* 등록된 부서가 있는지 중복체크하고 없으면 저장한다.
|
||||
|
||||
. 부서 수정 +
|
||||
PUT /admin/department/custom/{selfDeptCode} +
|
||||
Query ID : updateCustomDepartment
|
||||
* 부서명, 정렬순서, 설명만 수정 가능하다.
|
||||
|
||||
. 부서 이동 +
|
||||
PUT /admin/department/custom +
|
||||
Query ID : updateDeptUpperDeptCode, updateDeptSequence
|
||||
* 부서는 Drag & Drop 으로 원하는 곳에 이동하여 저장할 수 있다.
|
||||
|
||||
. 부서 매핑 조회 +
|
||||
GET /admin/department/custom/mappings/{selfDeptCode} +
|
||||
Query ID : selectDeptMapping
|
||||
|
||||
. 부서 매핑 저장 +
|
||||
POST /admin/department/custom/mappings/{selfDeptCode} +
|
||||
Query ID : insertDeptMapping
|
||||
* DB에 저장된 mapping과 화면에서 추가한 mapping을 비교하여 DB에 없는 mapping만 insert 한다.
|
||||
|
||||
. 부서 매핑 삭제 +
|
||||
DELETE /admin/department/custom/mappings/{selfDeptCode}/{deptCode} +
|
||||
Query ID : deleteDeptMapping
|
||||
|
||||
== 부서관리(사용자정의) 기본 정보
|
||||
사용자의 임의로 부서를 등록 및 해당되는 부서의 Knox 부서 매핑.
|
||||
|
||||
image::deptList.png[]
|
||||
|
||||
=== 기본정보 필드 설명
|
||||
- 부서코드 : 부서에 부여되는 코드(Unique)
|
||||
- 부서명 : 부서명칭
|
||||
- 부서레벨 : 부서에 부여되는 Level이며 최상위 Department 부서는 0 level이다.
|
||||
- 정렬순서 : 같은 레벨 상 나오는 부서의 순서
|
||||
- 설명 : 부서에 대한 설명
|
||||
|
||||
=== 기본정보 기능별 설명
|
||||
- 삭제 : 부서목록의 부서를 선택 후 삭제 버튼 클릭시 선택된 부서 삭제(하위 부서 포함)
|
||||
- 추가 : 추가하고자 하는 상위 부서를 선택 후 추가 버튼을 클릭시 Tree 구조에 New Document 생성
|
||||
- 저장 : 추가로 생성된 또는 선택된 부서 정보를 저장
|
||||
|
||||
== 부서 매핑
|
||||
해당 부서에 대한 Knox 부서 매핑 정보.
|
||||
|
||||
=== 부서 매핑 정보 필드 설명
|
||||
- 부서코드 : Knox 부서에 부여되는 코드(Unique)
|
||||
- 부서명 : Knox 부서명칭
|
||||
- 삭제 : 삭제 실행 버튼
|
||||
|
||||
=== 기능별 설명
|
||||
- 추가 : Knox 부서 Popup 호출
|
||||
- 저장 : 호출된 Knox 부서 Popup 화면에서 추가하고자 하는 부서 매핑 정보에 저장.
|
||||
@@ -0,0 +1,23 @@
|
||||
= 사용권한 신청/승인
|
||||
|
||||
== 개요
|
||||
사용자가 시스템에 가입시 시스템 사용 권한을 승인/승인 취소/삭제를 하여 시스템을 사용/미사용하게 한다.
|
||||
|
||||
== Table
|
||||
* 사용자 : TN_CF_USER
|
||||
|
||||
== API
|
||||
.UserController.java
|
||||
|
||||
. 사용자 승인 +
|
||||
PUT /auth/users/status/confirm +
|
||||
Query ID : updateUser
|
||||
|
||||
. 사용자 승인취소 +
|
||||
PUT /auth/users/status/inactive +
|
||||
Query ID : updateUser
|
||||
|
||||
. 사용자 삭제 +
|
||||
DELETE /auth/users +
|
||||
Query ID : updateUser
|
||||
* DeleteMapping이지만 DB 삭제가 아닌 ACTIVE_FLAG = 0, DELETED = 1 로 업데이트 한다.
|
||||
@@ -0,0 +1,30 @@
|
||||
= 사용자 관리
|
||||
|
||||
== 사용자 관리 화면
|
||||
사용자 목록을 조회하며 시스템 사용 신청 대기자에 대한 사용 승인 및 취소가 가능하다. +
|
||||
사용자 조회 목록의 Row를 클릭시 사용자 상세 정보 화면으로 이동할 수 있다.
|
||||
|
||||
image::userMgmt.png[]
|
||||
|
||||
=== 기능별 설명
|
||||
- 엑셀다운로드 : 조회 조건과 동일한 조회 결과 값을 담은 엑셀 파일을 다운로드 하는 기능.
|
||||
- 삭제 : 선택된 사용자를 삭제 처리하는 기능.
|
||||
* 삭제된 사용자의 row 데이터는 남아 있지만 사용자의 정보(EP ID, 사용자 이름 제외)는 모두 삭제 된다.
|
||||
- 승인취소 : 선택된 사용자를 승인취소 처리하는 기능.
|
||||
- 승인 : 선택된 사용자를 승인 처리하는 기능.
|
||||
* 세부적인 내용은 <<_사용권한_신청승인,사용권한 신청/승인>> 매뉴얼을 참조한다.
|
||||
|
||||
== 사용자 상세 조회 화면
|
||||
사용자의 상세정보표시 및 조회한 사용자에 대한 역할 및 메뉴 추가/삭제가 가능하다.
|
||||
|
||||
image::userInfo.png[]
|
||||
|
||||
=== 기능별 설명
|
||||
- 사용자-역할 삭제 : 선택된 역할을 삭제
|
||||
- 사용자-역할 추가 : 선택된 역할을 추가하는 기능. 역할 팝업이 호출 되며, 사용자에게 부여하고 싶은 역할을 선택하여 확인 버튼 클릭시 사용자-역할 Grid에 추가된다.
|
||||
- 사용자-역할 저장 : 추가 또는 수정된 역할이 있을 경우, 저장 기능 수행.
|
||||
- 사용자-메뉴 삭제 : 선택된 메뉴를 삭제
|
||||
- 사용자-메뉴 추가 : 선택된 메뉴를 추가하는 기능. 메뉴 팝업이 호출 되며, 사용자에게 부여하고 싶은 메뉴을 선택하여 확인 버튼 클릭시 사용자-메뉴 Grid에 추가된다.
|
||||
* 메뉴의 Page, API 권한 타입(READ, UPDATE, EXECUTE, DOWNLOAD) 중 필요한 권한을 check하여 등록한다.
|
||||
* 권한 타입에 대한 설명은 메뉴관리 가이드를 참고한다. <<_메뉴_관리,메뉴 관리>>
|
||||
- 사용자-메뉴 저장 : 추가 또는 수정된 메뉴이 있을 경우, 저장 기능 수행
|
||||
@@ -0,0 +1,228 @@
|
||||
= 업무 그룹 관리
|
||||
|
||||
== 개요
|
||||
|
||||
메일 그룹 및 맵핑 정보 관리 기능 제공.
|
||||
|
||||
== UI Design & Function
|
||||
|
||||
=== 업무 그룹 목록(WorkgroupList.vue)
|
||||
|
||||
업무별로 업무그룹을 만들고 해당 업무그룹에서 사용가능한 메뉴를 선택, 해당 메뉴 사용 가능한 Role 또는 User를 추가한다.
|
||||
|
||||
image::workgroupList.png[업무 그룹 목록]
|
||||
|
||||
* 기능 설명
|
||||
. 업무그룹 목록 조회
|
||||
. 업무그룹 상세정보 조회
|
||||
. 업무그룹 등록 : 업무그룹 정보를 입력하는 Popup이 호출 되며, 정보 기입 후 저장 버튼을 클릭하여 등록한다.
|
||||
. 업무그룹 수정 : 선택된(checkbox : true) 업무그룹 정보를 수정하는 Popup이 호출 되며 역할 정보 기입 후 저장 버튼을 클릭하여 수정한다.
|
||||
. 업무그룹 삭제 : 선택된(checkbox : true) 역할 목록을 삭제한다.
|
||||
|
||||
=== 업무그룹-메뉴(WorkgroupMenuInfo.vue)
|
||||
|
||||
업무그룹에서 사용 가능한 메뉴를 관리 하는 화면. +
|
||||
메뉴 등록 및 삭제가 가능하며 메뉴별 권한 설정이 가능하다.
|
||||
|
||||
image::workgroupMenuInfo.png[업무그룹-메뉴]
|
||||
|
||||
* 기능 설명
|
||||
. 업무그룹-메뉴 목록 조회
|
||||
. 업무그룹-메뉴 등록 : 메뉴를 선택하는 Popup이 호출 되며 선택 후 저장 버튼을 클릭하여 등록한다.
|
||||
. 업무그룹-메뉴 수정 : 권한을 선택(checkbox : true)하여 해당 메뉴에 대한 권한을 수정한다.
|
||||
. 업무그룹-메뉴 삭제 : 선택된(checkbox : true) 메뉴 목록을 삭제한다.
|
||||
|
||||
=== 업무그룹-역할(WorkgroupMenuInfo.vue)
|
||||
|
||||
선택한 업무그룹에 역할 또는 사용자를 관리하는 화면. +
|
||||
역할과 사용자를 추가 또는 삭제할 수 있다.
|
||||
|
||||
image::workgroupRoleInfo.png[업무그룹-역할]
|
||||
|
||||
* 기능 설명
|
||||
. 업무그룹-역할 목록 조회
|
||||
. 업무그룹-역할 등록
|
||||
.. 사용자 등록 : 사용자 추가 버튼 클릭 시 사용자를 선택할 수 있는 Popup이 호출 되며 선택 후 저장 버튼을 클릭하여 등록 한다.
|
||||
.. 역할 등록 : 역할 추가 버튼 클릭 시 역할을 선택할 수 있는 Popup이 호출 되며 선택 후 저장 버튼을 클릭하여 등록 한다.
|
||||
. 업무그룹-역할 수정 : 등록된 사용자의 업무그룹 만료일을 수정 한다.
|
||||
. 업무그룹-역할 삭제 : 선택된(checkbox : true) 역할(사용자) 목록을 삭제한다.
|
||||
|
||||
== API & Service
|
||||
|
||||
=== API
|
||||
|
||||
* API : WorkgroupController.java
|
||||
|
||||
. 업무그룹 목록 조회 : GET /auth/workgroups-with-paging
|
||||
. 업무그룹 상세정보 조회 : GET /auth/workgroups/{workgroupId}
|
||||
. 업무그룹 등록 : POST /auth/workgroups
|
||||
. 업무그룹 수정 : PUT /auth/workgroups/{workgroupId}
|
||||
. 업무그룹 삭제 : DELETE /auth/workgroups
|
||||
. 업무그룹-메뉴 목록 조회 : GET /auth/workgroups/{workgroupId}/menus
|
||||
. 업무그룹-메뉴 등록 : POST /auth/workgroups/{workgroupId}/menus
|
||||
. 업무그룹-메뉴 수정 : PUT /auth/workgroups/{workgroupId}/menus
|
||||
. 업무그룹-메뉴 삭제 : DELETE /auth/workgroups/{workgroupId}/menus
|
||||
. 업무그룹-역할 목록 조회 : GET /auth/workgroups/{workgroupId}/roles
|
||||
. 업무그룹-역할 등록 : POST /auth/workgroups/{workgroupId}/roles
|
||||
. 업무그룹-역할 수정 : PUT /auth/workgroups/{workgroupId}/roles
|
||||
. 업무그룹-역할 삭제 : DELETE /auth/workgroups/{workgroupId}/roles
|
||||
|
||||
* Service : WorkgroupServiceImpl.java
|
||||
|
||||
. 업무그룹-메뉴 목록 조회. +
|
||||
메뉴의 전체 경로 셋팅 및 권한별 row 데이터를 메뉴 row 데이터로 가공한다.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Override
|
||||
public List<WorkgroupMenuDto> getWorkgroupMenuList(String workgroupId) {
|
||||
|
||||
List<WorkgroupMenuDto> workgroupMenuDtoList = new ArrayList<>();
|
||||
List<String> authorizationIdList;
|
||||
String beforeMenuId = "";
|
||||
-- 생략 --
|
||||
----
|
||||
|
||||
== Entity Table & SQL
|
||||
|
||||
=== Entity Table
|
||||
|
||||
* TN_CF_WORKGROUP : 업무그룹
|
||||
* TN_CF_WORK_AUTHORIZATION : 업무그룹 메뉴 권한 맵핑
|
||||
* TN_CF_WORKGROUP_ROLE : 업무그룹 역할 맵핑
|
||||
|
||||
=== SQL
|
||||
|
||||
. 업무그룹 목록 조회
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<select id="selectWorkgroupPagingList" parameterType="java.util.HashMap" resultMap="workgroupResult">
|
||||
SELECT *
|
||||
FROM (SELECT ROW_NUMBER() OVER ( <if test="@com.samsung.SdlComparator@isNotEmptyForDynamicSql(orderBy)">ORDER BY ${orderBy}</if> ) ROWNUM,
|
||||
<include refid="columnsWorkgroup" />
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=2]
|
||||
. 업무그룹 상세정보 조회
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<select id="selectWorkgroup" parameterType="java.util.HashMap" resultMap="workgroupResult">
|
||||
SELECT <include refid="columnsWorkgroup" />
|
||||
FROM <include refid="tableWorkgroup" />
|
||||
<include refid="conditionWorkgroup" />
|
||||
</select>
|
||||
----
|
||||
|
||||
[start=3]
|
||||
. 업무그룹 등록
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<insert id="insertWorkgroup" parameterType="workgroup">
|
||||
INSERT INTO <include refid="tableWorkgroup" />
|
||||
(<include refid="columnsWorkgroup"/>)
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=4]
|
||||
. 업무그룹 수정
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<update id="updateWorkgroup" parameterType="workGroup">
|
||||
UPDATE <include refid="tableWorkgroup" />
|
||||
SET WORKGROUP_NAME = #{workgroupName},
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=5]
|
||||
. 업무그룹 삭제
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<delete id="deleteWorkgroup" parameterType="java.util.HashMap">
|
||||
UPDATE <include refid="tableWorkgroup" />
|
||||
SET DELETE_YN = 1,
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=6]
|
||||
. 업무그룹-메뉴 목록 조회
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<select id="selectWorkgroupMenuList" parameterType="java.util.HashMap" resultMap="workgroupMenuResult">
|
||||
WITH TREE_TABLE AS (SELECT SR.UPPER_SYS_RESOURCE_ID, M.MENU_ID,
|
||||
CONVERT(NVARCHAR(100), M.LABEL) LABEL,
|
||||
CONVERT(NVARCHAR(100), M.LABEL_JSON) LABEL_JSON,
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=7]
|
||||
. 업무그룹-메뉴 등록
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<insert id="insertWorkgroupMenu" parameterType="workgroupMenu">
|
||||
INSERT INTO <include refid="tableWorkAuthorization" />
|
||||
(WORKGROUP_ID, SYS_RESOURCE_ID, AUTHORIZATION_ID,
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=8]
|
||||
. 업무그룹-메뉴 삭제
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<delete id="deleteWorkgroupMenu" parameterType="workgroupMenu">
|
||||
DELETE FROM <include refid="tableWorkAuthorization" />
|
||||
WHERE SYS_RESOURCE_ID = #{sysResourceId}
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=9]
|
||||
. 업무그룹-역할 목록 조회
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<select id="selectRoleWorkgroupList" parameterType="java.util.HashMap" resultMap="workgroup.workgroupResult">
|
||||
SELECT W.WORKGROUP_ID, W.WORKGROUP_NAME, W.DESCRIPTION,
|
||||
W.LABEL
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=10]
|
||||
. 업무그룹-역할 등록
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<insert id="insertWorkgroupRole" parameterType="workgroupRole">
|
||||
INSERT INTO <include refid="tableWorkgroupRole" />
|
||||
(WORKGROUP_ID, USER_ROLE_ID, FROM_DATE, THRU_DATE, ROLE_ID, USER_ID,
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=11]
|
||||
. 업무그룹-역할 수정
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<update id="updateWorkgroupRole" parameterType="workgroupRole">
|
||||
UPDATE <include refid="tableWorkgroupRole" />
|
||||
SET THRU_DATE = #{thruDate},
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=12]
|
||||
. 업무그룹-역할 삭제
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<delete id="deleteWorkgroupRole" parameterType="java.util.HashMap">
|
||||
DELETE FROM <include refid="tableWorkgroupRole" />
|
||||
WHERE WORKGROUP_ID = #{workgroupId}
|
||||
--생략--
|
||||
----
|
||||
@@ -0,0 +1,207 @@
|
||||
= 역할 관리
|
||||
|
||||
== 개요
|
||||
|
||||
역할을 등록하고, 해당 역할에 사용자 및 업무그룹을 Mapping 한다.
|
||||
|
||||
== UI Design & Function
|
||||
|
||||
=== 역할 목록(RoleList.vue)
|
||||
|
||||
역할 등록, 수정 및 조회. +
|
||||
조회된 목록의 사용자/업무그룹 컬럼을 클릭하여 역할-사용자, 역할-업무그룹 상세 화면으로 이동한다.
|
||||
|
||||
image::roleList.png[역할 관리]
|
||||
|
||||
* 기능 설명
|
||||
. 역할 목록 조회
|
||||
. 역할 등록 : 역할 정보를 입력하는 Popup이 호출 되며 정보 기입 후 저장 버튼을 클릭하여 등록한다.
|
||||
. 역할 수정 : 선택된(checkbox : true) 역할 정보를 수정하는 Popup이 호출 되며, 정보 기입 후 저장 버튼을 클릭하여 수정한다.
|
||||
. 역할 삭제 : 선택된(checkbox : true) 역할 목록을 삭제한다.
|
||||
. 엑셀 다운로드 : 조회 조건과 동일한 역할 목록을 엑셀 파일로 다운로드 한다.
|
||||
|
||||
=== 역할-사용자 관리(RoleUserInfo.vue)
|
||||
|
||||
선택한 역할에 대한 사용자 추가. +
|
||||
역할에 대한 사용자가 많을 경우를 대비하여 상단에 페이징 처리 되는 Grid를, 하단에는 사용자를 추가하는 Grid로 나누어 화면을 구성된다.
|
||||
|
||||
image::roleUserInfo.png[역할-사용자 관리]
|
||||
|
||||
* 기능 설명
|
||||
. 역할-사용자 목록 조회
|
||||
. 역할-사용자 등록 : 추가 버튼 클릭시 사용자 조회 Popup이 호출 되며 사용자 선택 후 추가된 하단의 사용자 목록에서 저장한다.
|
||||
. 역할-사용자 수정 : 기등록된 상단 사용자 목록에서 역할 적용 기간을 수정한다.
|
||||
. 역할-사용자 삭제 : 기등록된 상단 사용자 목록에서 선택된(checkbox : true) 사용자 목록을 삭제한다.
|
||||
|
||||
=== 역할-업무그룹 관리(RoleWorkgroupInfo.vue)
|
||||
|
||||
선택한 역할에 대한 업무그룹 추가.
|
||||
|
||||
image::roleWorkgroupInfo.png[역할-업무그룹 관리]
|
||||
|
||||
* 기능 설명
|
||||
. 역할-업무그룹 목록 조회
|
||||
. 역할-업무그룹 등록 : 추가 버튼 클릭시 업무그룹 조회 Popup이 호출 되며 업무그룹을 선택 하여 등록 할 수 있다.
|
||||
. 역할-업무그룹 삭제 : 기등록된 상단 업무그룹 목록에서 선택된(checkbox : true) 업무그룹 목록을 삭제한다.
|
||||
|
||||
== API & Service
|
||||
|
||||
=== API
|
||||
|
||||
* API : RoleController.java
|
||||
|
||||
. 역할 목록 조회 : GET /roles-with-paging
|
||||
. 역할 상세정보 조회 : GET /roles/{roleId}
|
||||
. 역할 등록 : POST /roles
|
||||
. 역할 수정 : PUT /roles
|
||||
. 역할 삭제 : DELETE /roles
|
||||
. 역할-사용자 목록 조회 : GET /roles/{roleId}/users-with-paging
|
||||
. 역할-사용자 등록 : POST /roles/{roleId}/users
|
||||
. 역할-사용자 수정 : PUT /roles/{roleId}/users
|
||||
. 역할-사용자 삭제 : DELETE /roles/{roleId}/users
|
||||
. 역할-업무그룹 목록 조회 : GET /roles/{roleId}/workgroups
|
||||
. 역할-업무그룹 등록 : POST /roles/{roleId}/workgroups
|
||||
. 역할-업무그룹 삭제 : DELETE /roles/{roleId}/workgroups
|
||||
|
||||
== Entity Table & SQL
|
||||
|
||||
=== Entity Table
|
||||
|
||||
* TN_CF_ROLE : 역할
|
||||
* TN_CF_USER_ROLE : 역할-사용자 맵핑
|
||||
* TN_CF_WORKGROUP_ROLE : 역할-업무그룹 맵핑
|
||||
|
||||
=== SQL
|
||||
|
||||
. 역할 목록 조회
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<select id="selectRolePagingList" parameterType="java.util.HashMap" resultMap="roleResult">
|
||||
SELECT *
|
||||
FROM (SELECT
|
||||
ROW_NUMBER() OVER ( <if test="@com.samsung.SdlComparator@isNotEmptyForDynamicSql(orderBy)">ORDER BY ${orderBy}</if> ) ROWNUM,
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=2]
|
||||
. 역할 상세정보 조회
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<select id="selectRole" parameterType="java.util.HashMap" resultMap="roleResult">
|
||||
SELECT <include refid="columnsRole" />
|
||||
FROM <include refid="tableRole" />
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=3]
|
||||
. 역할 등록
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<insert id="insertRole" parameterType="role">
|
||||
INSERT INTO <include refid="tableRole" />
|
||||
(<include refid="columnsRole" />)
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=4]
|
||||
. 역할 수정
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<update id="updateRole" parameterType="role" flushCache="true" >
|
||||
UPDATE <include refid="tableRole" />
|
||||
SET ROLE_NAME = #{roleName},
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=5]
|
||||
. 역할 삭제
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<update id="deleteRole" parameterType="role" flushCache="true" >
|
||||
UPDATE <include refid="tableRole" />
|
||||
SET DELETE_YN = 1,
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=6]
|
||||
. 역할-사용자 목록 조회
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<select id="selectRoleUserPagingList" parameterType="java.util.HashMap" resultMap="roleUserResult">
|
||||
SELECT *
|
||||
FROM (SELECT
|
||||
ROW_NUMBER() OVER ( <if test="@com.samsung.SdlComparator@isNotEmptyForDynamicSql(orderBy)">ORDER BY ${orderBy}</if> ) ROWNUM,
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=7]
|
||||
. 역할-사용자 등록
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<insert id="insertUserRole" parameterType="userRole">
|
||||
INSERT INTO <include refid="tableUserRole" />
|
||||
(ROLE_ID, USER_ID, FROM_DATE, THRU_DATE,
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=8]
|
||||
. 역할-사용자 수정
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<update id="updateUserRole" parameterType="userRole">
|
||||
UPDATE <include refid="tableUserRole" />
|
||||
SET THRU_DATE = #{thruDate},
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=9]
|
||||
. 역할-사용자 삭제
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<delete id="deleteUserRole" parameterType="java.util.HashMap">
|
||||
DELETE FROM <include refid="tableUserRole" />
|
||||
WHERE USER_ID = #{userId}
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=10]
|
||||
. 역할-업무그룹 목록 조회
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<select id="selectRoleWorkgroupList" parameterType="java.util.HashMap" resultMap="workgroup.workgroupResult">
|
||||
SELECT W.WORKGROUP_ID, W.WORKGROUP_NAME, W.DESCRIPTION,
|
||||
W.LABEL
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=11]
|
||||
. 역할-업무그룹 등록
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<insert id="insertWorkgroupRole" parameterType="workgroupRole">
|
||||
INSERT INTO <include refid="tableWorkgroupRole" />
|
||||
(WORKGROUP_ID, USER_ROLE_ID, FROM_DATE, THRU_DATE, ROLE_ID, USER_ID,
|
||||
--생략--
|
||||
----
|
||||
|
||||
[start=12]
|
||||
. 역할-업무그룹 삭제
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<delete id="deleteWorkgroupRole" parameterType="java.util.HashMap">
|
||||
DELETE FROM <include refid="tableWorkgroupRole" />
|
||||
WHERE WORKGROUP_ID = #{workgroupId}
|
||||
--생략--
|
||||
----
|
||||
@@ -0,0 +1,15 @@
|
||||
= 외부 사용자 관리
|
||||
|
||||
== 개요
|
||||
ID/PW 회원가입을 통해서 등록한 사용자 목록을 조회하는 화면이다.
|
||||
|
||||
== 화면
|
||||
외부 사용자 목록 조회 및 엑셀 다운로드 기능을 제공한다.
|
||||
|
||||
NOTE: 사용자 테이블(TN_CF_USER)의 외부 사용자 여부(EXTERNAL_FLAG) 정보를 통해서 확인할 수 있다. +
|
||||
사용자 관리 메뉴에서 관리자 승인이 된 사용자 정보만 조회할 수 있다.
|
||||
|
||||
image::externalUserList.png[]
|
||||
|
||||
=== 기능별 설명
|
||||
- 엑셀다운로드 : 조회 조건과 동일한 조회 결과 값을 담은 엑셀 파일을 다운로드 하는 기능.
|
||||
@@ -0,0 +1,25 @@
|
||||
= 장기미사용자
|
||||
|
||||
== 개요
|
||||
일정 기간 시스템에 로그인(TN_CF_USER.RECENT_LOGIN_DATETIME)을 하지 않은 사용자는 장기 미사용자로 관리한다.
|
||||
|
||||
== 설명
|
||||
장기 미사용자 관리 배치를 실행하면서 3개월간 로그인 하지 않은 사용자를 조회한다. +
|
||||
|
||||
* config.properties 파일에서 개월수(기본 3개월) 및 배치 실행시간을 변경 할 수 있다.
|
||||
[source,properties]
|
||||
----
|
||||
batch.user.long-term.month=3
|
||||
batch.user.long-term-check.cron=0 10 00 * * ?
|
||||
----
|
||||
|
||||
* 조회된 장기 미사용자에게 매핑된 메뉴, 업무그룹, 역할을 삭제하고 장기 미접속자로 상태를 변경한다. 사용자 관리 재직상태 컬럼(TN_CF_USER.EXPIRE_STATUS_CODE)에서 확인 할 수 있다.
|
||||
|
||||
== 배치 설정
|
||||
|
||||
=== Spring 환경설정
|
||||
* QuartzConfig.java: Scheduler 등록
|
||||
* UserBatchConfig.java: 배치 Job, Trigger 등록
|
||||
* UserBatchExecutor.java: 서비스 호출
|
||||
|
||||
SDL의 배치 작업은 Quartz 를 이용하여 구현하고 있다. 자세한 내용은 <<_작업_스케쥴링, 작업 스케쥴링>> 및 <<_quartz_clustering_with_jdbc_jobstore, Quartz Clustering with JDBC-JobStore>> 을 참고한다.
|
||||
Reference in New Issue
Block a user