# Swit JAVA development guide

# 시작 및 준비

프로그래밍을 준비하기 위한 시작 및 준비사항 입니다.

# 실행 환경

본 가이드는 다음과 같은 환경에서 실행 됩니다.
* JAVA SDK 17
* Spring Boot 2.7.5
* Spring JPA
* Spring Quartz
* GSON (google.code.gson)
* Querydsl
  * 데이터베이스의 독립성과 편의성을 위해 Querydsl을 사용합니다. Querydsl에 대한 내요은 [Querydsl Reference Guide](http://querydsl.com/static/querydsl/5.0.0/reference/html_single/)을 참조해 주세요
* Gradle
* MySQL or MairaDB

dependency에 대한 자세한 내용은 **[Swit CTS Github](https://github.com/swit-cts/cts-api-java/blob/main/build.gradle)** 을 참조하여 주시기 바랍니다.

# 토큰 저장

# 1. 시작하며
Swit API를 사용하기 위해서는 기본적으로 토큰이 필요 합니다.
자세한 사항은 Swit Developers의 [OAuth flow](https://devdocs.swit.io/docs/guides/25i4ipzs76uyw-o-auth-flow) 를 참조해 주세요

---
# 2. 토큰의 저장
* 본 예제에서는 Access token과 Refresh token을 데이터 베이스에 저장하도록 하겠습니다.
* 먼저 Token을 담을 객체를 작성하겠습니다.
---
# 3. 토큰 객체 생성
## 3.1. SwitTokenEntity
```java
package io.swit.api.model.entity.eai;

import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Entity
@Data
@Table(name = "swit_token")
@NoArgsConstructor(access = AccessLevel.PUBLIC)
public class SwitTokenEntity {

    @Id
    @Column(name = "token_id", nullable = false, length = 30)
    private String tokenId = "SWIT";

    @Column(name = "access_token", length = 500, nullable = false)
    private String accessToken;

    @Column(name = "refresh_token", length = 500, nullable = false)
    private String refreshToken;

    @Column(name = "expire_in", nullable = true)
    private Long expireIn;

    @Column(name = "scope", length = 100, nullable = true)
    private String scope;

    @Column(name = "token_type", length = 50, nullable = false)
    private String tokenType;

}
```
* swit_token 테이블을 데이터베이스에 생성해 줍니다.

## 3.2. SwitTokenDto

```java
package io.swit.api.model.dto.eai;

import io.swit.api.model.entity.eai.SwitTokenEntity;
import lombok.Data;

@Data
public class SwitTokenDto {
    /** 토큰 아이디 */
    private String tokenId;

    /** 접근 토큰 */
    private String accessToken;

    /** 재인증 토큰 */
    private String refreshToken;

    /** 만료시간 */
    private Long expireIn;

    /** 인증 점위 */
    private String scope;

    /** 토큰 타입 */
    private String tokenType;



    /**
     * 엔티티 변환
     * @return 스윗 유저 토큰 엔티티
     */
    public SwitTokenEntity toEntity() {
        SwitTokenEntity entity = new SwitTokenEntity();
        entity.setTokenId(tokenId);
        entity.setAccessToken(this.accessToken);
        entity.setRefreshToken(this.refreshToken);
        entity.setExpireIn(this.expireIn);
        entity.setScope(this.scope);
        entity.setTokenType(this.tokenType);
        return entity;
    }
}
```
## 3.3. SwitTokenRepository
```java
package io.swit.api.data.repository.eai;

import io.swit.api.model.entity.eai.SwitTokenEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface SwitTokenRepository extends JpaRepository<SwitTokenEntity, String> {
}

```

## 3.4. AuthQuery
```java
package io.swit.api.data.query.eai;

import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import io.swit.api.data.repository.eai.SwitTokenRepository;
import io.swit.api.model.dto.eai.SwitTokenDto;
import io.swit.api.model.entity.eai.QSwitTokenEntity;
import io.swit.api.model.entity.eai.SwitTokenEntity;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@AllArgsConstructor
public class AuthQuery {
   /**
     * 사용자 토큰 저장
     * @param param 사용자 토큰 파라미터
     * @throws Exception
     */
  public void saveSwitToken(SwitTokenDto param) throws Exception {
    this.switUserTokenRepository.save(param.toEntity());
  } 
}
```

## 3.5. AuthService
```java
package io.swit.api.service.eai;

import io.swit.api.data.query.eai.AuthQuery;
import io.swit.api.model.dto.eai.SwitTokenDto;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;

@Service
@AllArgsConstructor
@Slf4j
public class AuthService {
  private AuthQuery authQuery;
  /**
    * 사용자 토큰 저장
    * @param param 사용자 토큰 파라미터
    * @throws Exception
    */
  public void saveSwitToken(SwitTokenDto param) throws Exception {
    this.authQuery.saveSwitToken(param);
  }
}
```

---
# 4. 인증 및 가져오기
## 4.1. 인증용 컨트롤러 작성
* 토큰을 요청하였을때 결과값을 받는 컨트롤러를 작성합니다.
* 토큰 요청은 [Swit Developers](https://devdocs.swit.io/docs/guides/25i4ipzs76uyw-o-auth-flow)를 참조해 주시고, 소스 관련은 Customer Tech Support 팀의 Github를 참조해 주세요.
* 아래의 소스는 인증을 받아 토큰을 받는 예제 입니다.
```java
@GetMapping(value = "/callback")
    public ResponseEntity<?> getOauthCallback(
            HttpServletRequest req,
            HttpServletResponse res,
            HttpSession session
    ) {
        String code = "";
        String state = "";
        SwitTokenDto retDto = new SwitTokenDto();

        WebClient webClient = WebClient.builder()
                .baseUrl(var.API_URL)
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
                .build();
        try {
            code = req.getParameter("code").trim();
            state = req.getParameter("state").trim();
            String redirectUri = req.getRequestURL().toString().replace("http://", "https://");

            MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
            body.add("grant_type", "authorization_code");
            body.add("code", code);
            body.add("client_id", var.CLIENT_ID);
            body.add("client_secret", var.CLIENT_SECRET);
            body.add("redirect_uri", redirectUri);

            String retVal =  webClient.post()
                    .uri(var.TOKEN_URL)
                    .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                    .bodyValue(body)
                    .retrieve()
                    .bodyToMono(String.class)
                    .block();

            JsonParser parser = new JsonParser();
            Object obj = parser.parse(retVal);
            JsonObject jsonObject = (JsonObject)obj;

            retDto.setAccessToken(jsonObject.get("access_token").toString().replace("\"", ""));
            retDto.setRefreshToken(jsonObject.get("refresh_token").toString().replace("\"", ""));
            retDto.setExpireIn(Long.parseLong(jsonObject.get("expires_in").toString()));
            retDto.setScope(jsonObject.get("scope").toString().replace("\"", ""));
            retDto.setTokenType(jsonObject.get("token_type").toString().replace("\"", ""));

            // 받아온 토큰을 데이터베이스에 저장한다.
            retDto.setTokenId("cts-api");
            this.authService.saveSwitToken(retDto);
        } catch (Exception e) {
            log.error(e.getMessage());
            return ResponseEntity.internalServerError().body(e.getMessage());
        }
        return ResponseEntity.ok(retDto);
    }
```
* 결과값으로 다음과 같이 데이터베이스에 저장 된 것을 확인할 수 있습니다.
[![3iOZQdAMnnRXJG0b-2023-10-12-11-15-35.png](https://tech-support.swit.io/uploads/images/gallery/2024-01/scaled-1680-/HToqcJeFAhJI4d33-3iozqdamnnrxjg0b-2023-10-12-11-15-35.png)](https://tech-support.swit.io/uploads/images/gallery/2024-01/HToqcJeFAhJI4d33-3iozqdamnnrxjg0b-2023-10-12-11-15-35.png)
## 4.2. 인증용 토큰 가져오기
* 데이터베이스에 저장한 인증키를 가져옵니다.
```java
 /**
     * DB에 저장된 토큰정보 가져오기
     * @param tokenId 가져올 토큰 아이디
     * @return 토큰 정보
     * @throws Exception 오류정보
     */
    public SwitTokenDto getSwitToken(String tokenId) throws Exception {
        QSwitTokenEntity sut = new QSwitTokenEntity("sut");
        JPAQuery<SwitTokenEntity> query = queryFactory.selectFrom(sut);
        SwitTokenDto tokenDto = new SwitTokenDto();

        BooleanBuilder where = new BooleanBuilder();
        where.and(sut.tokenId.eq(tokenId));

        tokenDto = query.select(
                Projections.bean(SwitTokenDto.class,
                        sut.tokenId,
                        sut.accessToken,
                        sut.tokenType,
                        sut.expireIn,
                        sut.scope,
                        sut.refreshToken
                )
        ).where(where).fetchFirst();
        return tokenDto;
    }

```
* 받아온 토큰정보를 header에 넣어 사용하시면 됩니다.

# RestfulAPI를 호출하기 위한 Class 및 Method 생성

# 1. 시작하며
* 반복되는 호출작업의 편의성을 위해  공통 클래스 및 메서드를 생성합니다.
* 프로비저닝에서 PUT, DELETE는 쓰이지 않으므로, POST와 GET의 두가지 Method를 만듭니다.
* Token관련 부분은 [토큰 저장](http://34.64.168.32:8000/books/swit-java-development-guide/page/c4100) 챕터를 확인하여 주시기 바랍니다.
* 각 클래스에서 궁금한 내용은 Swit CTS의 깃허브를 참조해 주시기 바랍니다.
# 2. 예제 코드

```JAVA
package io.swit.api.util;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.swit.api.config.Variables;
import io.swit.api.model.dto.eai.SwitTokenDto;
import io.swit.api.service.com.LogService;
import io.swit.api.service.eai.AuthService;
import lombok.AllArgsConstructor;
import org.json.simple.JSONObject;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;

import java.time.LocalDateTime;

@Component
@AllArgsConstructor
public class HttpUtils{

    private Variables var;
    private AuthService authService;
    private LogService logService;

    /**
     * POST 호출을 위한 Method
     * @param apiUrl 호출 URL
     * @param params 파라미터
     * @param tokenDto 토큰 정보
     * @return
     * @throws Exception
     */
    public JsonObject post(String apiUrl, JSONObject params, SwitTokenDto tokenDto) throws Exception {
        try{
            // API 호출을 위해 WebClient 설정
            WebClient webClient = WebClient.builder()
                    .baseUrl(String.format("%s/v1/api/%s", var.API_URL, apiUrl))
                    .defaultHeaders(httpHeaders -> {
                        httpHeaders.set("Accept", "application/json");
                        httpHeaders.set(
                          "Authorization",
                          String.format("Bearer %s", tokenDto.getAccessToken().replace("\"", ""))
                        );
                        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
                    })
                    .build();
            // API 호출 및 결과 수신
            String retVal =  webClient.post()
                    .bodyValue(params)
                    .retrieve()
                    .bodyToMono(String.class)
                    .block();

            // 수신된 결과를 JSON Object로 변환
            JsonParser parser = new JsonParser();
            JsonObject jsonObject = new JsonObject();
            if(retVal != null) {
                Object obj = parser.parse(retVal);
                jsonObject = (JsonObject) obj;
            }
            return jsonObject;
        }catch (Exception e){
            if(e.getMessage().contains("Unauthorized")){
                return this.post(apiUrl,params,authService.getRefreshToken());
            }else{
                logService.saveErrorLog(e, "[POST]"+apiUrl);
                throw new Exception(e);
            }
        }
    }

    /**
     * Get Method
     * @param apiUrl API 주소
     * @param tokenDto 토큰 정보
     * @return
     * @throws Exception
     */
    public JsonObject get(String apiUrl, SwitTokenDto tokenDto) throws Exception {
        try{
            // 전송을 위해 WebClient 설정
            WebClient webClient = WebClient.builder()
                    .baseUrl(String.format("%s/v1/api/%s", var.API_URL, apiUrl))
                    .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
                    .build();

            // API 호출 및 결과 수신
            String retVal =  webClient.get()
                    .headers(
                            httpHeaders -> {
                                httpHeaders.set("Accept", "application/json");
                                httpHeaders.set(
                                  "Authorization",
                                  String.format("Bearer %s", tokenDto.getAccessToken().replace("\"", ""))
                                );
                            }
                    )
                    .retrieve()
                    .bodyToMono(String.class)
                    .block();

            // 수신한 결과를 JSON Object로 변환
            JsonParser parser = new JsonParser();
            Object obj = parser.parse(retVal);
            JsonObject jsonObject = (JsonObject)obj;

            return jsonObject;
        }catch (Exception e){
            if(e.getMessage().contains("Unauthorized")){
                return this.get(apiUrl,authService.getRefreshToken());
            }else{
                logService.saveErrorLog(e, "[GET]"+apiUrl);
                throw new Exception(e);
            }
        }
    }
}
```
# 3. 코드 설명
```java
// API 호출을 위해 WebClient 설정
WebClient webClient = WebClient.builder()
        .baseUrl(String.format("%s/v1/api/%s", var.API_URL, apiUrl))
        .defaultHeaders(httpHeaders -> {
            httpHeaders.set("Accept", "application/json");
            httpHeaders.set("Authorization", String.format("Bearer %s", tokenDto.getAccessToken().replace("\"", "")));
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        })
        .build();
```
* Webflux의 WebClient를 이용하기 위해 호출할 URL과 인증 정보를 설정해 줍니다.
* 헤더에 Bearer 토큰으로 저장한 Access Token을 삽입하고, 매체유형은 JSON으로 설정해 줍니다.

# 프로비저닝



# 개요

# 시작하며
> **프로비저닝은 IT 인프라를 생성하고 설정하는 프로세스 입니다.** 이 예제에서는 회사의 조직도와 Swit의 조직도를 연동하는 과정을 설명합니다.
>
> 가이드에 앞서 **[시작 및 준비](http://34.64.168.32:8000/books/swit-java-development-guide/chapter/072b0)** 를 먼저 해주시길 바랍니다.
> 
> API호출시 사용되는 get 및 post 는 **[RestfulAPI](http://34.64.168.32:8000/books/swit-java-development-guide/page/restfulapi-class-method)** 를 참조해주세요.
---
# 프로비저닝 과정

## 조직도 생성
<div drawio-diagram="13"><img src="https://tech-support.swit.io/uploads/images/drawio/2024-01/R0K0OjdwKjhuv9MP-drawing-5-1704686606.png"></div>

## 조직원 구성
<div drawio-diagram="14"><img src="https://tech-support.swit.io/uploads/images/drawio/2024-01/VGlALaSODicLYaco-drawing-5-1704686626.png"></div>

## 조직 부서 제외


<!-- ## 부서 이동
<div drawio-diagram="18"><img src="http://34.64.168.32:8000/uploads/images/drawio/2023-10/AYR8OkK19uu2yN37-drawing-5-1697181890.png"></div> -->

# 팀 생성

# Swit Team 정보 생성
---
* Swit 의 Team API를 이용하여 팀을 생성 합니다.
* 상위조직의 아이디는 필수값이므로 상위조직부터 생성하시길 바랍니다.
* 최상위 조직을 등록하실땐 parent_id를 0으로 주시면 됩니다.
---
``` JAVA
package io.swit.api.service.eai;

@Service
@Slf4j
@AllArgsConstructor
public class TeamService {
   public String createSwitTeam(SwitDeptDto team) throws Exception{
        String teamId = "";
        JSONObject params = new JSONObject();
        try {
            params.put("name", team.getTeamName());
            params.put("parent_id", team.getParentId());
            params.put("reference", team.getReference());

            SwitTokenDto tokenDto = authService.getSwitTokenByDb("cts-api");
            JsonObject result = httpUtils.post("team.create", params, tokenDto);
            JsonElement data = result.getAsJsonObject().get("data");
            SwitDeptDto switDept = new SwitDeptDto();
            switDept = this.parsingTeamResult(data);
            teamId = this.saveSwitDeptBySync(switDept);
        } catch (Exception e) {
            log.error(e.getMessage());
        }

        return teamId;
    }
}
```

# 팀 리스트 불러오기

# Swit Team 리스트
---
* Swit 의 Team List API를 이용하여 팀 목록을 불러옵니다.
* 팀 데이터 안에 소속된 유저의 아이디 배열이 포함되어 있습니다.
---
``` JAVA
public List<SwitDeptDto> getSwitTDeptByAPI() throws Exception {
        List<SwitDeptDto> list = new ArrayList<>();
        try {

            SwitTokenDto tokenDto = authService.getSwitTokenByDb("cts-api");
            JsonObject jsonObject = httpUtils.get("user.team.list", tokenDto) ;
            JsonElement data = jsonObject.get("data");

            // 유저목록 가져오기
            JsonArray teams = data.getAsJsonObject().get("team").getAsJsonArray();
            for (JsonElement team: teams) {
                SwitDeptDto teamDto = new SwitDeptDto();
                teamDto.setTeamId(team.getAsJsonObject().get("team_id").toString().trim().replaceAll("\"", ""));
                teamDto.setTeamName(team.getAsJsonObject().get("team_name").toString().trim().replaceAll("\"", ""));
                teamDto.setParentId(team.getAsJsonObject().get("parent_id").toString().trim().replaceAll("\"", ""));
                teamDto.setDepth(Integer.parseInt(team.getAsJsonObject().get("depth").toString().trim()));
                teamDto.setMemberCnt(Integer.parseInt(team.getAsJsonObject().get("member_cnt").toString().trim()));
                teamDto.setReference(team.getAsJsonObject().get("reference").toString().trim().replaceAll("\"",""));
                //팀 사용자 정보
                JsonArray users = team.getAsJsonObject().get("users").getAsJsonArray();
                List<String> userList = new ArrayList<>();
                for (int i = 0; i < users.size(); i++) {
                    userList.add(users.get(i).toString().trim());
                }
                teamDto.setUsers(userList);
                list.add(teamDto);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return list;
    }
```

# 사용자 등록

# Swit User 생성
---
* Swit 의 User API를 이용하여 사용자를 등록 합니다.
* 소속팀 정보는 유저를 생성후 유저아이디를 가지고 생성되어 있는 팀에 추가 시켜줘야 합니다.
---
``` JAVA
public HrUserDto createSwitUser(SwitUserDto user) throws Exception {
    HrUserDto hrUserDto = new HrUserDto();
    try {
        // 유저생성 파라미터를 설정한다.
        JSONObject params = new JSONObject();
        params.put("language", user.getLanguage());
        params.put("timezone", user.getTimezone());
        params.put("user_name", user.getUserName());
        params.put("user_email", user.getEmail());
        params.put("tel", user.getTel());
        params.put("msg", user.getMsg());


        SwitTokenDto tokenDto = authService.getSwitTokenByDb("cts-api");
        JsonObject result = httpUtils.post("organization.user.create", params, tokenDto);

        // 팀에 유저 추가 및 메인팀 설정 (Team에 유저 등록하면 main_dept_yn 자동 Y)
        List<String> userIds = new ArrayList<String>();
        userIds.add(result.get("data").getAsJsonObject().get("user").getAsJsonObject().get("user_id").getAsString());
        teamService.addUserToSwitTeam(user.getTeamId(), userIds); // return값 없음
    } catch (Exception e) {
        log.error(e.getMessage());
        throw new Exception(e);
    }
    return hrUserDto;
}
```
---
##### 팀에 유저추가 및 메인팀 설정은 **[사용자 팀 설정](https://tech-support.swit.io/books/swit-java-development-guide/page/05f97)** 을 참고 해 주세요.

# 사용자 팀 설정

# Swit User 팀 등록
---
* Swit 의 Team API를 이용하여 등록한 유저들을 팀에 추가 합니다.
* 소속된 팀이 없는 유저를 처음 추가하면 Main 부서로 등록되게 됩니다.
---
``` JAVA
public SwitDeptDto addUserToSwitTeam(String teamId, List<String> userIds) throws Exception{
    SwitDeptDto retVal = new SwitDeptDto();
    JSONObject params = new JSONObject();
    try {
        JSONArray userList = new JSONArray();
        for(String id : userIds)
            userList.add(id);
        params.put("user_ids", userList);
        params.put("id", teamId);
        SwitTokenDto tokenDto = authService.getSwitTokenByDb("cts-api");

        JsonObject result = httpUtils.post("team.user.add", params, tokenDto);
        JsonElement data = result.getAsJsonObject().get("data").getAsJsonObject().get("data");
        retVal = this.parsingTeamResult(data);
    } catch (Exception e) {
        log.error(e.getMessage());
        throw new Exception(e);
    }
    return retVal;
}
```

# 사용자 리스트 불러오기

# Swit User 리스트
---
* ##### Swit 의 User API를 이용하여 등록한 유저들의 리스트를 불러옵니다.
* ##### cnt(한페이지에 불러올 데이터의 갯수) 는 필수값입니다.
---
``` JAVA
public List<SwitUserDto> getSwitUsersByAPI() throws Exception {
    List<SwitUserDto> list = new ArrayList<>();
    try {
        //유저 정보를 가져온다.
        SwitTokenDto tokenDto = authService.getSwitTokenByDb("cts-api");
        int page = 1;

        boolean isFinsished = true;

        while (isFinsished) {
            //100개씩 묶어서 처리한다.
            JsonObject jsonObject = httpUtils.get("organization.user.list?cnt=100&page=" + page, tokenDto) ;
            JsonElement data = jsonObject.get("data");
            JsonArray users = data.getAsJsonObject().get("users").getAsJsonArray();

            int totalRows = 0;
            totalRows = data.getAsJsonObject().get("total_rows").getAsInt();

            if (users.isEmpty() || list.size() >= totalRows) {
                isFinsished = false;
            } else {
                // 유저목록 가져오기
                for (JsonElement user : users) {
                    list.add(this.switUserMappingByJson(user));
                }
                page += 1;

            }

        }
    } catch (Exception e) {
        log.error(e.getMessage());
    }
    return list;
}
```
---
##### 유저목록을 가져올때 Json 에서 객체로 매핑하는 함수는 **[여기](https://tech-support.swit.io/books/swit-java-development-guide/page/by-json)** 를 참고해 주세요

# 스케줄러 를 이용한 배치

## 기본 개념
#### 1. 스케줄러 : 일정한 시간 또는 시각에 특정 로직을 실행
#### 2. 배치 : 실시간이 아닌 데이터의 일괄처리
---
## SpringBoot에서 Quartz를 이용
#### 1. dependencies 에 Quartz 추가
    implementation 'org.springframework.boot:spring-boot-starter-quartz'

#### 2. Application Class에 스케줄링
    @SpringBootApplication
    @EnableScheduling       // <------ 추가
    public class CtsApiJavaApplication {
    	public static final String APPLICATION_LOCATIONS = "spring.config.location="
			+ "classpath:environment.yml,"
			+ "classpath:application.yml";

    	public static void main(String[] args) {
    		new SpringApplicationBuilder(CtsApiJavaApplication.class)
				.properties(APPLICATION_LOCATIONS)
				.run(args);
    	}

    }

#### 3. Job 작성
``` JAVA
package io.swit.api.schedule.job;

import io.swit.api.service.com.LogService;
import io.swit.api.service.eai.SyncService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Map;

@Slf4j
@AllArgsConstructor
public class SyncScheduleJob implements Job {  // <------ Job implements
    private SyncService syncService;
    private LogService logService;

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {  // <------ execute 메서드에 수행업무 작성
        //조직 및 구성원 동기화
        try {
            log.info("조직 및 구성원 동기화 시작");
            Map<String,Integer> result = syncService.syncSwitTeams();
            log.info(String.format("%d조직 및 %d구성원 동기화 성공",result.get("team"),result.get("teamUser")));
        } catch (Exception e) {
            log.error("조직 및 구성원 동기화 실패 : " + e.getMessage());
            try {
                logService.saveErrorLog(e, "TeamBatch");
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        } finally {
            log.info("조직 및 구성원 동기화 종료");
        }

        //유저정보 동기화
        try{
            log.info("유저 및 팀 유저 동기화 시작");
            Map<String,Integer> result = syncService.syncSwitUsers();
            log.info(String.format("%d명 유저 및 %d명 팀 유저 동기화 성공",result.get("user"),result.get("teamUser")));
        }catch(Exception e){
            log.error("유저 및 팀 유저 동기화 실패 : " + e.getMessage());
            try {
                logService.saveErrorLog(e, "UserBatch");
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }finally {
            log.info("유저 및 팀 유저 동기화 종료");
        }
    }
}

```

#### 4. Schedule 작성
``` JAVA
package io.swit.api.schedule.config;

import io.swit.api.schedule.job.SyncScheduleJob;
import lombok.RequiredArgsConstructor;
import org.quartz.*;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.annotation.PostConstruct;
import java.util.TimeZone;

@Configuration
@RequiredArgsConstructor
public class SyncSchedule {
    private final SchedulerFactoryBean schedulerFactoryBean;  // <------ 스케줄러빈 사용

    @PostConstruct
    public void scheduled() throws SchedulerException {
        JobDetail syncProvosioningJob = jobDetail("syncTeamUser", "Provisioning");
        CronTrigger syncProvisioningTrigger = trigger("scheduleTrigger", "trigger-group", "0 0 0/1 1/1 * ? *");
        this.schedulerFactoryBean.getScheduler().scheduleJob(syncProvosioningJob, syncProvisioningTrigger);
    }
    private JobDetail jobDetail(String name, String group){  // <------ 작성해놓은 Job class 로 JobDeail 생성
        JobDetail job = JobBuilder.newJob(SyncScheduleJob.class)
                .build();
        return job;
    }

    private CronTrigger trigger(String name, String group, String cronExpression) {  // <------ 트리거 생성 ( 작성자는 크론표현식을 사용하여 크론 트리거 생성 )

        CronTrigger trigger = TriggerBuilder.newTrigger()
                .startNow()
                .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression).inTimeZone(TimeZone.getTimeZone("Asia/Seoul")))
                .build();
        return trigger;
    }
}

```

# 유저 정보 매핑 ( by Json )

## Json타입의 유저정보 객체로 매핑
* #### Json타입으로 받은 객체정보를 Java Dto 로 매핑
---
``` JAVA
private SwitUserDto switUserMappingByJson(JsonElement user) throws Exception{
    SwitUserDto retVal = new SwitUserDto();//사용자 정보
    List<SwitDeptDto> teams = new ArrayList<>();    //사용자 조직 정보

    // 사용자 정보를 가져 온다.
    retVal.setUserId(user.getAsJsonObject().get("user_id").getAsString());      //사용자 아이디
    retVal.setUserName(user.getAsJsonObject().get("user_name").getAsString());  //사용자 이름
    retVal.setEmail(user.getAsJsonObject().get("email").getAsString()); //이메일
    retVal.setRole(user.getAsJsonObject().get("role").getAsInt());  //역할코드
    retVal.setStatus(user.getAsJsonObject().get("status").getAsString());   //상태
    retVal.setMode(user.getAsJsonObject().get("mode").getAsString());   //모드?
    retVal.setTel(user.getAsJsonObject().get("tel").getAsString()); //전화번호
    retVal.setTimezone(user.getAsJsonObject().get("timezone").getAsString());   //타입존
    retVal.setTimezoneAutoFlag(user.getAsJsonObject().get("timezone_auto_flag").getAsInt());    //타임존 자동 여부
    retVal.setMsg(user.getAsJsonObject().get("msg").getAsString());
    retVal.setBgColor(user.getAsJsonObject().get("bg_color").getAsString());    //배경색
    retVal.setPhoto(user.getAsJsonObject().get("photo").getAsString());     //프로필 이미지
    retVal.setActive(user.getAsJsonObject().get("is_active").getAsBoolean());   //활성화 여부
    retVal.setLanguage(user.getAsJsonObject().get("language").getAsString().trim());    //사용언언
    // 날짜 포맷을 맞춰 준다.
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    if (StringUtils.hasText(user.getAsJsonObject().get("created").getAsString())) {
        Date createdDate = sdf.parse(user.getAsJsonObject().get("created").getAsString().trim().replace("T", " ").replace("Z", ""));
        LocalDateTime created = LocalDateTime.ofInstant(createdDate.toInstant(), ZoneId.of("Asia/Seoul"));
        retVal.setCreated(created);
    }
    if (StringUtils.hasText(user.getAsJsonObject().get("last_activity").getAsString())) {
        Date lastActivityDate = sdf.parse(user.getAsJsonObject().get("last_activity").getAsString().trim().replace("T", " ").replace("Z", ""));
        LocalDateTime lastActivity = LocalDateTime.ofInstant(lastActivityDate.toInstant(), ZoneId.of("Asia/Seoul"));
        retVal.setLastActivity(lastActivity);
    }

    // 팀 정보
    if (user.getAsJsonObject().get("team") != null && !user.getAsJsonObject().get("team").toString().equals("null")) {
        JsonArray userTeams = user.getAsJsonObject().get("team").getAsJsonArray();
        for (JsonElement team : userTeams) {
            SwitDeptDto teamDto = new SwitDeptDto();
            teamDto.setTeamId(team.getAsJsonObject().get("team_id").getAsString());
            teamDto.setTeamName(team.getAsJsonObject().get("team_name").getAsString());
            teamDto.setMainDeptYn(team.getAsJsonObject().get("main_dept_yn").getAsString());
            teams.add(teamDto);
            //메인부서일 경우 등록해 준다.
            if (StringUtils.hasText(teamDto.getMainDeptYn())
                    && "Y".equalsIgnoreCase(teamDto.getMainDeptYn())) {
                retVal.setTeamId(teamDto.getTeamId());
                retVal.setTeamName(teamDto.getTeamName());
            }
        }
    }
    retVal.setTeam(teams);
    return retVal;
}
```