feat: init

dev
gitea-관리자 2 years ago
commit 5bb9354b43

35
.gitignore vendored

@ -0,0 +1,35 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
/.idea/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

Binary file not shown.

@ -0,0 +1,18 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.2/apache-maven-3.9.2-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar

@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

@ -0,0 +1,171 @@
### API 가이드
[카카오페이 문서발송 단건](./document/카카오페이내문서함_1.문서발송(단건).pdf)
[카카오페이 문서발송 대량](./document/카카오페이내문서함_1.문서발송(대량).pdf)
[카카오페이 문서발송 네트워크가이드](./document/카카오페이내문서함_1.네트워크가이드.pdf)
### swagger
[API URL](http://localhost:8081/swagger-ui.html)
[Front URL](http://localhost:8080/swagger-ui.html)
[Front test page](http://localhost:8080/api/kakaopay/test)
### API 결과 수신
* 정상 수신
```java
public class ApiResponseDTO<T> implements Serializable {
private static final String FAIL_STATUS = "fail";
private static final String ERROR_STATUS = "error";
@Schema(example = "true", description = "에러인 경우 false", requiredMode = Schema.RequiredMode.REQUIRED)
private boolean success;
@Schema(example = " ", description = "HttpStatus.OK", requiredMode = Schema.RequiredMode.REQUIRED)
private String code;
@Schema(description = "결과 데이타, 오류시 null", example = " ")
private T data;
@Schema(description = "오류 발생시 오류 메세지", example = " ", requiredMode = Schema.RequiredMode.AUTO)
@Setter
private String message;
@Schema(example = " ", description = "HttpStatus.OK", requiredMode = Schema.RequiredMode.AUTO)
private HttpStatus httpStatus;
@Schema(description = "API 실행 결과 데이타 수")
private int count;
}
```
* 정상 수신
```json
{
"success": true,
"code": "200",
"httpStatus": "OK",
"message": "성공했습니다.",
"data": {
"token_status": "USED",
"token_expires_at": 1624344762,
"token_used_at": 0,
"doc_box_sent_at": 0,
"doc_box_received_at": 0,
"authenticated_at": 0,
"user_notified_at": 0,
"payload": "payload 파라미터 입니다.",
"signed_at": 0
},
"count": 1,
"paginationInfo": null
}
```
* 에러 수신
```json
{
"success": false,
"code": "error",
"data": null,
"message": "로그인 정보가 올바르지 않습니다.",
"httpStatus": "BAD_REQUEST",
"count": 0,
"paginationInfo": null
}
```
* API 호출 결과가 서버등(네트웍장애)의 장애인 경우를 제외 하고
예외로 return 되는 경우는 없다(발생시 공통팀에 반드시 알려 줄 것)
```js
$.ajax({
url: url,
type: method,
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify(data),
beforeSend: (xhr) => {
//xhr.setRequestHeader(header, token);
$("#loading").show();
},
success: function (res, textStatus) {
console.log( JSON.stringify(res));
if(res.success){
//정상 응답
$("#resData").text(res.data)
}else{
//에러 응답
$("#errData").text(JSON.stringify(res));
}
},
error : function(data) {
// 여기로 오는 경우 공통팀에 알려 주세요
alert("점검필요-error로 return", data.responseText);
},
complete: () => {
$("#loading").hide();
}
});
```
### API(Restful call) validation
* Controller 단에서 @Validated 사용으로 처리 가능
* But, 이경우 API 로그를 남기기 위해 Service 단에서 체크 하도록 컨트롤러 단에서는 유효성 체크 skip
### spring validation
```text
@Valid는 Java, @Validated는 Spring에서 지원하는 어노테이션
@Validated@Valid의 기능을 포함하고, 유효성을 검토할 그룹을 지정할 수 있는 기능이 추가됨
```
```java
@Null // null만 혀용
@NotNull // null을 허용하지 않습니다. "", " "는 허용
@NotEmpty // null, ""을 허용하지 않습니다. " "는 허용
@NotBlank // null, "", " " 모두 허용하지 않습니다.
@Email // 이메일 형식을 검사합니다. 다만 ""의 경우를 통과 시킵니다
@Pattern(regexp = ) // 정규식을 검사할 때 사용됩니다.
@Size(min=, max=) // 길이를 제한할 때 사용됩니다.
@Max(value = ) // value 이하의 값을 받을 때 사용됩니다.
@Min(value = ) // value 이상의 값을 받을 때 사용됩니다.
@Positive // 값을 양수로 제한합니다.
@PositiveOrZero // 값을 양수와 0만 가능하도록 제한합니다.
@Negative // 값을 음수로 제한합니다.
@NegativeOrZero // 값을 음수와 0만 가능하도록 제한합니다.
@Future // 현재보다 미래
@Past // 현재보다 과거
@AssertFalse // false 여부, null은 체크하지 않습니다.
@AssertTrue // true 여부, null은 체크하지 않습니다.
```
### intellij devtools 활성
```text
1. IntelliJ - Preferencs…
2. 컴파일러 - build project automatically(프로젝트 자동 빌드) 체크
3. Advanced Settings > Compiler
Allow auto-make to start even if developed application is current running
(개발된 애플리케이션이 현재 실행 중인 경우에도 auto-make가 시작되도록 허용) 체크
# 1 ~ 3항 까지 설정후 에도 안되는 경우만 4번 설정
4. 서버설정 : Edit Configurations...
Modfy Options > On Update Action > Update Resources
```
### ens-api 배포 및 run : profile에 따라 local|dev|prod
```shell
# jdk : azul-17.0.1
# 프로젝트 root 폴더로 이동 : ens-parent
# 패키지 생성 : local|dev|prod
$ mvnw clean package -P local
# 실행 : 프로젝트폴더/ens-parent/ens-api/target에 생성된 jar파일 실행
$ c:\tools\java\azul-17.0.1\java -jar -Dspring.profiles.active=local .\ens-api.jar
# mvn 명령어 설명
# -pl [모듈명] : 모듈명의 프로젝트만 빌드
# -am : 의존성 있는 프로젝트 함께 빌드 - C가 A를 디펜던시로 가지고 있으며 C를 빌드하면 A -> C 순으로 빌드
$ mvnw clean package -pl ens-api -am -P local
# -amd : 의존성 있는 타 프로젝트 빌드 - C가 A를 디펜던시로 가지고 있는 경우 A를 빌드 하면 A -> C 순으로 빌드
$ mvnw clean package -pl egov-core -amd -P local
```
### 스프링 배치 DB schema
[mysql DDL 스크립트](./document/batch-schema-mysql.sql)

@ -0,0 +1,101 @@
-- Autogenerated: do not edit this file
CREATE TABLE BATCH_JOB_INSTANCE (
JOB_INSTANCE_ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT ,
JOB_NAME VARCHAR(100) NOT NULL,
JOB_KEY VARCHAR(32) NOT NULL,
constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_EXECUTION (
JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT ,
JOB_INSTANCE_ID BIGINT NOT NULL,
CREATE_TIME DATETIME(6) NOT NULL,
START_TIME DATETIME(6) DEFAULT NULL ,
END_TIME DATETIME(6) DEFAULT NULL ,
STATUS VARCHAR(10) ,
EXIT_CODE VARCHAR(2500) ,
EXIT_MESSAGE VARCHAR(2500) ,
LAST_UPDATED DATETIME(6),
JOB_CONFIGURATION_LOCATION VARCHAR(2500) NULL,
constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID)
references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_EXECUTION_PARAMS (
JOB_EXECUTION_ID BIGINT NOT NULL ,
TYPE_CD VARCHAR(6) NOT NULL ,
KEY_NAME VARCHAR(100) NOT NULL ,
STRING_VAL VARCHAR(250) ,
DATE_VAL DATETIME(6) DEFAULT NULL ,
LONG_VAL BIGINT ,
DOUBLE_VAL DOUBLE PRECISION ,
IDENTIFYING CHAR(1) NOT NULL ,
constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID)
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_STEP_EXECUTION (
STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT NOT NULL,
STEP_NAME VARCHAR(100) NOT NULL,
JOB_EXECUTION_ID BIGINT NOT NULL,
START_TIME DATETIME(6) NOT NULL ,
END_TIME DATETIME(6) DEFAULT NULL ,
STATUS VARCHAR(10) ,
COMMIT_COUNT BIGINT ,
READ_COUNT BIGINT ,
FILTER_COUNT BIGINT ,
WRITE_COUNT BIGINT ,
READ_SKIP_COUNT BIGINT ,
WRITE_SKIP_COUNT BIGINT ,
PROCESS_SKIP_COUNT BIGINT ,
ROLLBACK_COUNT BIGINT ,
EXIT_CODE VARCHAR(2500) ,
EXIT_MESSAGE VARCHAR(2500) ,
LAST_UPDATED DATETIME(6),
constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID)
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT (
STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
SHORT_CONTEXT VARCHAR(2500) NOT NULL,
SERIALIZED_CONTEXT TEXT ,
constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID)
references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT (
JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
SHORT_CONTEXT VARCHAR(2500) NOT NULL,
SERIALIZED_CONTEXT TEXT ,
constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID)
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_STEP_EXECUTION_SEQ (
ID BIGINT NOT NULL,
UNIQUE_KEY CHAR(1) NOT NULL,
constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;
INSERT INTO BATCH_STEP_EXECUTION_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_STEP_EXECUTION_SEQ);
CREATE TABLE BATCH_JOB_EXECUTION_SEQ (
ID BIGINT NOT NULL,
UNIQUE_KEY CHAR(1) NOT NULL,
constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;
INSERT INTO BATCH_JOB_EXECUTION_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_JOB_EXECUTION_SEQ);
CREATE TABLE BATCH_JOB_SEQ (
ID BIGINT NOT NULL,
UNIQUE_KEY CHAR(1) NOT NULL,
constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;
INSERT INTO BATCH_JOB_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_JOB_SEQ);

Binary file not shown.

@ -0,0 +1,163 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>kr.xit</groupId>
<artifactId>mens-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>mens-api</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>mens-api</name>
<description>Mobile Electronic Notice System API</description>
<dependencies>
<dependency>
<groupId>kr.xit</groupId>
<artifactId>mens-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
<scope>system</scope>
<systemPath>${basedir}/lib/ojdbc6.jar</systemPath>
</dependency>
<!-- ChainedTransactionManager 사용을 위해 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
</dependencies>
<build>
<defaultGoal>install</defaultGoal>
<directory>${basedir}/target</directory>
<finalName>${project.name}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>*</include>
<include>static/**/*</include>
<include>egovframework/**/*</include>
<include>config/application.yml</include>
<include>config/application-app.yml</include>
<include>config/application-ens.yml</include>
<include>config/application-jpa.yml</include>
<include>config/application-${env}.yml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<!-- dependency 추가 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<!-- dependency jar 파일명 -->
<!--finalName>${project.name}-${project.version}</finalName-->
<!-- 기본생성 jar와 dependency jar 가 포함된 각각 파일 생성-->
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- spring-boot-maven-plugin : multi 프로젝트 이므로 mainClass 지정 필요 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
<mainClass>kr.xit.EnsApiApplication</mainClass>
<!-- 외부jar import -->
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,89 @@
package kr.xit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.ApplicationPidFileWriter;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.ComponentScan;
import kr.xit.core.spring.config.support.CustomBeanNameGenerator;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : ens API application main
* ServletComponentScan
* - 릿(, 릿, )
* - WebFilter, WebServlet, WebListener annotaion sacan
* - SpringBoot
* ConfigurationPropertiesScan
* - ConfigurationProperties annotaion class scan
* - EnableConfigurationProperties
* packageName : kr.xit
* fileName : EnsApiApplication
* author : julim
* date : 2023-04-28
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-04-28 julim
*
* </pre>
*/
@Slf4j
@SpringBootApplication
@ConfigurationPropertiesScan(basePackages = {"egovframework", "kr.xit"})
@ServletComponentScan
@ComponentScan(
nameGenerator = CustomBeanNameGenerator.class,
basePackages = {"egovframework", "kr.xit"}
)
public class EnsApiApplication {
static final List<String> basePackages = new ArrayList<>(
Arrays.asList("egovframework", "kr.xit")
);
public static void main(String[] args) {
final String line = "====================================================================";
log.info(line);
log.info("==== EnsApiApplication start :: active profiles - {} ====", System.getProperty("spring.profiles.active"));
if(Objects.isNull(System.getProperty("spring.profiles.active"))) {
log.error(">>>>>>>>>>>>>> Undefined start VM option <<<<<<<<<<<<<<");
log.error(">>>>>>>>>>>>>> -Dspring.profiles.active=local|dev|prd <<<<<<<<<<<<<<");
log.error("============== EnsApiApplication start fail ===============");
log.error(line);
System.exit(-1);
}
log.info(line);
// beanName Generator 등록 : API v1, v2 등으로 분류하는 경우
// Bean 이름 식별시 풀패키지 명으로 식별 하도록 함
CustomBeanNameGenerator beanNameGenerator = new CustomBeanNameGenerator();
beanNameGenerator.addBasePackages(basePackages);
SpringApplicationBuilder applicationBuilder = new SpringApplicationBuilder(EnsApiApplication.class);
applicationBuilder.beanNameGenerator(beanNameGenerator);
SpringApplication application = applicationBuilder.build();
application.setBannerMode(Banner.Mode.OFF);
application.setLogStartupInfo(false);
//TODO : 이벤트 실행 시점이 Application context 실행 이전인 경우 리스너 추가
//PID(Process ID 작성)
application.addListeners(new ApplicationPidFileWriter()) ;
application.run(args);
log.info("=========================================================================================");
log.info("========== EnsApiApplication load Complete :: active profiles - {} ==========", System.getProperty("spring.profiles.active"));
log.info("=========================================================================================");
}
}

@ -0,0 +1,32 @@
package kr.xit.biz.ens.mapper;
import kr.xit.biz.ens.model.EnsDTO;
import org.egovframe.rte.psl.dataaccess.mapper.Mapper;
import java.util.List;
/**
* <pre>
* description :
*
* packageName : kr.xit.biz.ens.mapper
* fileName : IEnsCctvFileMapper
* author : seojh
* date : 2023-07-17
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-17 seojh
*
* </pre>
*/
@Mapper
public interface IEnsCctvFileMapper {
int selectLicense(String jobSeCode);
int insertNtcnCntcData(EnsDTO.NtcnCntcData dto);
List<EnsDTO.EnsNtncCntcSndngTgts> selectEnsNtncCntcSndngs();
int insertCntcSndngMst(EnsDTO.EnsNtncCntcSndngTgts dto);
int insertCntcSndngDtl(EnsDTO.EnsNtncCntcSndngTgts dto);
int updateEnsNtcnCntcData(EnsDTO.EnsNtncCntcSndngTgts dto);
}

@ -0,0 +1,254 @@
package kr.xit.biz.ens.mapper;
import java.util.List;
import java.util.Optional;
import org.egovframe.rte.psl.dataaccess.mapper.Mapper;
import kr.xit.biz.ens.model.CntcDTO;
import kr.xit.biz.ens.model.EnsDTO;
import kr.xit.ens.support.kakao.model.KkopayDocAttrDTO;
import kr.xit.ens.support.kakao.model.KkopayDocBulkDTO;
import kr.xit.ens.support.kakao.model.KkopayDocDTO;
/**
* <pre>
* description :
*
* packageName : kr.xit.biz.ens.mapper
* fileName : ISendMessageLinkMapper
* author : limju
* date : 2023-06-05
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-05 limju
*
* </pre>
*/
@Mapper
public interface ISendMessageLinkMapper {
//----------------------------------------------------------------------
// accept
//----------------------------------------------------------------------
/**
*
* @param t
* @return List<EnsDTO.SndngMssageParam>
*/
<T> List<EnsDTO.SndngMssageParam> selectAcceptTgts(final T t);
/**
*
* @param t
* @return List<EnsDTO.SndngMssageParam>
*/
<T> List<EnsDTO.sndngVali> selectAcceptVali(final T t);
/**
*
* @param t status
* @return int
*/
<T> int insertUnitySndngMst(final T t);
/**
*
* @param t status
* @return int
*/
<T> int insertUnitySndngDtls(final T t);
/**
*
* @param t status, newStatus
* @return int
*/
<T> int updateProcessSttusCntcSndngMst(final T t);
//----------------------------------------------------------------------
// accept
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// make
//----------------------------------------------------------------------
/**
*
* @param t
* @return List<EnsDTO.SndngMssageParam>
*/
<T> List<EnsDTO.SndngMssageParam> selectMakeTgts(final T t);
/**
* 2
* @param t
* @return int
*/
<T> int selectSendOkTgts(final T t);
/**
*
* @param t status
* @return int
*/
<T> int insertSndngMst(final T t);
/**
*
* @param t
* @return int
*/
<T> int insertKakaoMyDocs(final T t);
/**
*
* @param t
* @return int
*/
<T> int insertMobilePageManage(final T t);
/**
*
* @param t
* @return int
*/
<T> int insertSmsSndng(final T t);
/**
*
* @param t
* @return int
*/
<T> int insertPostSndng(final T t);
/**
*
* @param t status, newStatus
* @return int
*/
<T> int updateProcessSttusUnitySndngMst(final T t);
//----------------------------------------------------------------------
// make
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// close
//----------------------------------------------------------------------
List<String> selectCloseTgts(final String sndngPprocessSttus);
//----------------------------------------------------------------------
// close
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// send
//----------------------------------------------------------------------
/**
*
* @param t
* @return List<EnsDTO.SndngMssageParam>
*/
<T> List<EnsDTO.SndngMssageParam> selectSendBulkTgts(final T t);
/**
*
* @param t status
* @return List<EnsDTO.SendKakaoTgt>
*/
<T> List<EnsDTO.SendKakaoTgt> selectKakaoSendTgts(final T t);
/**
* E-GREEN
* @param t status
* @return List<EnsDTO.PostSndng>
*/
<T> List<EnsDTO.PostSndng> selectPostTgts(final T t);
/**
* SMS
* @param t status
* @return List<EnsDTO.SmsSndng>
*/
<T> List<EnsDTO.SmsSndng> selectSmsSendTgts(final T t);
/**
* :
* @param t
* @return EnsDTO.SndngMssageParam
*/
<T> Optional<EnsDTO.SndngMssageParam> selectSndProcessStatus(final T t);
/**
*
* @param t ID, , , ID
* @return int
*/
<T> int updateKakaoSendBulksResult(final T t);
/**
*
* @param t status, newStatus
* @return int
*/
<T> int updateProcessSttusSndngMst(final T t);
//----------------------------------------------------------------------
// send
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// status
//----------------------------------------------------------------------
/**
*
* @param t status, newStatus
* @return int
*/
<T> int updateProcessSttusBulkSndngMst(final T t);
//----------------------------------------------------------------------
// send
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// status
//----------------------------------------------------------------------
/**
*
* @param sndngPprocessSttus
* @return List<String>
*/
List<String> selectKakaoStatusTgts(final String sndngPprocessSttus);
/**
*
* @param dto EnsDTO.KakaoMyDoc
* @return int
*/
//int updateKakaoStatusInfo(final EnsDTO.KakaoMyDoc dto);
int updateKakaoStatusInfo(final KkopayDocBulkDTO.BulkStatus dto);
//----------------------------------------------------------------------
// status
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// result
//----------------------------------------------------------------------
/**
* <pre>
*
* @param dto , , ()/()/ ,
* @return int
* </pre>
*/
int insertCntcSndngResult(final CntcDTO.SndngResult dto);
int updateCntcSndngResult(final CntcDTO.SndngResult dto);
//----------------------------------------------------------------------
// result
//----------------------------------------------------------------------
Optional<EnsDTO.TmplatManage> selectTmplat(final String tmplatId);
EnsDTO.MobilePageManage selectMobilePage(final KkopayDocDTO.OneTimeToken dto);
}

@ -0,0 +1,197 @@
package kr.xit.biz.ens.model;
import java.io.Serializable;
import kr.xit.core.biz.model.AuditFields;
import kr.xit.ens.support.kakao.model.KkopayDocBulkDTO;
import lombok.*;
import lombok.experimental.SuperBuilder;
/**
* <pre>
* description : tb_cntc_ Entity DTO
*
* packageName : kr.xit.biz.ens.model
* fileName : CntcDTO
* author : limju
* date : 2023-06-05
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-05 limju
*
* </pre>
*/
public class CntcDTO {
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@EqualsAndHashCode(callSuper = false)
public static class SndngMst extends AuditFields implements Serializable {
/**
* id
*/
private String unitySndngMastrId;
/**
*
*/
private String signguCode;
/**
*
*/
private String ffnlgCode;
/**
* 릿 ID
*/
private String tmplatId;
/**
*
*/
private String sndngTyCode;
/**
*
*/
private int sndngCo;
/**
*
*/
private String sndngProcessSttus;
/**
*
*/
private String sndngDt;
/**
*
*/
private String closDt;
/**
*
*/
private String errorCode;
/**
*
*/
private String errorMssage;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@EqualsAndHashCode(callSuper = false)
public static class SndngDtl extends AuditFields implements Serializable {
/**
* id
*/
private String unitySndngDetailId;
/**
* id
*/
private String unitySndngMastrId;
/**
*
*/
private String signguCode;
/**
*
*/
private String ffnlgCode;
/**
*
*/
private String mainCode;
/**
*
*/
private String ihidnum;
/**
*
*/
private String moblphonNo;
/**
*
*/
private String nm;
/**
*
*/
private String adres;
/**
*
*/
private String detailAdres;
/**
*
*/
private String zip;
/**
* 릿
*/
private String tmpltMsgData;
/**
*
*/
private String mobilePageCn;
/**
* ID
*/
private String useInsttIdntfcId;
/**
*
*/
private String externalDocumentUuid;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@EqualsAndHashCode(callSuper = false)
public static class SndngResult extends AuditFields implements Serializable {
/**
* id
*/
private String unitySndngDetailId;
/**
*
*/
private String sndngSeCode;
/**
*
*/
private String signguCode;
/**
*
*/
private String ffnlgCode;
/**
*
*/
private String sndngResultSttus;
/**
*
*/
private String requstDt;
/**
*
*/
private String inqireDt;
/**
*
*/
private String readngDt;
/**
*
*/
private String errorCn;
/**
* documentBinderUuid
*/
private String documentBinderUuid;
}
}

@ -0,0 +1,971 @@
package kr.xit.biz.ens.model;
import java.io.Serializable;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import io.swagger.v3.oas.annotations.media.Schema;
import kr.xit.core.biz.model.AuditFields;
import kr.xit.ens.support.common.ApiConstants;
import kr.xit.ens.support.kakao.model.KkopayDocBulkDTO;
import lombok.*;
import lombok.experimental.SuperBuilder;
import javax.validation.Valid;
import javax.validation.constraints.Digits;
import javax.validation.constraints.Size;
/**
* <pre>
* description : tb_ens_ Entity DTO
*
* packageName : kr.xit.biz.ens.model
* fileName : EnsDTO
* author : limju
* date : 2023-06-05
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-05 limju
*
* </pre>
*/
public class EnsDTO {
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public static class SndngMssageParam implements Serializable {
/**
* id
*/
private String unitySndngMastrId;
/**
* id
*/
private String unitySndngDetailId;
/**
* id
*/
private String sndngMastrId;
/**
* ID
*/
private String sndngDetailId;
/**
* 릿ID
*/
private String tmplatId;
/**
*
*/
private int sndngCo;
/**
*
*/
private String sndngProcessSttus;
/**
*
*/
private String newSndngProcessSttus;
private String try1;
private String try2;
private String try3;
private int tryCnt;
private int trySeq;
private String sndngSeCode;
private String sndngDt;
private String sndngDt2;
private String sndngDt3;
private String try2Minute;
private String try3Minute;
private String errorMssage;
private String errorCode;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public static class SendKakaoTgt extends EnsDTO.KakaoMyDoc implements Serializable {
/**
* id
*/
private String sndngMastrId;
/**
* id
*/
private String unitySndngMastrId;
/**
*
*/
private String signguCode;
/**
*
*/
private String ffnlgCode;
/**
* 릿 ID
*/
private String tmplatId;
/**
*
*/
private String sndngTyCode;
/**
*
*/
private int sndngCo;
/**
*
*/
//private String sndngProcessSttus;
/**
*
*/
private long closDt;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@SuperBuilder
public static class UnitySndngMst implements Serializable {
/**
* id
*/
private String unitySndngMastrId;
/**
*
*/
private String signguCode;
/**
*
*/
private String ffnlgCode;
/**
* 릿 ID
*/
private String tmplatId;
/**
*
*/
private String sndngTyCode;
/**
*
*/
private int sndngCo;
/**
*
*/
private String sndngProcessSttus;
/**
*
*/
private String sndngDt1;
/**
* 2
*/
private String sndngDt2;
/**
* 3
*/
private String sndngDt3;
/**
* try1
*/
private String try1;
/**
* try2
*/
private String try2;
/**
* try3
*/
private String try3;
/**
* try_cnt
*/
private int tryCnt;
/**
*
*/
private String closDt;
/**
*
*/
private String errorCode;
/**
*
*/
private String errorMssage;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@EqualsAndHashCode(callSuper = false)
public static class UnitySndngDtl extends AuditFields implements Serializable {
/**
* id
*/
private String unitySndngDetailId;
/**
* id
*/
private String unitySndngMastrId;
/**
*
*/
private String signguCode;
/**
*
*/
private String ffnlgCode;
/**
*
*/
private String mainCode;
/**
*
*/
private String vhcleNo;
/**
*
*/
private String ihidnum;
/**
*
*/
private String moblphonNo;
/**
*
*/
private String nm;
/**
*
*/
private String adres;
/**
*
*/
private String detailAdres;
/**
*
*/
private String zip;
/**
* 릿
*/
private String tmpltMsgData;
/**
*
*/
private String mobilePageCn;
/**
* ID
*/
private String useInsttIdntfcId;
/**
*
*/
private String externalDocumentUuid;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@EqualsAndHashCode(callSuper = false)
public static class TmplatManage extends AuditFields implements Serializable {
/**
* 릿 ID
*/
private String tmplatId;
/**
*
*/
private String signguCode;
/**
*
*/
private String ffnlgCode;
/**
*
*/
private String sndngTyCode;
/**
* 릿
*/
private String tmplatNm;
/**
* 릿
*/
private String tmplatSj;
/**
* 릿
*/
private String tmplatCn;
/**
*
*/
private String cstmrCnterTlphonNo;
/**
* REDIRECT URL
*/
private String redirectUrl;
/**
* try1
*/
private String try1;
/**
* try2
*/
private String try2;
/**
* try3
*/
private String try3;
/**
* try2_minute
*/
private int try2Minute;
/**
* try3_minute
*/
private int try3Minute;
/**
*
*/
private String useAt;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@EqualsAndHashCode(callSuper = false)
public static class KakaoMyDoc extends AuditFields implements Serializable {
/**
* id
*/
private String sndngDetailId;
/**
* id
*/
private String unitySndngDetailId;
/**
* id
*/
private String sndngMastrId;
/**
*
*/
private String signguCode;
/**
*
*/
private String ffnlgCode;
/**
*
*/
private String title;
/**
*
*/
private String hash;
/**
*
*/
private String commonCategories;
/**
*
*/
private double readExpiredAt;
/**
* ci
*/
private String recvCi;
/**
*
*/
private String recvPhoneNumber;
/**
*
*/
private String recvName;
/**
*
*/
private String recvBirthday;
/**
*
*/
private String recvIsRequiredVerifyName;
/**
* URL
*/
private String propLink;
/**
* payload
*/
private String propPayload;
/**
*
*/
private String propMessage;
/**
*
*/
private String propCsNumber;
/**
*
*/
private String propCsName;
/**
*
*/
private String externalDocumentUuid;
/**
*
*/
private String documentBinderUuid;
/**
*
*/
private String errorCode;
/**
*
*/
private String errorMessage;
/**
*
*/
private String docBoxStatus;
/**
*
*/
private String docBoxSentAt;
/**
*
*/
private String docBoxReceivedAt;
/**
*
*/
private String authenticatedAt;
/**
* OTT
*/
private String tokenUsedAt;
/**
*
*/
private String docBoxReadAt;
/**
*
*/
private String userNotifiedAt;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@EqualsAndHashCode(callSuper = false)
public static class PostSndng extends AuditFields implements Serializable {
/**
* id
*/
private String unitySndngMastrId;
/**
* id
*/
private String sndngDetailId;
/**
* id
*/
private String unitySndngDetailId;
/**
* id
*/
private String sndngMastrId;
/**
*
*/
private String signguCode;
/**
*
*/
private String ffnlgCode;
/**
*
*/
private String serviceCd;
/**
*
*/
private String conKey;
/**
*
*/
private String senderNm;
/**
*
*/
private String senderZipNo;
/**
*
*/
private String senderAddr;
/**
*
*/
private String senderDetailAddr;
/**
*
*/
private String receiverSendNo;
/**
*
*/
private String receiverNm;
/**
*
*/
private String receiverZipNo;
/**
*
*/
private String receiverAddr;
/**
*
*/
private String receiverDetailAddr;
/**
* 1
*/
private String sschnge1;
/**
* 2
*/
private String sschnge2;
/**
* 3
*/
private String sschnge3;
/**
*
*/
private String sndngProcessSttus;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class MobilePageManage implements Serializable {
/**
* id
*/
private String sndngDetailId;
/**
*
*/
private String sndngSeCode;
/**
*
*/
private String mobilePageCn;
/**
*
*/
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd kk:mm:ss")
private LocalDateTime registDt;
/**
*
*/
private String register;
}
@Schema(name = "sndngVali", description = "Ac 요청 결과 DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public static class sndngVali {
/* mastr */
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "시군구 코드 (max:5)", example = " ")
@Size(max = 5)
private String signguCode;
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "과태료 코드 (max:2)", example = " ")
@Size(max = 2)
private String ffnlgCode;
/**
* 릿 ID
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "템플릿 ID (max:5)", example = " ")
@Size(max = 5)
private String tmplatId;
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "발송 유형 코드 (max:3)", example = " ")
@Size(max = 3)
private String sndngTyCode;
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "발송 건수 (max:1)", example = " ")
@Digits(integer = 1, fraction = 0, message = "발송 건수는 필수입니다(max:1)")
private int sndngCo;
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "발송 일시 (max:14)", example = " ")
@Size(max = 14)
private String sndngDt;
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "마감 일시 (max:14)", example = " ")
@Size(max = 14)
private String closDt;
/* detail */
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "메인 코드 (max:20)", example = " ")
@Size(max = 20)
private String mainCode;
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "차량 번호 (max:30)", example = " ")
@Size(max = 30)
private String vhcleNo;
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "성명 (max:200)", example = " ")
@Size(max = 200)
private String nm;
/**
* (detail)
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "시군구 코드 detail (max:5)", example = " ")
@Size(max = 5)
private String signguCodeDe;
/**
* (detail)
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "과태료 코드 detail (max:2)", example = " ")
@Size(max = 2)
private String ffnlgCodeDe;
/* other */
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "주민등록번호 (max:100)", example = " ")
@Size(max = 100)
private String ihidnum;
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "핸드폰 번호 (max:20)", example = " ")
@Size(max = 20)
private String moblphonNo;
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "모바일 페이지 내용", example = " ")
private String mobilePageCn;
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "주소 (max:300)", example = " ")
@Size(max = 300)
private String adres;
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "상세 주소 (max:300)", example = " ")
@Size(max = 300)
private String detailAdres;
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "우편번호 (max:5)", example = " ")
@Size(max = 5)
private String zip;
/**
* try1
*/
private String try1;
/**
* try2
*/
private String try2;
/**
* try3
*/
private String try3;
private int trySeq;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class SmsSndng implements Serializable {
/**
* id
*/
private String sndngDetailId;
/**
* id
*/
private String unitySndngDetailId;
/**
* id
*/
private String sndngMastrId;
/**
*
*/
private String signguCode;
/**
*
*/
private String ffnlgCode;
/**
*
*/
private String vhcleNo;
/**
*
*/
private String smsSndngDt;
/**
*
*/
private String smsTrnsmisStle;
/**
*
*/
private String smsTrnsmitTlphonNo;
/**
*
*/
private String smsRecptnTlphonNo;
/**
*
*/
private String smsMssage;
/**
*
*/
private String smsSndngSttus;
/**
*
*/
private String smsSndngProcessSttus;
/**
*
*/
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd kk:mm:ss")
private LocalDateTime registDt;
/**
*
*/
private String register;
private String unitySndngMastrId;
private String sndngProcessSttus;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class NtcnCntcData implements Serializable {
/**
* data id
*/
private String ensCntcDataId;
/**
*
*/
private String regltDt;
/**
*
*/
private String vhcleNo;
/**
*
*/
private String sptNm;
/**
*
*/
private String sptAcctoCode;
/**
*
*/
private String fileNm;
/**
*
*/
private String processAt;
/**
*
*/
private String trgetAt;
/**
* id
*/
private String unitySndngDetailId;
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd kk:mm:ss")
private LocalDateTime registDt;
/**
*
*/
private String register;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public static class EnsNtncCntcSndngTgts implements Serializable {
/**
* data id
*/
private String ensCntcDataId;
/**
*
*/
private String vhcleNo;
/**
*
*/
private String processAt;
/**
*
*/
private String trgetAt;
/**
*
*/
private String moblphonNo;
/**
*
*/
private String nm;
/**
*
*/
private String brthdy;
/**
*
*/
private String zip;
/**
*
*/
private String adres;
/**
*
*/
private String detailAdres;
/**
*
*/
private String regltDt;
/**
*
*/
private String sptNm;
/**
*
*/
private String closDt;
/*모바일 페이지 내용*/
private String mobilePageCn;
/*삭제여부*/
private String deleteAt;
/**
*
*/
private int sndngCo;
/**
*
*/
@Builder.Default
private String sndngTyCode = "ENS";
/**
* FIXME ::
* 릿 ID
*/
private String tmplatId = "JU102";
/**
*
*/
private String tgtYn;
/**
* ID
*/
private String unitySndngMastrId;
/**
* ID
*/
private String unitySndngDetailId;
/**
*
*/
@Builder.Default
private String sndngProcessSttus = ApiConstants.SndngProcessStatus.ACCEPT.getCode();
}
}

@ -0,0 +1,11 @@
/**
* ENS business packages
* <p>
* : ens
* sms
* </p>
* @since 1.0
* @author limju
* @version 1.0
*/
package kr.xit.biz.ens;

@ -0,0 +1,311 @@
package kr.xit.biz.ens.service;
import kr.xit.biz.ens.mapper.IEnsCctvFileMapper;
import kr.xit.biz.ens.model.EnsDTO;
import kr.xit.core.support.utils.Checks;
import kr.xit.core.support.utils.DateUtils;
import kr.xit.core.support.utils.SFTPUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static egovframework.com.cmm.util.EgovDateUtil.formatDate;
import static egovframework.com.cmm.util.EgovDateUtil.formatTime;
/**
* <pre>
* description : CCTV
*
* packageName : kr.xit.biz.ens.service
* fileName : EnsCctvFileService
* author : seojh
* date : 2023-07-17
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-17 seojh
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class EnsCctvFileService extends EgovAbstractServiceImpl implements IEnsCctvFileService {
@Value("${app.ssh.host}") private String host;
@Value("${app.ssh.port}") private int port;
@Value("${app.ssh.id}") private String id;
@Value("${app.ssh.passwd}") private String passwd;
@Value("${app.ssh.sg.ens-path}") private String ensPath;
@Value("${app.ssh.sg.rcv}") private String rcvPath;
@Value("${app.ssh.sg.backup}") private String backupPath;
@Value("${app.ssh.sg.err}") private String errPath;
private final IEnsCctvFileMapper mapper;
/**
* cctv ()
*/
@Override
@Transactional
public void createCctvFileOfSg() {
// 전자 고지 라이선스 유효성 체크
int licenseCnt = mapper.selectLicense("ENS");
if(licenseCnt == 0) return;
SFTPUtils sftp = null;
try {
// SFTP connect
sftp = new SFTPUtils();
sftp.init(host, port, id, passwd, StringUtils.EMPTY);
// 서광 CCTV 전자 고지 대상 조회
final String srcPath = ensPath + rcvPath;
ArrayList<String> fileNameList = sftp.findFileNameList(srcPath);
//if(fileNameList.size() == 0) throw BizRuntimeException.create("사전고지[서광] 처리 대상이 없습니다");
// 에러 파일 목록
ArrayList<String> errFileList = new ArrayList<>();
// 성공 파일 목록
ArrayList<String> rtnFileList = new ArrayList<>();
for (String fn : fileNameList) {
String[] arrFi = fn.split("_");
// 파일 이름 정보 오류시
if (arrFi.length != 4 || arrFi[0].length() != 14) {
errFileList.add(fn);
continue;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FIXME :: GS 임시 소스
// 파일 다운로드
final String downLoadPath = "D:/ImageData/ENS" + "/" + DateUtils.getToday(StringUtils.EMPTY);
File Folder = new File(downLoadPath);
if (!Folder.exists()) Folder.mkdir(); //폴더 생성합니다.
sftp.fileDownload(srcPath + "/" + fn, downLoadPath);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FIXME : 중복데이타(단속시간 + 차량번호)인 경우 데이타 skip 하도록 해야 할 듯
// 전자 고지 연계 데이타 생성
mapper.insertNtcnCntcData(
EnsDTO.NtcnCntcData.builder()
.regltDt(arrFi[0])
.vhcleNo(arrFi[1])
.sptNm(arrFi[2])
.sptAcctoCode(arrFi[3].split("\\.")[0])
.fileNm(fn)
.build()
);
rtnFileList.add(fn);
// FIXME :: sftp rm 기능 추후 제거
// 파일 삭제
sftp.rm(fn);
}
// FIXME :: sftp move 기능 추후 사용
// 처리한 데이타 backup
//fileBackup(sftp, srcPath, errFileList, rtnFileList);
} finally {
if(sftp != null) sftp.disconnect();
}
}
/**
* accept
* 1.
* 2.
* 3.
* 4.
*/
@Override
@Transactional
public void acceptEnsNtnccntcSndng() {
List<EnsDTO.EnsNtncCntcSndngTgts> tgtList = mapper.selectEnsNtncCntcSndngs();
if(tgtList.size() == 0) return;
// search 전자 고지 연계발송 데이타 생성 대상
Optional<EnsDTO.EnsNtncCntcSndngTgts> first = tgtList.stream()
.filter(dto -> "Y".equals(dto.getTgtYn()))
.findFirst();
// 전자 고지 연계발송 master 생성
final String unitySndngMastrId;
final String closDt;
if(first.isPresent()){
EnsDTO.EnsNtncCntcSndngTgts tgtDTO = first.get();
mapper.insertCntcSndngMst(tgtDTO);
unitySndngMastrId = tgtDTO.getUnitySndngMastrId();
closDt = tgtDTO.getClosDt();
MDC.put("unitySndngMastrId", unitySndngMastrId);
} else {
unitySndngMastrId = null;
closDt = null;
}
// 전자 고지 연계발송 상세 생성 및 결과 처리(전자 고지 연계 데이타 반영)
// 전자 고지 대상 -> 전자 고지 연계 발송 상세 생성
// 전자 고지 연계 데이타 -> 연계 결과 반영
// : 대상 : 연계대상-Y, 진행상태-Y, 통합발송상세ID
// 미대상 : 연계대상-N, 진행상태-Y, 통합발송상세ID = null
tgtList.forEach(dto -> {
dto.setProcessAt("Y");
if("Y".equals(dto.getTgtYn())){
dto.setUnitySndngMastrId(unitySndngMastrId);
dto.setClosDt(closDt);
dto.setMobilePageCn(jsonCn(dto));
mapper.insertCntcSndngDtl(dto);
}
mapper.updateEnsNtcnCntcData(dto);
});
}
private void fileBackup(SFTPUtils sftp, String srcPath, ArrayList<String> errFileList, ArrayList<String> rtnFileList) {
final String errorPath = ensPath + errPath;
String destPath = ensPath + backupPath;
destPath = destPath + "/" + DateUtils.getToday(StringUtils.EMPTY);
log.info("src path::[{}]", srcPath);
log.info("tgt path::[{}]", destPath);
// file backup
for (String fn : rtnFileList) {
log.info("fileName::[{}]", fn);
sftp.mv(srcPath + "/" + fn, destPath + "/" + fn);
}
// error file backup
for (String fn : errFileList) {
sftp.mv(srcPath + "/" + fn, errorPath + "/" + fn);
}
}
private String jsonCn(EnsDTO.EnsNtncCntcSndngTgts dto){
String jsonCn = "{"
+ "\"details\": ["
+ "{"
+ "\"title\": \"주정차 위반 과태료 통지서\","
+ "\"item_type\": \"SUBJECT_TEXT\","
+ "\"elements\": ["
+ "\"\""
+ "]"
+ "},"
+ "{"
+ "\"title\": \"위반내역\","
+ "\"item_type\": \"KEY_VALUE\","
+ "\"properties\": {"
+ "},"
+ "\"elements\": ["
+ "{"
+ "\"key\": \"차량번호\","
+ "\"value\": \""+ Checks.checkVal(dto.getVhcleNo(),"") +"\","
+ "\"level\": 1"
+ "},"
+ "{"
+ "\"key\": \"성명\","
+ "\"value\": \""+ Checks.checkVal(dto.getNm(),"") +"\","
+ "\"level\": 1"
+ "},"
+ "{"
+ "\"key\": \"위반일시\","
+ "\"value\": \""+ Checks.checkVal(formatDate(dto.getRegltDt().substring(0,8), "-") + " " + formatTime(dto.getRegltDt().substring(8,14), ":"),"") +"\","
+ "\"level\": 1"
+ "},"
+ "{"
+ "\"key\": \"위반내용\","
+ "\"value\": \"주정차금지 구역\","
+ "\"level\": 1"
+ "},"
+ "{"
+ "\"key\": \"위반장소\","
+ "\"value\": \""+ Checks.checkVal(dto.getSptNm(),"") +"\","
+ "\"level\": 1"
+ "},"
+ "{"
+ "\"key\": \"납부금액\","
+ "\"value\": \"32,000원\","
+ "\"level\": 1"
+ "},"
+ "{"
+ "\"key\": \"단속구분\","
+ "\"value\": \"CCTV\","
+ "\"level\": 1"
+ "},"
+ "{"
+ "\"key\": \"자진납부 기한\","
+ "\"value\": \""+ Checks.checkVal(formatDate(dto.getClosDt().substring(0,8), "-"),"") +"\\\\n위 납부 기한이 경과 시에는 과태료 감경 혜택을 받으실 수 없습니다.\","
+ "\"level\": 1"
+ "}"
+ "]"
+ "},"
+ "{"
+ "\"title\": \"과태료 부과 및 감경\","
+ "\"item_type\": \"TABLE\","
+ "\"elements\": {"
+ "\"head\": ["
+ "\"구분\","
+ "\"승용 감경금액\\\\n(부과금액)\","
+ "\"승합 감경금액\\\\n(부과금액)\""
+ "],"
+ "\"rows\": ["
+ "["
+ "\"주정차금지구역\","
+ "\"32,000원\\\\n(40,000원)\","
+ "\"40,000원\\\\n(50,000원)\""
+ "],"
+ "["
+ "\"같은 장소 2시간 초과\","
+ "\"40,000원\\\\n(50,000원)\","
+ "\"48,000원\\\\n(60,000원)\""
+ "]"
+ "]"
+ "}"
+ "},"
+ "{"
+ "\"title\": \"질서위반행위 규제법 시행령 규정 감경 대상\","
+ "\"item_type\": \"KEY_VALUE\","
+ "\"elements\": ["
+ "{"
+ "\"key\": \"감경대상자\","
+ "\"value\": \"○ 국민기초생활 수급자\\\\n○ 한부모가족 보호대상자\\\\n○ 장애의 정도가 심한 장애인\\\\n○ 국가유공자(상이등급 3급 이상)\\\\n○ 미성년자\","
+ "\"level\": 1"
+ "},"
+ "{"
+ "\"key\": \"시행일\","
+ "\"value\": \"2010년 1월 16일 부터\\\\n○ 적용 : 시행일 이후 단속된 차량\","
+ "\"level\": 1"
+ "},"
+ "{"
+ "\"key\": \"감경율(%)\","
+ "\"value\": \"과태료 부과금액의 50%\","
+ "\"level\": 1"
+ "},"
+ "{"
+ "\"key\": \"비고\","
+ "\"value\": \"※ 자진 납부 시 추가감경 가능\","
+ "\"level\": 1"
+ "}"
+ "]"
+ "}"
+ "]"
+"}";
return jsonCn;
}
}

@ -0,0 +1,28 @@
package kr.xit.biz.ens.service;
/**
* <pre>
* description :
*
* packageName : kr.xit.biz.ens.service
* fileName : IEnsCctvFileService
* author : seojh
* date : 2023-07-17
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-17 seojh
*
* </pre>
*/
public interface IEnsCctvFileService {
/**
* cctv ()
*/
void createCctvFileOfSg();
/**
* accept
*/
void acceptEnsNtnccntcSndng();
}

@ -0,0 +1,41 @@
package kr.xit.biz.ens.service;
import java.util.List;
import kr.xit.biz.ens.model.EnsDTO;
import kr.xit.core.model.ApiResponseDTO;
import kr.xit.ens.support.kakao.model.KkopayDocDTO;
/**
* <pre>
* description :
*
* packageName : kr.xit.biz.ens.service
* fileName : ISendMessageLinkService
* author : limju
* date : 2023-06-05
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-05 limju
*
* </pre>
*/
public interface ISendMessageLinkService {
void accept(final String sndngProcessSttus);
void make(final String sndngProcessSttus);
void close(final String sndngProcessSttus);
void sendBulks(final String sndngProcessSttus);
void findKkoMyDocStatusBulks(final String sndngProcessSttus);
List<EnsDTO.SendKakaoTgt> findKakaoSendTargets(final String sndngProcessSttus);
ApiResponseDTO<?> findKkoMyDocReadyAndMblPage(KkopayDocDTO.OneTimeToken reqDTO);
void updateErrorLog(EnsDTO.SndngMssageParam dto);
}

@ -0,0 +1,882 @@
package kr.xit.biz.ens.service;
import java.io.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import egovframework.com.cmm.util.EgovDateUtil;
import egovframework.com.cmm.util.EgovStringUtil;
import kr.xit.biz.sms.service.ISmsMessageService;
import kr.xit.core.consts.ErrorCode;
import kr.xit.core.support.utils.DateUtils;
import kr.xit.ens.support.kakao.model.KkopayDocDTO;
import kr.xit.ens.support.kakao.service.IKkopayEltrcDocService;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.egovframe.rte.fdl.cryptography.EgovPasswordEncoder;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import kr.xit.biz.ens.mapper.ISendMessageLinkMapper;
import kr.xit.biz.ens.model.CntcDTO;
import kr.xit.biz.ens.model.EnsDTO;
import kr.xit.core.exception.BizRuntimeException;
import kr.xit.core.model.ApiResponseDTO;
import kr.xit.core.support.utils.Checks;
import kr.xit.ens.support.common.ApiConstants;
import kr.xit.ens.support.kakao.model.KkopayDocAttrDTO;
import kr.xit.ens.support.kakao.model.KkopayDocBulkDTO;
import kr.xit.ens.support.kakao.service.IAsyncKkopayEltrcDocService;
import lombok.RequiredArgsConstructor;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
/**
* <pre>
* description : ()
*
* packageName : kr.xit.biz.ens.service
* fileName : SendMessageLinkService
* author : limju
* date : 2023-06-05
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-05 limju
*
* </pre>
*/
@RequiredArgsConstructor
@Service
public class SendMessageLinkService extends EgovAbstractServiceImpl implements ISendMessageLinkService {
private final ISendMessageLinkMapper mapper;
private final EgovPasswordEncoder encryptor;
private final IAsyncKkopayEltrcDocService asyncService;
private final IKkopayEltrcDocService service;
private final ISmsMessageService smsService;
private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
@Value("${contract.provider.kakao.isAsync:false}")
private boolean isAsync;
@Value("${contract.provider.kakao.bulk-max-cnt}")
private int bulkMaxCnt;
@Value("${file.cmm.upload.root}")
private String fileRoot;
@Value("${file.cmm.upload.post}")
private String filePost;
private static final String KKO_MY_DOC = "KKO-MY-DOC";
private static final String E_GREEN = "E-GREEN";
private static final String SMS = "SMS";
private static final String SNDNG_PROCESS_STTUS = "sndngProcessSttus";
private static final String UNITY_SNDNG_MST_ID = "unitySndngMastrId";
private static final String YMDHMS = "yyyyMMddHHmmss";
//-----------------------------------------------------------------------------------------------------------------
// accept
//-----------------------------------------------------------------------------------------------------------------
/**
*
* @param sndngProcessSttus
*/
@Override
@Transactional
public void accept(final String sndngProcessSttus) {
final List<EnsDTO.SndngMssageParam> list = mapper.selectAcceptTgts(sndngProcessSttus);
String sndngDt = null;
for(EnsDTO.SndngMssageParam dto : list) {
final List<String> mstIdList = new ArrayList<>();
String mstId = "";
if(!Objects.equals(mstId, dto.getUnitySndngMastrId())){
mstId = dto.getUnitySndngMastrId();
mstIdList.add(mstId);
MDC.put(UNITY_SNDNG_MST_ID, mstId);
}
// validation check
final List<EnsDTO.sndngVali> valiList = mapper.selectAcceptVali(dto);
try {
validatedAccept(valiList);
}catch (Exception e){
updateAcceptCntcSndngMstFailStatus(mstIdList, "accept 생성 요청 실패(파라메터 오류) : " + EgovStringUtil.cutString(e.getMessage(), 300));
continue;
}
// 초기 1회 셋팅 (발송 일시2, 발송 일시3 : 템플릿 관리 try2_minute, try3_minute 값에 따라 처리)
if(sndngDt == null) {
sndngDt = StringUtils.defaultString(dto.getSndngDt());
if(dto.getTry2() != null && !"".equals(sndngDt)) dto.setSndngDt2(EgovDateUtil.addMinute(sndngDt,Integer.parseInt(dto.getTry2Minute())));
if(dto.getTry3() != null && !"".equals(sndngDt)) dto.setSndngDt3(EgovDateUtil.addMinute(sndngDt,Integer.parseInt(dto.getTry3Minute())));
}
dto.setSndngProcessSttus(sndngProcessSttus);
dto.setNewSndngProcessSttus(ApiConstants.SndngProcessStatus.ACCETP_OK.getCode());
if(mapper.insertUnitySndngMst(dto) != 1) throw BizRuntimeException.create("접수 데이타 생성(마스터) 실패");
int insCnt = mapper.insertUnitySndngDtls(dto);
if(insCnt != dto.getSndngCo()) throw BizRuntimeException.create(String.format("접수 상세 데이타 생성 실패-발송건수[%d]와 생성건수[%d] 불일치", dto.getSndngCo(), insCnt));
if(mapper.updateProcessSttusCntcSndngMst(dto) != 1) throw BizRuntimeException.create("접수 데이타 생성(통합발송마스터 상태변경) 실패");
}
}
/**
* accept
* @param valiList List<EnsDTO.sndngVali>
*/
private void validatedAccept(List<EnsDTO.sndngVali> valiList){
List<String> errors = new ArrayList<>();
int idx = 0;
List<List<EnsDTO.sndngVali>> arrList = ListUtils.partition(valiList, bulkMaxCnt);
for(List<EnsDTO.sndngVali> list: arrList) {
for (EnsDTO.sndngVali dto : list) {
Set<ConstraintViolation<EnsDTO.sndngVali>> errList = validator.validate(dto);
if (list.size() > 0) {
int finalIdx = idx;
errors.addAll(errList.stream()
.map(row -> String.format("%s[%d]=%s", row.getPropertyPath(), finalIdx + 1, row.getMessageTemplate()))
.collect(Collectors.toList())
);
}
// 발송일시보다 마감일시가 빠르면 안되고, 마감일시가 현재 시간보다 빠르면 오류
if(!Checks.isEmpty(dto.getSndngDt()) && !Checks.isEmpty(dto.getClosDt())){
if(!DateUtils.isBeforeLocalDateTime(dto.getSndngDt(),dto.getClosDt(),YMDHMS))
errors.add(String.format("발송일시보다 마감일시가 빠를 수 없습니다(dto.getSndngDt[%d] 번째 오류)", idx+1));
if(!DateUtils.isBeforeLocalDateTime(DateUtils.getTodayAndNowTime(YMDHMS),dto.getClosDt(),YMDHMS))
errors.add(String.format("현재 시간보다 마감일시가 빠를 수 없습니다(dto.getSndngDt[%d] 번째 오류)", idx+1));
}
String[] tryVal = {Checks.checkVal(dto.getTry1(),""), Checks.checkVal(dto.getTry2(),""), Checks.checkVal(dto.getTry3(),"")};
switch (tryVal[dto.getTrySeq()-1]){
case KKO_MY_DOC:
if(Checks.isEmpty(dto.getIhidnum())) errors.add(String.format("주민등록번호는 필수입니다(dto.getIhidnum[%d] 번째 오류)", idx+1));
if(Checks.isEmpty(dto.getMoblphonNo())) errors.add(String.format("핸드폰 번호는 필수입니다(dto.getMoblphonNo[%d] 번째 오류)", idx+1));
if(Checks.isEmpty(dto.getIhidnum())) errors.add(String.format("받는이 이름은 필수입니다(dto.getIhidnum[%d] 번째 오류)", idx+1));
break;
case E_GREEN:
if(Checks.isEmpty(dto.getAdres())) errors.add(String.format("주소는 필수입니다(dto.getAdres[%d] 번째 오류)", idx+1));
if(Checks.isEmpty(dto.getDetailAdres())) errors.add(String.format("상세 주소는 필수입니다(dto.getDetailAdres[%d] 번째 오류)", idx+1));
if(Checks.isEmpty(dto.getZip())) errors.add(String.format("우편번호는 필수입니다(dto.getZip[%d] 번째 오류)", idx+1));
break;
case SMS:
if(Checks.isEmpty(dto.getMoblphonNo())) errors.add(String.format("핸드폰 번호는 필수입니다(dto.getMoblphonNo[%d] 번째 오류)", idx+1));
break;
default:
break;
}
idx++;
}
}
if(errors.size() > 0) {
throw BizRuntimeException.create(errors.toString());
}
}
/**
* accpet -
* @param mstIdList
* @param stsErrMsg
*/
private void updateAcceptCntcSndngMstFailStatus(final List<String> mstIdList, final String stsErrMsg) {
for(String id : mstIdList) {
EnsDTO.SndngMssageParam paramDTO = EnsDTO.SndngMssageParam.builder()
.unitySndngMastrId(id)
.newSndngProcessSttus("accept-fail")
.errorMssage(stsErrMsg)
.build();
if(mapper.updateProcessSttusCntcSndngMst(paramDTO) != 1) throw BizRuntimeException.create("[accept]연계 발송 마스터 상태변경 실패");
}
}
//-----------------------------------------------------------------------------------------------------------------
// accept
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
// make
//-----------------------------------------------------------------------------------------------------------------
/**
* tryCnt - 1:
* @param sndngProcessSttus
*/
@Override
@Transactional
public void make(final String sndngProcessSttus) {
final String sndngProcessSttus2 = ApiConstants.SndngProcessStatus.SENDING1.getCode();
final String sndngProcessSttus3 = ApiConstants.SndngProcessStatus.SENDING2.getCode();
final List<EnsDTO.SndngMssageParam> list = mapper.selectMakeTgts(sndngProcessSttus);
final List<EnsDTO.SndngMssageParam> list2 = mapper.selectMakeTgts(sndngProcessSttus2);
for(EnsDTO.SndngMssageParam dto : list) {
MDC.put(UNITY_SNDNG_MST_ID, dto.getUnitySndngMastrId());
MDC.put(SNDNG_PROCESS_STTUS, "make-fail1");
dto.setSndngProcessSttus(sndngProcessSttus);
dto.setNewSndngProcessSttus(ApiConstants.SndngProcessStatus.MAKE_OK.getCode());
//FIXME: try1,try2,tr3이 있는 경우 try1 이전에 try2,3이 실행될 수 있는 경우???
dto.setTrySeq(1);
dto.setSndngSeCode(dto.getTry1());
makeMstStatusUpdate(dto);
}
for(EnsDTO.SndngMssageParam dto : list2) {
MDC.put(UNITY_SNDNG_MST_ID, dto.getUnitySndngMastrId());
MDC.put(SNDNG_PROCESS_STTUS, "make-fail2");
//try2 발송 대상이 있는지 확인
final int sendCnt = mapper.selectSendOkTgts(dto);
//없으면 연계 발송 마스터, 통합 발송 마스터 send-ok
if(sendCnt == 0) {
dto.setNewSndngProcessSttus(ApiConstants.SndngProcessStatus.SEND_OK.getCode());
if(mapper.updateProcessSttusCntcSndngMst(dto) != 1) throw BizRuntimeException.create("[make]연계 발송 마스터 상태변경 실패");
if(mapper.updateProcessSttusUnitySndngMst(dto) != 1) throw BizRuntimeException.create("[make]통합 발송 마스터 상태변경 실패");
} else {
//있으면 make
dto.setSndngProcessSttus(sndngProcessSttus2);
dto.setNewSndngProcessSttus(ApiConstants.SndngProcessStatus.MAKE_OK.getCode());
dto.setTrySeq(2);
dto.setSndngCo(sendCnt);
dto.setSndngSeCode(dto.getTry2());
makeMstStatusUpdate(dto);
}
}
//TODO:: GS 인증 시험에서 카카오, E-GREEN만 있어서 3단계는 필요 시 구현
}
/**
* make
* 1. master
* 2. detail
* 2. master
* @param dto
*/
private void makeMstStatusUpdate(EnsDTO.SndngMssageParam dto) {
// 1. master
if(mapper.insertSndngMst(dto) != 1) throw BizRuntimeException.create("[make]데이타 생성(마스터) 실패");
// 2. detail
int insCnt = insertEnsDetailByTry(dto);
if(insCnt != dto.getSndngCo()) throw BizRuntimeException.create(String.format("[make]상세 데이타 생성 실패-발송건수[%d]와 생성건수[%d] 불일치", dto.getSndngCo(), insCnt));
// 3. status
if(mapper.updateProcessSttusCntcSndngMst(dto) != 1) throw BizRuntimeException.create("[make]데이타 생성(발송마스터 상태변경) 실패");
if(mapper.updateProcessSttusUnitySndngMst(dto) != 1) throw BizRuntimeException.create("[make]데이타 생성(통합발송마스터 상태변경) 실패");
}
/**
* make - 2. detail
* @param dto
* @return
*/
private int insertEnsDetailByTry(final EnsDTO.SndngMssageParam dto){
String[] tryVal = {Checks.checkVal(dto.getTry1(),""), Checks.checkVal(dto.getTry2(),""), Checks.checkVal(dto.getTry3(),"")};
switch (tryVal[dto.getTrySeq()-1]){
case KKO_MY_DOC:
return mapper.insertKakaoMyDocs(dto);
case E_GREEN:
return mapper.insertPostSndng(dto);
case SMS:
return mapper.insertSmsSndng(dto);
default:
return 1;
}
}
//-----------------------------------------------------------------------------------------------------------------
// make
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
// send
//-----------------------------------------------------------------------------------------------------------------
/**
* () send - | E-GREEN | SMS
* 1. /
* 2. send()
* @param sndngProcessSttus String
*/
@Override
@Transactional
public void sendBulks(final String sndngProcessSttus) {
// 발송/통합발송 마스터에서 대상 조회
final List<EnsDTO.SndngMssageParam> tgtList = mapper.selectSendBulkTgts(sndngProcessSttus);
for(EnsDTO.SndngMssageParam tgtDTO : tgtList){
MDC.put(UNITY_SNDNG_MST_ID, tgtDTO.getUnitySndngMastrId());
MDC.put("sndngMastrId", tgtDTO.getSndngMastrId());
MDC.put(SNDNG_PROCESS_STTUS, "send-fail" + tgtDTO.getTrySeq());
String[] tryVal = {Checks.checkVal(tgtDTO.getTry1(),""), Checks.checkVal(tgtDTO.getTry2(),""), Checks.checkVal(tgtDTO.getTry3(),"")};
// 마스터 상태 변경값을 파라메터에서 받은 상태값으로 set
tgtDTO.setNewSndngProcessSttus(sndngProcessSttus);
// 업무 문서 구분에 따른 분기
switch (tryVal[tgtDTO.getTrySeq() -1]){
// 카카오
case KKO_MY_DOC:
sendBulkKakaoMyDocs(tgtDTO);
break;
// E-GREEN
case E_GREEN:
sendEgreen(tgtDTO);
break;
// SMS
case SMS:
sendSms(tgtDTO);
break;
default:
break;
}
}
}
/**
*
* @param tgtDTO EnsDTO.SndngMssageParam
*/
@Transactional
public void sendBulkKakaoMyDocs(final EnsDTO.SndngMssageParam tgtDTO) {
final List<EnsDTO.SendKakaoTgt> list = mapper.selectKakaoSendTgts(tgtDTO);
final List<KkopayDocBulkDTO.BulkSendReq> bulkList = new ArrayList<>();
final List<String> mstIdList = new ArrayList<>();
setKkoMyDocSendBulks(list, bulkList, mstIdList);
// validation check
try {
validatedKkoMyDocSendBulks(bulkList);
}catch (Exception e){
updateKkoMyDocSndngMstFailStatus(mstIdList, "[send]카카오 문서 발송(bulks)요청 실패(파라메터 오류)");
throw e;
}
List<ApiResponseDTO<KkopayDocBulkDTO.BulkSendResponses>> apiResults;
List<List<KkopayDocBulkDTO.BulkSendReq>> partitions = ListUtils.partition(bulkList, bulkMaxCnt);
if(isAsync) {
// 카카오페이 전자문서발송 벌크 최대수 단위로 분할하여 실행
List<CompletableFuture<ApiResponseDTO<KkopayDocBulkDTO.BulkSendResponses>>> apiAsyncResults = partitions.stream()
.map(bulkSendList ->
asyncService.requestSendBulk(
KkopayDocBulkDTO.BulkSendRequests.builder()
.documents(bulkSendList)
.build())
)
.collect(Collectors.toList());
// GET API 호출 결과
apiResults = CompletableFuture.allOf(apiAsyncResults.toArray(new CompletableFuture[0]))
.thenApply(Void -> apiAsyncResults.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()))
.join();
}else{
apiResults = partitions.stream()
.map(bulkSendList ->
service.requestSendBulk(
KkopayDocBulkDTO.BulkSendRequests.builder()
.documents(bulkSendList)
.build())
)
.collect(Collectors.toList());
}
// GET 결과 목록
List<KkopayDocBulkDTO.BulkSendResponses> resList = apiResults.stream()
.map(ApiResponseDTO::getData)
.collect(Collectors.toList());
// 카카오 문서 요청 결과 반영
saveKkoMyDocResult(mstIdList, tgtDTO.getUnitySndngMastrId(), resList);
}
/**
*
* @param mstIdList List<String> ID
* @param resList List<KkopayDocBulkDTO.BulkSendResponses>
* @param unitySndMstId String ID
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveKkoMyDocResult(final List<String> mstIdList, String unitySndMstId, final List<KkopayDocBulkDTO.BulkSendResponses> resList) {
// 결과 반영
resList.forEach(o ->
o.getDocuments().forEach(
t -> {
// 카카오페이 문서요청 결과 반영
mapper.updateKakaoSendBulksResult(t);
// 모바일 페이지 컨텐트 생성
if(Checks.isNotEmpty(t.getDocument_binder_uuid())){
//tgtDTO.setUnitySndngDetailId(t.getExternal_document_uuid());
mapper.insertMobilePageManage(t.getExternal_document_uuid());
}
// 연계발송결과 생성
insertKkoMyDocCntcSndngResult(ApiConstants.SndngSeCode.KAKAO_MY_DOC.getCode(), t.getExternal_document_uuid(), t.getError_message());
})
);
// 마스터 상태 변경
updateKkoMyDocSendSndngMstStatus(mstIdList, unitySndMstId, "카카오 문서 발송요청 실패(발송마스터 데이타 오류)");
}
/**
* GET
* @param list List<EnsDTO.SendKakaoTgt>
* @param bulkList List<KkopayDocBulkDTO.BulkSendReq>
* @param mstIdList List<String> ID
*/
private static void setKkoMyDocSendBulks(List<EnsDTO.SendKakaoTgt> list, List<KkopayDocBulkDTO.BulkSendReq> bulkList, List<String> mstIdList) {
String mstId = null;
for(EnsDTO.SendKakaoTgt sendTgtDTO : list){
if(!Objects.equals(mstId, sendTgtDTO.getSndngMastrId())){
mstId = sendTgtDTO.getSndngMastrId();
mstIdList.add(mstId);
}
KkopayDocAttrDTO.Receiver receiver = KkopayDocAttrDTO.Receiver.builder()
.phone_number(sendTgtDTO.getRecvPhoneNumber())
.name(sendTgtDTO.getRecvName())
.birthday(sendTgtDTO.getRecvBirthday())
.is_required_verify_name(false)
.build();
KkopayDocBulkDTO.PropertyBulk property = KkopayDocBulkDTO.PropertyBulk.builder()
.link(sendTgtDTO.getPropLink())
.payload(sendTgtDTO.getPropPayload())
.message(sendTgtDTO.getPropMessage())
.cs_name(sendTgtDTO.getPropCsName())
.cs_number(sendTgtDTO.getPropCsNumber())
.external_document_uuid(sendTgtDTO.getUnitySndngDetailId())
.build();
KkopayDocBulkDTO.BulkSendReq bulkReqDTO = KkopayDocBulkDTO.BulkSendReq.builder()
.title(sendTgtDTO.getTitle())
.common_categories(Collections.singletonList(ApiConstants.Categories.NOTICE))
.read_expired_at(sendTgtDTO.getClosDt())
//
.hash(sendTgtDTO.getHash())
//.hash(encryptor.encryptPassword(sendTgtDTO.getUnitySndngDetailId()))
.receiver(receiver)
.property(property)
.build();
bulkList.add(bulkReqDTO);
}
}
/**
*
* @param bulkList List<KkopayDocBulkDTO.BulkSendReq>
*/
private void validatedKkoMyDocSendBulks(List<KkopayDocBulkDTO.BulkSendReq> bulkList){
List<String> errors = new ArrayList<>();
int idx = 0;
List<List<KkopayDocBulkDTO.BulkSendReq>> arrList = ListUtils.partition(bulkList, bulkMaxCnt);
for(List<KkopayDocBulkDTO.BulkSendReq> list: arrList) {
for(KkopayDocBulkDTO.BulkSendReq dto: list) {
Set<ConstraintViolation<KkopayDocBulkDTO.BulkSendReq>> errList = validator.validate(dto);
if (list.size() > 0) {
int finalIdx = idx;
errors.addAll(errList.stream()
.map(row -> String.format("%s[%d]=%s", row.getPropertyPath(), finalIdx + 1, row.getMessageTemplate()))
.collect(Collectors.toList())
);
}
if(dto.getRead_expired_at() != null && dto.getRead_expired_sec() != null){
errors.add("처리마감시간(절대시간 또는 상대시간)은 하나만 지정해야 합니다.");
}
if(dto.getRead_expired_at() == null && dto.getRead_expired_sec() == null){
errors.add("처리마감시간(절대시간 또는 상대시간)을 지정해야 합니다.");
}
KkopayDocAttrDTO.Receiver receiver = dto.getReceiver();
if (Checks.isEmpty(receiver.getCi())) {
if (Checks.isEmpty(receiver.getName())) errors.add(String.format("받는이 이름은 필수입니다(receiver.name[%d] 번째 오류)", idx+1));
if (Checks.isEmpty(receiver.getPhone_number())) errors.add(String.format("받는이 전화번호는 필수입니다(receiver.phone_number[%d] 번째 오류)", idx+1));
if (Checks.isEmpty(receiver.getBirthday())) errors.add(String.format("받는이 생년월일은 필수입니다(receiver.birthday[%d] 번째 오류)", idx+1));
} else {
StringBuilder sb = new StringBuilder()
.append(StringUtils.defaultString(receiver.getName(), StringUtils.EMPTY))
.append(StringUtils.defaultString(receiver.getPhone_number(), StringUtils.EMPTY))
.append(StringUtils.defaultString(receiver.getBirthday(), StringUtils.EMPTY));
if(Checks.isNotEmpty(sb.toString())){
errors.add(String.format("CI가 지정 되었습니다(받는이 정보 불필요:[%d] 번째 오류) .", idx+1));
}
}
idx++;
}
}
if(errors.size() > 0){
throw BizRuntimeException.create(errors.toString());
}
}
/**
* E-GREEN
* @param tgtDTO EnsDTO.SndngMssageParam
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendEgreen(final EnsDTO.SndngMssageParam tgtDTO) {
final List<EnsDTO.PostSndng> list = mapper.selectPostTgts(tgtDTO);
final String filePath = fileRoot+filePost;
final String fileName = list.get(0).getConKey()+".txt";
if(!egreenFileWrite(filePath, fileName, list)) {
throw BizRuntimeException.create("우편 파일 생성 실패");
}
EnsDTO.SndngMssageParam paramDTO = EnsDTO.SndngMssageParam.builder()
.unitySndngMastrId(list.get(0).getUnitySndngMastrId())
.sndngMastrId(list.get(0).getSndngMastrId())
.newSndngProcessSttus(list.get(0).getSndngProcessSttus())
.build();
updateStepSendMststatus(paramDTO, "E-GREEN");
}
/**
* E-GREEN -
* @param filePath String
* @param fileName String
* @param list List<EnsDTO.PostSndng>
* @return boolean
*/
private boolean egreenFileWrite (final String filePath, final String fileName, final List<EnsDTO.PostSndng> list){
try {
File folder = new File(filePath);
if(!folder.exists()) //noinspection ResultOfMethodCallIgnored
folder.mkdirs();
File file = new File(filePath+fileName);
if(!file.exists()) {
if(!file.createNewFile()){
return false;
};
}
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file, true))) {
for (EnsDTO.PostSndng postSndng : list) {
writer.write(StringUtils.defaultString(postSndng.getConKey()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSenderNm()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSenderZipNo()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSenderAddr()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSenderDetailAddr()) + "|");
writer.write(StringUtils.defaultString(postSndng.getReceiverSendNo()) + "|");
writer.write(StringUtils.defaultString(postSndng.getReceiverNm()) + "|");
writer.write(StringUtils.defaultString(postSndng.getReceiverZipNo()) + "|");
writer.write(StringUtils.defaultString(postSndng.getReceiverAddr()) + "|");
writer.write(StringUtils.defaultString(postSndng.getReceiverDetailAddr()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSschnge1()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSschnge2()) + "|");
writer.write(StringUtils.defaultString(postSndng.getSschnge3()));
writer.newLine();
}
writer.flush();
}
}catch (IOException ie){
return false;
}
return true;
}
/**
* SMS
* - xit SMS : call
* @param tgtDTO EnsDTO.SndngMssageParam
* @see kr.xit.biz.sms.service.SmsMessageService#sendSmsList(List)
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendSms(final EnsDTO.SndngMssageParam tgtDTO) {
final List<EnsDTO.SmsSndng> list = mapper.selectSmsSendTgts(tgtDTO);
// Orcale DB - 서비스 분리
smsService.sendSmsList(list);
EnsDTO.SndngMssageParam paramDTO = EnsDTO.SndngMssageParam.builder()
.unitySndngMastrId(list.get(0).getUnitySndngMastrId())
.sndngMastrId(list.get(0).getSndngMastrId())
.newSndngProcessSttus(list.get(0).getSndngProcessSttus())
.build();
updateStepSendMststatus(paramDTO, "SMS");
}
/**
*
* @param paramDTO EnsDTO.SndngMssageParam
* @param workCfg String
*/
@Transactional
public void updateStepSendMststatus(final EnsDTO.SndngMssageParam paramDTO, final String workCfg) {
if(mapper.updateProcessSttusCntcSndngMst(paramDTO) != 1) throw BizRuntimeException.create(String.format("[send-%s]연계 발송 마스터 상태변경 실패", workCfg));
if(mapper.updateProcessSttusUnitySndngMst(paramDTO) != 1) throw BizRuntimeException.create(String.format("[send-%s]통합 발송 마스터 상태변경 실패", workCfg));
if(mapper.updateProcessSttusSndngMst(paramDTO) != 1) throw BizRuntimeException.create(String.format("[send-%s]발송 마스터 상태변경 실패", workCfg));
}
/**
* (tb_cntc_sndng_result)
* @param sndngSeCode - KKO-MY-DOC|E-GREEN|SMS
* @param unitySndngDetailId ID
* @param errMessage (API )
*/
@Transactional
public void insertKkoMyDocCntcSndngResult(final String sndngSeCode, final String unitySndngDetailId, final String errMessage) {
String rsltSts = StringUtils.defaultString(errMessage, ApiConstants.DocBoxStatus.SENT.getCode());
mapper.insertCntcSndngResult(CntcDTO.SndngResult.builder()
.unitySndngDetailId(unitySndngDetailId)
.sndngSeCode(sndngSeCode)
.sndngResultSttus(rsltSts.equals(ApiConstants.DocBoxStatus.SENT.getCode())? "SENT" : "FAIL")
.errorCn(errMessage)
.build());
}
/**
* / /
* send-ok|sending1|sending2
* @param mstIdList ID
* @param unitySndMstId String ID
* @param stsErrMsg
*/
@Transactional
public void updateKkoMyDocSendSndngMstStatus(final List<String> mstIdList, final String unitySndMstId, final String stsErrMsg) {
for(String id : mstIdList) {
EnsDTO.SndngMssageParam dto = mapper.selectSndProcessStatus(id).orElseThrow(() -> BizRuntimeException.create(stsErrMsg));
EnsDTO.SndngMssageParam paramDTO = EnsDTO.SndngMssageParam.builder()
.sndngMastrId(id)
.unitySndngMastrId(unitySndMstId)
.newSndngProcessSttus(dto.getNewSndngProcessSttus())
.build();
if(mapper.updateProcessSttusCntcSndngMst(paramDTO) != 1) throw BizRuntimeException.create("[send-카카오]연계 발송 마스터 상태변경 실패");
if(mapper.updateProcessSttusUnitySndngMst(paramDTO) != 1) throw BizRuntimeException.create("[send-카카오]통합 발송 마스터 상태변경 실패");
if(mapper.updateProcessSttusSndngMst(paramDTO) != 1) throw BizRuntimeException.create("[send-카카오]발송 마스터 상태변경 실패");
}
}
/**
* -
* @param mstIdList List<String>
* @param stsErrMsg String
*/
@Transactional
public void updateKkoMyDocSndngMstFailStatus(final List<String> mstIdList, final String stsErrMsg) {
for(String id : mstIdList) {
EnsDTO.SndngMssageParam dto = mapper.selectSndProcessStatus(id).orElseThrow(() -> BizRuntimeException.create(stsErrMsg));
String newSndngProcessSttus;
if(dto.getTrySeq() == 2) newSndngProcessSttus = "send-fail2";
else if(dto.getTrySeq() == 3) newSndngProcessSttus = "send-fail3";
else newSndngProcessSttus = "send-fail1";
EnsDTO.SndngMssageParam paramDTO = EnsDTO.SndngMssageParam.builder()
.unitySndngMastrId(dto.getUnitySndngMastrId())
.sndngMastrId(dto.getSndngMastrId())
.newSndngProcessSttus(newSndngProcessSttus)
.build();
if(mapper.updateProcessSttusCntcSndngMst(paramDTO) != 1) throw BizRuntimeException.create("[send-카카오]연계 발송 마스터 상태변경 실패");
if(mapper.updateProcessSttusUnitySndngMst(paramDTO) != 1) throw BizRuntimeException.create("[send-카카오]통합 발송 마스터 상태변경 실패");
if(mapper.updateProcessSttusSndngMst(paramDTO) != 1) throw BizRuntimeException.create("[send-카카오]발송 마스터 상태변경 실패");
}
}
//-----------------------------------------------------------------------------------------------------------------
// send
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
// status
//-----------------------------------------------------------------------------------------------------------------
/**
* (bulks)
* @param sndngProcessSttus
* @return
*/
@Override
@Transactional
public void findKkoMyDocStatusBulks(final String sndngProcessSttus) {
final List<String> docsBinderUuids = mapper.selectKakaoStatusTgts(sndngProcessSttus);
List<ApiResponseDTO<KkopayDocBulkDTO.BulkStatusResponses>> apiResults;
List<List<String>> partitions = ListUtils.partition(docsBinderUuids, bulkMaxCnt);
if(isAsync) {
List<CompletableFuture<ApiResponseDTO<KkopayDocBulkDTO.BulkStatusResponses>>> apiAsyncResults = partitions.stream()
.map(uuids ->
asyncService.findBulkStatus(
KkopayDocBulkDTO.BulkStatusRequests.builder()
.document_binder_uuids(uuids)
.build())
)
.collect(Collectors.toList());
apiResults = CompletableFuture.allOf(apiAsyncResults.toArray(new CompletableFuture[0]))
.thenApply(Void ->
apiAsyncResults.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()))
.join();
}else{
apiResults = partitions.stream()
.map(uuids ->
service.findBulkStatus(
KkopayDocBulkDTO.BulkStatusRequests.builder()
.document_binder_uuids(uuids)
.build())
).collect(Collectors.toList());
}
List<KkopayDocBulkDTO.BulkStatusResponses> resList = apiResults.stream()
.map(ApiResponseDTO::getData)
.collect(Collectors.toList());
// 결과 반영
resList.forEach(o ->
o.getDocuments().forEach(t -> {
mapper.updateKakaoStatusInfo(t);
mapper.updateCntcSndngResult(CntcDTO.SndngResult.builder()
.documentBinderUuid(t.getDocument_binder_uuid())
.sndngResultSttus(StringUtils.defaultString(String.valueOf(t.getStatus_data().getDoc_box_status()), "FAIL"))
.requstDt(Checks.isEmpty(t.getStatus_data().getDoc_box_sent_at())? null: t.getStatus_data().getDoc_box_sent_at().toString())
.inqireDt(Checks.isEmpty(t.getStatus_data().getDoc_box_received_at())? null: t.getStatus_data().getDoc_box_received_at().toString())
.readngDt(Checks.isEmpty(t.getStatus_data().getDoc_box_read_at())? null: t.getStatus_data().getDoc_box_read_at().toString())
.errorCn(t.getError_message())
.build());
})
);
}
//-----------------------------------------------------------------------------------------------------------------
// status
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
// close
//-----------------------------------------------------------------------------------------------------------------
/**
* send-ok close
* @param sndngProcessSttus
*/
@Override
@Transactional
public void close(final String sndngProcessSttus) {
final List<String> list = mapper.selectCloseTgts(sndngProcessSttus);
// 연계발송 마스터(tb_cntc_sndng_mastr) 상태 변경 : send-ok -> close
for(String id : list) {
EnsDTO.SndngMssageParam paramDTO = EnsDTO.SndngMssageParam.builder()
.unitySndngMastrId(id)
.sndngProcessSttus(sndngProcessSttus)
.newSndngProcessSttus(ApiConstants.SndngProcessStatus.CLOSE.getCode())
.build();
if(mapper.updateProcessSttusCntcSndngMst(paramDTO) != 1) throw BizRuntimeException.create("[close]연계 발송 마스터 상태변경 실패");
if(mapper.updateProcessSttusUnitySndngMst(paramDTO) != 1) throw BizRuntimeException.create("[close]통합 발송 마스터 상태변경 실패");
// 2차 이상 발송 시 발송 마스터가 1개 이상 존재
if(mapper.updateProcessSttusBulkSndngMst(paramDTO) < 1) throw BizRuntimeException.create("[close]발송 마스터 상태변경 실패");
}
}
//-----------------------------------------------------------------------------------------------------------------
// close
//-----------------------------------------------------------------------------------------------------------------
/**
* <pre>
* </h3>
* - : (bulks-) document_binder_uuid
* - redirect url
* ->
* -> > >
*
*
* 1. (redirect url /
* 2.
*
* @param reqDTO KkopayDocDTO.OneTimeToken
* </pre>
*/
@Override
public ApiResponseDTO<?> findKkoMyDocReadyAndMblPage(KkopayDocDTO.OneTimeToken reqDTO) {
if (Checks.isEmpty(reqDTO.getToken()) || Checks.isEmpty(reqDTO.getDocument_binder_uuid()) || Checks.isEmpty(reqDTO.getExternal_document_uuid()))
throw BizRuntimeException.create(String.valueOf(ErrorCode.BAD_REQUEST.getHttpStatus().value()), "정상적인 요청이 아닙니다. 재인증 후 시도하시기 바랍니다.");
// document_binder_uuid와 external_document_uuid로 데이타 검증
EnsDTO.MobilePageManage mobilePageManage = mapper.selectMobilePage(reqDTO);
if(mobilePageManage == null) throw BizRuntimeException.create("데이타 오류[내문서함 문서가 없습니다]");
ApiResponseDTO<KkopayDocDTO.ValidTokenResponse> res = service.findMyDocReadyAndMblPage(reqDTO);
if(!res.isSuccess()){
return res;
}
return ApiResponseDTO.success(mobilePageManage.getMobilePageCn());
}
/**
*
* @param dto EnsDTO.SndngMssageParam
* @see kr.xit.ens.support.batch.task.cmm.TaskCmmUtils#taskEnsMessageLinkServiceUpdateErrorLog(String, String, String)
*/
public void updateErrorLog(EnsDTO.SndngMssageParam dto) {
if ("SndngAcceptJob".equals(dto.getErrorCode())) {
mapper.updateProcessSttusCntcSndngMst(dto);
} else if ("SndngMakeJob".equals(dto.getErrorCode())){
mapper.updateProcessSttusCntcSndngMst(dto);
mapper.updateProcessSttusUnitySndngMst(dto);
} else if ("SndngSendBulksJob".equals(dto.getErrorCode())) {
mapper.updateProcessSttusCntcSndngMst(dto);
mapper.updateProcessSttusUnitySndngMst(dto);
mapper.updateProcessSttusSndngMst(dto);
}
}
/**
* (bulks)
* -
* @param sndngProcessSttus
* @return
*
*/
@Override
public List<EnsDTO.SendKakaoTgt> findKakaoSendTargets(final String sndngProcessSttus) {
return mapper.selectKakaoSendTgts(sndngProcessSttus);
}
}

@ -0,0 +1,49 @@
package kr.xit.biz.ens.web;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import kr.xit.biz.ens.service.IEnsCctvFileService;
import kr.xit.biz.pni.service.IPniCctvFileService;
import kr.xit.core.model.ApiResponseDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <pre>
* description :
*
* packageName : kr.xit.biz.pni.web
* fileName : PniCctvFileController
* author : limju
* date : 2023-07-07
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-07 limju
*
* </pre>
*/
@Tag(name = " EnsCctvFileController", description = "전자고지 CCTV 파일 연계 서비스")
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/ens/v1")
public class EnsCctvFileController {
private final IEnsCctvFileService service;
@Operation(summary = "전자고지 파일 연계 데이타 생성(서광)", description = "전자고지 파일 연계 데이타 생성(서광)")
@PostMapping(value = "/createCctvFileOfSg", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> createCctvFileOfSg() {
service.createCctvFileOfSg();
return ApiResponseDTO.success();
}
@Operation(summary = "전자고지 연계 데이타 생성(accept)", description = "전자고지 연계 데이타 생성(accept)")
@PostMapping(value = "/acceptEnsNtnccntcSndng", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> acceptEnsNtnccntcSndng() {
service.acceptEnsNtnccntcSndng();
return ApiResponseDTO.success();
}
}

@ -0,0 +1,49 @@
package kr.xit.biz.ens.web;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.tags.Tag;
import kr.xit.biz.ens.service.ISendMessageLinkService;
import kr.xit.core.model.ApiResponseDTO;
import kr.xit.ens.support.kakao.model.KkopayDocDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
* <pre>
* description :
*
* packageName : kr.xit.biz.ens.web
* fileName : KkoMyDocController
* author : limju
* date : 2023-06-05
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-05 limju
*
* </pre>
*/
@Tag(name = "KkoMyDocController", description = "전자고지 통합발송(카카오페이전자문서) 연계 서비스")
@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/api/ens/v1")
public class KkoMyDocController {
private final ISendMessageLinkService service;
@Operation(summary = "모바일 데이타 요청(모바일 페이지에서 호출)", description = "모바일 데이타 요청(모바일 페이지에서 호출)")
@PostMapping(value = "/mblData", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> findKkoMyDocReadyAndMblData(
@RequestBody final KkopayDocDTO.OneTimeToken reqDTO
) {
return service.findKkoMyDocReadyAndMblPage(reqDTO);
}
}

@ -0,0 +1,113 @@
package kr.xit.biz.ens.web;
import java.util.Map;
import kr.xit.ens.support.kakao.model.KkopayDocDTO;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.tags.Tag;
import kr.xit.biz.ens.service.ISendMessageLinkService;
import kr.xit.core.model.ApiResponseDTO;
import lombok.RequiredArgsConstructor;
/**
* <pre>
* description :
*
* packageName : kr.xit.biz.ens.web
* fileName : SendMessageLinkController
* author : limju
* date : 2023-06-05
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-05 limju
*
* </pre>
*/
@Tag(name = "SendMessageLinkController", description = "전자고지 통합발송 연계 서비스(배치) 테스트")
@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/api/ens/v1")
public class SendMessageLinkController {
private static final String SNDNG_PROCESS_STTUS = "sndngProcessSttus";
private final ISendMessageLinkService service;
@Operation(summary = "통합문서 접수", description = "통합문서 접수")
@io.swagger.v3.oas.annotations.parameters.RequestBody(required = true, content = {
@Content(mediaType = "application/json", examples = {
@ExampleObject(value = "{\"sndngProcessSttus\":\"accept\"}")
})
})
@PostMapping(value = "/accept", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> accept(@RequestBody final Map<String, String> map) {
service.accept(map.get(SNDNG_PROCESS_STTUS));
return ApiResponseDTO.success();
}
@Operation(summary = "통합문서 발송 데이타 생성", description = "통합문서 발송데이타 생성")
@io.swagger.v3.oas.annotations.parameters.RequestBody(required = true, content = {
@Content(mediaType = "application/json", examples = {
@ExampleObject(value = "{\"sndngProcessSttus\":\"accept-ok\"}")
})
})
@PostMapping(value = "/make", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> make(@RequestBody final Map<String, String> map){
service.make(map.get(SNDNG_PROCESS_STTUS));
return ApiResponseDTO.success();
}
@Operation(summary = "통합문서 발송 데이타 종료", description = "통합문서 발송 데이타 종료")
@io.swagger.v3.oas.annotations.parameters.RequestBody(required = true, content = {
@Content(mediaType = "application/json", examples = {
@ExampleObject(value = "{\"sndngProcessSttus\":\"send-ok\"}")
})
})
@PostMapping(value = "/close", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> close(@RequestBody final Map<String, String> map){
service.close(map.get(SNDNG_PROCESS_STTUS));
return ApiResponseDTO.success();
}
@Operation(summary = "통합문서(bulk) 발송 요청", description = "통합문서 발송 요청")
@io.swagger.v3.oas.annotations.parameters.RequestBody(required = true, content = {
@Content(mediaType = "application/json", examples = {
@ExampleObject(value = "{\"sndngProcessSttus\":\"make-ok\"}")
})
})
@PostMapping(value = "/sendBulks", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> sendBulks(@RequestBody final Map<String, String> map){
service.sendBulks(map.get(SNDNG_PROCESS_STTUS));
return ApiResponseDTO.success();
}
@Operation(summary = "통합문서(bulk) 상태 조회", description = "통합문서 상태 조회")
@io.swagger.v3.oas.annotations.parameters.RequestBody(required = true, content = {
@Content(mediaType = "application/json", examples = {
@ExampleObject(value = "{\"sndngProcessSttus\":\"send-ok\"}")
})
})
@PostMapping(value = "/findStatusBulks", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> findStatusBulks(@RequestBody final Map<String, String> map){
service.findKkoMyDocStatusBulks(map.get(SNDNG_PROCESS_STTUS));
return ApiResponseDTO.success();
}
@Operation(summary = "통합 문서발송 목록 조회", description = "통합 문서발송 목록 조회")
@io.swagger.v3.oas.annotations.parameters.RequestBody(required = true, content = {
@Content(mediaType = "application/json", examples = {
@ExampleObject(value = "{\"sndngProcessSttus\":\"make-ok\"}")
})
})
@PostMapping(value = "/findKakaoSendTargets", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> findkakaoSendTargets(@RequestBody final Map<String, String> map){
return ApiResponseDTO.success(service.findKakaoSendTargets(map.get(SNDNG_PROCESS_STTUS)));
}
}

@ -0,0 +1,12 @@
/**
* ENS business packages
* <p>
* : ens
* : pni
* sms
* </p>
* @since 1.0
* @author limju
* @version 1.0
*/
package kr.xit.biz;

@ -0,0 +1,32 @@
package kr.xit.biz.pni.mapper;
import kr.xit.biz.pni.model.PniDTO;
import org.egovframe.rte.psl.dataaccess.mapper.Mapper;
import java.util.List;
/**
* <pre>
* description :
*
* packageName : kr.xit.biz.pni.mapper
* fileName : IPniCctvFileMapper
* author : limju
* date : 2023-07-07
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-07 limju
*
* </pre>
*/
@Mapper
public interface IPniCctvFileMapper {
int selectLicense(String jobSeCode);
int insertNtcnCntcData(PniDTO.NtcnCntcData dto);
List<PniDTO.PniNtncCntcSndngTgts> selectPniNtncCntcSndngs();
int insertCntcSndngMst(PniDTO.PniNtncCntcSndngTgts dto);
int insertCntcSndngDtl(PniDTO.PniNtncCntcSndngTgts dto);
int updatePniNtcnCntcData(PniDTO.PniNtncCntcSndngTgts dto);
}

@ -0,0 +1,164 @@
package kr.xit.biz.pni.model;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import kr.xit.ens.support.common.ApiConstants;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <pre>
* description : tb_pni_ Entity DTO
*
* packageName : kr.xit.biz.pni.model
* fileName : PniDTO
* author : limju
* date : 2023-07-07
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-07 limju
*
* </pre>
*/
public class PniDTO {
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class NtcnCntcData implements Serializable {
/**
* data id
*/
private String ntcnCntcDataId;
/**
*
*/
private String regltDt;
/**
*
*/
private String vhcleNo;
/**
*
*/
private String sptNm;
/**
*
*/
private String sptAcctoCode;
/**
*
*/
private String fileNm;
/**
*
*/
private String processAt;
/**
*
*/
private String trgetAt;
/**
* id
*/
private String unitySndngDetailId;
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd kk:mm:ss")
private LocalDateTime registDt;
/**
*
*/
private String register;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public static class PniNtncCntcSndngTgts implements Serializable {
/**
* data id
*/
private String ntcnCntcDataId;
/**
*
*/
private String vhcleNo;
/**
*
*/
private String processAt;
/**
*
*/
private String trgetAt;
/**
* ID
*/
private String ntcnTrgetId;
/**
*
*/private String moblphonNo;
/**
*
*/
private String nm;
/**
*
*/
private String brthdy;
/*삭제여부*/
private String deleteAt;
/**
*
*/
private int sndngCo;
/**
*
*/
@Builder.Default
private String sndngTyCode = "PNI";
/**
* 릿 ID
*/
private String tmplatId;
/**
*
*/
private String tgtYn;
/**
* ID
*/
private String unitySndngMastrId;
/**
* ID
*/
private String unitySndngDetailId;
/**
*
*/
@Builder.Default
private String sndngProcessSttus = ApiConstants.SndngProcessStatus.ACCEPT.getCode();
/**
* 릿
*/
private String tmpltMsgData;
/**
*
*/
private String mobilePageCn;
}
}

@ -0,0 +1,28 @@
package kr.xit.biz.pni.service;
/**
* <pre>
* description :
*
* packageName : kr.xit.biz.pni.service
* fileName : IPniCctvFileService
* author : limju
* date : 2023-07-07
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-07 limju
*
* </pre>
*/
public interface IPniCctvFileService {
/**
* cctv ()
*/
void createCctvFileOfSg();
/**
* accept
*/
void acceptPniNtnccntcSndng();
}

@ -0,0 +1,239 @@
package kr.xit.biz.pni.service;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
import kr.xit.biz.pni.mapper.IPniCctvFileMapper;
import kr.xit.biz.pni.model.PniDTO;
import kr.xit.core.exception.BizRuntimeException;
import kr.xit.core.support.utils.DateUtils;
import kr.xit.core.support.utils.SFTPUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* <pre>
* description : CCTV
*
* packageName : kr.xit.biz.pni.service
* fileName : PniCctvFileService
* author : limju
* date : 2023-07-07
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-07 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class PniCctvFileService extends EgovAbstractServiceImpl implements IPniCctvFileService {
@Value("${app.ssh.host}") private String host;
@Value("${app.ssh.port}") private int port;
@Value("${app.ssh.id}") private String id;
@Value("${app.ssh.passwd}") private String passwd;
@Value("${app.ssh.sg.root-path}") private String rootPath;
@Value("${app.ssh.sg.rcv}") private String rcvPath;
@Value("${app.ssh.sg.backup}") private String backupPath;
@Value("${app.ssh.sg.err}") private String errPath;
private final IPniCctvFileMapper mapper;
/**
* cctv ()
*/
@Override
@Transactional
public void createCctvFileOfSg() {
// 사전알림 라이선스 유효성 체크
int licenseCnt = mapper.selectLicense("PNI");
if(licenseCnt == 0) return;
SFTPUtils sftp = null;
try {
// SFTP connect
sftp = new SFTPUtils();
sftp.init(host, port, id, passwd, StringUtils.EMPTY);
// 서광 CCTV 사전알림 대상 조회
final String srcPath = rootPath + rcvPath;
ArrayList<String> fileNameList = sftp.findFileNameList(srcPath);
//if(fileNameList.size() == 0) throw BizRuntimeException.create("사전고지[서광] 처리 대상이 없습니다");
// 에러 파일 목록
ArrayList<String> errFileList = new ArrayList<>();
// 성공 파일 목록
ArrayList<String> rtnFileList = new ArrayList<>();
for (String fn : fileNameList) {
String[] arrFi = fn.split("_");
// 파일 이름 정보 오류시
if (arrFi.length != 4 || arrFi[0].length() != 14) {
errFileList.add(fn);
continue;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FIXME :: GS 임시 소스
// 파일 다운로드
final String downLoadPath = "D:/ImageData/PNI" + "/" + DateUtils.getToday(StringUtils.EMPTY);
File Folder = new File(downLoadPath);
if (!Folder.exists()) Folder.mkdir(); //폴더 생성합니다.
sftp.fileDownload(srcPath + "/" + fn, downLoadPath);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FIXME : 중복데이타(단속시간 + 차량번호)인 경우 데이타 skip 하도록 해야 할 듯
// 사전알림 연계 데이타 생성
mapper.insertNtcnCntcData(
PniDTO.NtcnCntcData.builder()
.regltDt(arrFi[0])
.vhcleNo(arrFi[1])
.sptNm(arrFi[2])
.sptAcctoCode(arrFi[3].split("\\.")[0])
.fileNm(fn)
.build()
);
rtnFileList.add(fn);
// FIXME :: sftp rm 기능 추후 제거
// 파일 삭제
sftp.rm(fn);
}
// FIXME :: sftp move 기능 추후 사용
// 처리한 데이타 backup
//fileBackup(sftp, srcPath, errFileList, rtnFileList);
} finally {
if(sftp != null) sftp.disconnect();
}
}
/**
* accept
* 1.
* 2.
* 3.
* 4.
*/
@Override
@Transactional
public void acceptPniNtnccntcSndng() {
List<PniDTO.PniNtncCntcSndngTgts> tgtList = mapper.selectPniNtncCntcSndngs();
if(tgtList.size() == 0) return;
// search 사전알림 연계발송 SMS 데이타 생성 대상
Optional<PniDTO.PniNtncCntcSndngTgts> sms = tgtList.stream()
.filter(dto -> "JU201".equals(dto.getTmplatId()))
.filter(dto -> "Y".equals(dto.getTgtYn()))
.findFirst();
// search 사전알림 연계발송 카카오 알림톡 데이타 생성 대상
Optional<PniDTO.PniNtncCntcSndngTgts> kkoMyDoc = tgtList.stream()
.filter(dto -> "JU202".equals(dto.getTmplatId()))
.filter(dto -> "Y".equals(dto.getTgtYn()))
.findFirst();
// 사전알림 연계발송 master 생성
String smsUnitySndngMastrId;
String kkoUnitySndngMastrId;
String unitySndngMastrId = null;
// sms 대상 있는지 확인해서 있으면 연계발송 master 생성
if(sms.isPresent()){
PniDTO.PniNtncCntcSndngTgts tgtDTO = sms.get();
mapper.insertCntcSndngMst(tgtDTO);
smsUnitySndngMastrId = tgtDTO.getUnitySndngMastrId();
MDC.put("unitySndngMastrId", smsUnitySndngMastrId);
} else {
smsUnitySndngMastrId = null;
}
// 카카오 알림톡 대상 있는지 확인해서 있으면 연계발송 master 생성
if(kkoMyDoc.isPresent()){
PniDTO.PniNtncCntcSndngTgts tgtDTO = kkoMyDoc.get();
mapper.insertCntcSndngMst(tgtDTO);
kkoUnitySndngMastrId = tgtDTO.getUnitySndngMastrId();
MDC.put("unitySndngMastrId", kkoUnitySndngMastrId);
} else {
kkoUnitySndngMastrId = null;
}
// 사전알림 연계발송 상세 생성 및 결과 처리(사전알림 연계 데이타 반영)
// 사전알림 대상 -> 사전알림 sms / 카카오 알림톡 연계 발송 상세 생성
// 사전알림 연계 데이타 -> 연계 결과 반영
// : 대상 : 연계대상-Y, 진행상태-Y, 통합발송상세ID
// 미대상 : 연계대상-N, 진행상태-Y, 통합발송상세ID = null
tgtList.forEach(dto -> {
dto.setProcessAt("Y");
if("Y".equals(dto.getTgtYn())){
// sms / 카카오 알림톡 템플릿메시지 data 생성
StringBuffer strTmpltMsgData = new StringBuffer();
strTmpltMsgData.append("[주정차 단속 사전 알림]");
strTmpltMsgData.append ( "\n" );
strTmpltMsgData.append("CCTV 주정차 단속 지역입니다.");
strTmpltMsgData.append ( "\n" );
strTmpltMsgData.append(dto.getVhcleNo() + " 차량은 즉시 이동바랍니다.");
dto.setTmpltMsgData(strTmpltMsgData.toString());
// FIXME :: GS 인증 시험 알림톡이 안되어서 인증톡으로 대체 알림톡 운영 시 필요 없음
// 카카오 알림톡 모바일 페이지 문구
StringBuffer strmobilePageCn = new StringBuffer();
strmobilePageCn.append("{");
strmobilePageCn.append("\"details\" : [");
strmobilePageCn.append( "{");
strmobilePageCn.append( "\"title\" : \"주정차 단속 사전 알림\",");
strmobilePageCn.append ( "\"item_type\" : \"PRE_TEXT\",");
strmobilePageCn.append ( "\"elements\" : \"CCTV 주정차 단속 지역입니다.\\n"+ dto.getVhcleNo() + " 차량은 즉시 이동바랍니다.\\n- 서광시스템\"");
strmobilePageCn.append( "}");
strmobilePageCn.append( "]");
strmobilePageCn.append("}");
dto.setMobilePageCn(strmobilePageCn.toString());
if("JU201".equals(dto.getTmplatId())) dto.setUnitySndngMastrId(smsUnitySndngMastrId);
else if("JU202".equals(dto.getTmplatId()))dto.setUnitySndngMastrId(kkoUnitySndngMastrId);
mapper.insertCntcSndngDtl(dto);
}
mapper.updatePniNtcnCntcData(dto);
});
}
private void fileBackup(SFTPUtils sftp, String srcPath, ArrayList<String> errFileList, ArrayList<String> rtnFileList) {
final String errorPath = rootPath + errPath;
String destPath = rootPath + backupPath;
destPath = destPath + "/" + DateUtils.getToday(StringUtils.EMPTY);
log.info("src path::[{}]", srcPath);
log.info("tgt path::[{}]", destPath);
// file backup
for (String fn : rtnFileList) {
log.info("fileName::[{}]", fn);
sftp.mv(srcPath + "/" + fn, destPath + "/" + fn);
}
// error file backup
for (String fn : errFileList) {
sftp.mv(srcPath + "/" + fn, errorPath + "/" + fn);
}
}
}

@ -0,0 +1,46 @@
package kr.xit.biz.pni.web;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import kr.xit.biz.pni.service.IPniCctvFileService;
import kr.xit.core.model.ApiResponseDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
/**
* <pre>
* description :
*
* packageName : kr.xit.biz.pni.web
* fileName : PniCctvFileController
* author : limju
* date : 2023-07-07
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-07 limju
*
* </pre>
*/
@Tag(name = "PniCctvFileController", description = "사전고지 CCTV 파일 연계 서비스")
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/pni/v1")
public class PniCctvFileController {
private final IPniCctvFileService service;
@Operation(summary = "사전고지 파일 연계 데이타 생성(서광)", description = "사전고지 파일 연계 데이타 생성(서광)")
@PostMapping(value = "/cctvFileOfSg", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> cctvFileOfSg() {
service.createCctvFileOfSg();
return ApiResponseDTO.success();
}
@Operation(summary = "사전고지 연계 데이타 생성(accept)", description = "사전고지 연계 데이타 생성(accept)")
@PostMapping(value = "/acceptPniNtnccntcSndng", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> acceptPniNtnccntcSndng() {
service.acceptPniNtnccntcSndng();
return ApiResponseDTO.success();
}
}

@ -0,0 +1,29 @@
package kr.xit.biz.sms.mapper;
import org.egovframe.rte.psl.dataaccess.mapper.Mapper;
/**
* <pre>
* description : SMS (oracle DB)
*
* packageName : kr.xit.biz.sms.mapper
* fileName : ISmsMessageMapper
* author : limju
* date : 2023-06-26
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-26 limju
*
* </pre>
*/
@Mapper
public interface ISmsMessageMapper {
/**
* sms
* @param t status
* @return int
*/
<T> int insertScTran(final T t);
}

@ -0,0 +1,24 @@
package kr.xit.biz.sms.service;
import kr.xit.biz.ens.model.EnsDTO;
import java.util.List;
/**
* <pre>
* description :
*
* packageName : kr.xit.biz.sms.service
* fileName : ISmsMessageService
* author : limju
* date : 2023-06-26
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-26 limju
*
* </pre>
*/
public interface ISmsMessageService {
<T> void sendSmsList(List<T> list);
}

@ -0,0 +1,42 @@
package kr.xit.biz.sms.service;
import kr.xit.biz.sms.mapper.ISmsMessageMapper;
import lombok.RequiredArgsConstructor;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* <pre>
* description : xit SMS - Oracle 11
*
* packageName : kr.xit.biz.sms.service
* fileName : SmsMessageService
* author : limju
* date : 2023-06-26
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-26 limju
*
* </pre>
*/
@RequiredArgsConstructor
@Service
public class SmsMessageService extends EgovAbstractServiceImpl implements ISmsMessageService {
private final ISmsMessageMapper mapper;
/**
* SMS - xit SMS
* @param list List SMS
*/
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public <T> void sendSmsList(List<T> list) {
for(T t : list) mapper.insertScTran(t);
}
}

@ -0,0 +1,260 @@
package kr.xit.core.aop;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import kr.xit.core.exception.BizRuntimeException;
import kr.xit.core.model.ApiResponseDTO;
import kr.xit.core.biz.model.LoggingDTO;
import kr.xit.core.biz.service.ILoggingService;
import kr.xit.core.spring.util.error.ErrorParse;
import kr.xit.core.support.slack.SlackWebhookPush;
import kr.xit.core.support.utils.Checks;
import kr.xit.core.support.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : logging trace aspect
* core LoggerAspect -> traceLogging / traceLoggingError / traceLoggingResult
*
* Logging trace
* - MDC(Mapped Diagnostic Context : logback, log4j )
* - ThreadLocal
* - nginx : proxy_set_header X-RequestID $request_id;
* - logback log pattern : [traceId=%X{request_id}]
*
* app.slack-webhook.enabled: true slack push
* Slack webhook : SlackWebhookPush
*
* packageName : kr.xit.core.aop
* fileName : TraceLoggerAspect
* author : julim
* date : 2023-04-28
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-04-28 julim
* 2023-06-12 julim RequestContextHolder.HttpServletRequest
* </pre>
* @see kr.xit.core.support.slack.SlackWebhookPush#sendSlackAlertLog(String, String, String)
*/
@Slf4j
@Aspect
@Component
public class TraceLoggerAspect {
@Value("${app.slack-webhook.enabled:false}")
private boolean isSlackEnabled;
@Value("#{'${app.mdc.log.trace.uris}'.split(',')}")
private String[] uris;
private final ILoggingService loggingService;
private final SlackWebhookPush slackWebhookPush;
private static final String MESSAGE = "message";
private static final String CODE = "code";
private static final String REQUEST_TRACE_ID = "request_trace_id";
public TraceLoggerAspect(@Lazy ILoggingService loggingService, SlackWebhookPush slackWebhookPush) {
this.loggingService = loggingService;
this.slackWebhookPush = slackWebhookPush;
}
@Pointcut("execution(public * egovframework..*.*(..)) || execution(public * kr.xit..*.*(..))")
public void errorPointCut() {
}
@Around(value = "@annotation(kr.xit.core.spring.annotation.TraceLogging)")
public Object serviceTraceLogging(final ProceedingJoinPoint pjp) throws Throwable {
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes != null? attributes.getRequest(): null;
traceLogging(JsonUtils.toJson(pjp.getArgs()), request);
Object result = pjp.proceed();
if(result instanceof CompletableFuture){
CompletableFuture future = (CompletableFuture)result;
while(true) {
if (future.isDone()) break;
}
traceLoggingResult(future.get());
}else{
traceLoggingResult(result);
}
return result;
}
//@AfterThrowing(value = "@annotation(kr.xit.core.spring.annotation.TraceLogging)", throwing = "error")
@AfterThrowing(value = "errorPointCut()", throwing="error")
public void afterThrowingProceed(final JoinPoint jp, final Throwable error) {
traceLoggingError(jp, error);
}
/**
* set MDC batch reading , tasklet set trace_id set
* @param params
* @param request
*/
protected void traceLogging(final String params, final HttpServletRequest request) {
if(request != null) {
String uri = request.getRequestURI().toString();
if(Arrays.asList(uris).stream().anyMatch(regx -> uri.matches(regx))) return;
MDC.put(REQUEST_TRACE_ID,
StringUtils.defaultString(MDC.get("request_trace_batch_id"), UUID.randomUUID().toString().replaceAll("/-/g", "")));
MDC.put("method", request.getMethod());
MDC.put("uri", uri);
MDC.put("ip", request.getRemoteAddr());
MDC.put("sessionId", request.getSession().getId());
// batch로 실행되는 경우 task에서 설정 : uri / method
}else{
// request_id는 중복 처리 되면 않되므로 여기에서 생성
MDC.put(REQUEST_TRACE_ID, StringUtils.defaultString(MDC.get("request_trace_batch_id"), UUID.randomUUID().toString().replaceAll("-", "")));
}
log.info("@@@@@@@@@@@@@@@@@로깅 start : [\n{}\n]",MDC.getCopyOfContextMap());
MDC.put("systemId", "ENS");
MDC.put("reqSystemId", "KAKAO");
MDC.put("param", params);
MDC.put("accessToken", "");
LoggingDTO loggingDTO = LoggingDTO.builder()
.requestId(MDC.getCopyOfContextMap().get(REQUEST_TRACE_ID))
.systemId("ENS")
.reqSystemId("KAKAO")
.method(MDC.getCopyOfContextMap().get("method"))
.uri(MDC.getCopyOfContextMap().get("uri"))
.param(params)
.ip(MDC.getCopyOfContextMap().get("ip"))
.accessToken("")
.sessionId(MDC.getCopyOfContextMap().get("sessionId"))
.build();
loggingService.saveLogging(loggingDTO);
}
protected void traceLoggingResult(final Object result) {
String success = "true";
//FIXME: slack webhook log push
if(result instanceof ApiResponseDTO<?>){
ApiResponseDTO<?> apiResponseDTO = (ApiResponseDTO<?>)result;
if(!apiResponseDTO.isSuccess()){
success = "false";
if(isSlackEnabled) {
if(RequestContextHolder.getRequestAttributes() != null) {
slackWebhookPush.sendSlackAlertLog(
String.format("[%s]%s", MDC.get(REQUEST_TRACE_ID), apiResponseDTO.getMessage()),
((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest());
}else{
slackWebhookPush.sendSlackAlertLog(
String.format("[%s]%s", MDC.get(REQUEST_TRACE_ID), apiResponseDTO.getMessage()),
MDC.get("uri"),
"batch call");
}
}
}else{
if(apiResponseDTO.getData() != null){
log.info("~~~~");
}
}
}
LoggingDTO reqDTO = LoggingDTO
.builder()
.requestId(MDC.get(REQUEST_TRACE_ID))
.success(success)
.response(getResult(result))
.message(HttpStatus.OK.name())
.build();
//}
loggingService.modifyLogging(reqDTO);
//loggingService.saveLogging(reqDTO);
log.info("@@@@@@@@@@@@@@로깅 end[\n{}\n]", MDC.getCopyOfContextMap());
//if(RequestContextHolder.getRequestAttributes() != null)
MDC.clear();
//}
}
private String getResult(final Object o){
if(o instanceof Future) {
try {
Future<?> future = (Future<?>)o;
return JsonUtils.toJson(future.get());
} catch (InterruptedException ie){
// thread pool에 에러 상태 전송
Thread.currentThread().interrupt();
throw BizRuntimeException.create(ie);
} catch (ExecutionException ee) {
throw BizRuntimeException.create(ee);
}
}else{
return JsonUtils.toJson(o);
}
}
protected void traceLoggingError(final JoinPoint jp, final Throwable e) {
log.info("~~~~~~~~~~~~~~~~~~~~~~~>>>>{}", MDC.get(REQUEST_TRACE_ID));
if(Checks.isEmpty(MDC.get(REQUEST_TRACE_ID))) return;
Map<String,Object> map = ErrorParse.extractError(e);
loggingService.modifyLogging(
LoggingDTO
.builder()
.requestId(MDC.get(REQUEST_TRACE_ID))
.success("false")
.response(JsonUtils.toJson(ApiResponseDTO.error(String.valueOf(map.get(CODE)), String.valueOf(map.get(MESSAGE)), (HttpStatus)map.get("httpStatus"))))
.message(String.valueOf(map.get(MESSAGE)))
.build());
//FIXME :: slack webhook log push
if(isSlackEnabled){
if(RequestContextHolder.getRequestAttributes() != null) {
slackWebhookPush.sendSlackAlertLog(
String.format("[%s]%s(%s)", MDC.get(REQUEST_TRACE_ID), String.valueOf(map.get(CODE)),
String.valueOf(map.get(MESSAGE))),
((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest());
}else{
slackWebhookPush.sendSlackAlertLog(
String.format("[%s]%s(%s)", MDC.get(REQUEST_TRACE_ID), String.valueOf(map.get(CODE)),
String.valueOf(map.get(MESSAGE))),
MDC.get("uri"),
"batch call");
}
}
log.info("@@@@@@@@@@@@ 로깅 end[\n{}\n]", MDC.getCopyOfContextMap());
//if(RequestContextHolder.getRequestAttributes() != null)
MDC.clear();
}
}

@ -0,0 +1,38 @@
package kr.xit.core.biz.batch;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
/**
* <pre>
* description : Spring Boot 2.0.x
* => Job
* Spring Boot 2.1.0
* => Job
* --> run.id
* .incrementer(new RunIdIncrementer()) -> .incrementer(new UniqueRunIdIncrementer())
*
* packageName : kr.xit.core.biz.batch
* fileName : CustomRunIdIncrementer
* author : limju
* date : 2023-05-16
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-16 limju
*
* </pre>
*/
public class CustomRunIdIncrementer extends RunIdIncrementer {
private static final String RUN_ID = "run.id";
@Override
public JobParameters getNext(JobParameters parameters) {
JobParameters params = (parameters == null) ? new JobParameters() : parameters;
return new JobParametersBuilder()
.addLong(RUN_ID, params.getLong(RUN_ID, 0L) + 1)
.toJobParameters();
}
}

@ -0,0 +1,37 @@
package kr.xit.core.biz.batch;
import java.util.List;
/**
* <pre>
* description : ItemReaderAdapter List<T> read()
* -> ItemReader read
* ItemReaderAdapter targetObject
* -> ItemReaderAdapter.setTargetObject(Object o)
* : new ListReader<>(- )
* : new ListReader<>(loggingService.findLogging(null))
* packageName : kr.xit.core.biz.batch
* fileName : ListReader
* author : limju
* date : 2023-05-16
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-16 limju
*
* </pre>
*/
public class ListReader<T> {
private List<T> list;
public ListReader(List<T> list){
this.list = list;
}
public T read() {
if(list.isEmpty()){
return null;
}
return list.remove(0);
}
}

@ -0,0 +1,48 @@
package kr.xit.core.biz.batch.listener;
import org.springframework.batch.core.ChunkListener;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : Chunk
*
* packageName : kr.xit.core.biz.batch.listener
* fileName : CustomChunkListener
* author : limju
* date : 2023-05-16
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-16 limju
*
* </pre>
*/
@Slf4j
@Component
public class CustomChunkListener {
@Bean
public ChunkListener chunkListener() {
return new ChunkListener() {
@Override
public void beforeChunk(ChunkContext context) {
log.info("↓ Chunk start");
}
@Override
public void afterChunk(ChunkContext context) {
log.info("↑ Chunk end");
}
@Override
public void afterChunkError(ChunkContext context) {
// Do nothing
}
};
}
}

@ -0,0 +1,29 @@
package kr.xit.core.biz.batch.listener;
import java.util.List;
import org.springframework.batch.core.listener.ItemListenerSupport;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class CustomItemListner extends ItemListenerSupport {
public void onReadError(Exception ex) {
log.error("onReadError", ex);
}
@Override
public void onProcessError(Object item, Exception e) {
log.error("onProcessError", e);
super.onProcessError(item, e);
}
@Override
public void onWriteError(Exception ex, List item) {
log.error("onWriteError", ex);
}
}

@ -0,0 +1,41 @@
package kr.xit.core.biz.batch.listener;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.listener.JobExecutionListenerSupport;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : Job
*
* packageName : kr.xit.core.biz.batch.listener
* fileName : CustomJobListener
* author : limju
* date : 2023-05-16
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-16 limju
*
* </pre>
*/
@Slf4j
public class CustomJobListener extends JobExecutionListenerSupport {
@Override
public void beforeJob(JobExecution jobExecution) {
log.info("\n");
log.info("====================================================");
log.info("========== ↓ [{}] Job start ===========", jobExecution.getJobInstance().getJobName());
log.info("====================================================");
}
@Override
public void afterJob(JobExecution jobExecution) {
log.info("===============================================================");
log.info("========== ↑ [{}] Job end :: {} ==========", jobExecution.getJobInstance().getJobName(), jobExecution.getStatus());
log.info("===============================================================");
log.info("\n");
}
}

@ -0,0 +1,40 @@
package kr.xit.core.biz.batch.listener;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.batch.core.listener.StepExecutionListenerSupport;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : step
*
* packageName : kr.xit.core.biz.batch.listener
* fileName : CustomStepListener
* author : limju
* date : 2023-05-16
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-16 limju
*
* </pre>
*/
@Slf4j
public class CustomStepListener extends StepExecutionListenerSupport {
@Override
public void beforeStep(StepExecution stepExecution) {
log.info("##### ↓ [{}] Step start", stepExecution.getStepName());
//log.info("##### [{}-{}] Step is start", stepExecution.getJobExecution().getJobInstance().getJobName(), stepExecution.getStepName());
}
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
log.info("##### ↑ [{}] Step end :: {}", stepExecution.getStepName(), stepExecution.getStatus());
//log.info("##### [{}-{}] Step is completed {}", stepExecution.getJobExecution().getJobInstance().getJobName(), stepExecution.getStepName(), stepExecution.getStatus());
return stepExecution.getExitStatus();
}
}

@ -0,0 +1,16 @@
package kr.xit.core.biz.batch.listener;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.listener.StepExecutionListenerSupport;
public class NoWorkFoundStepListener extends StepExecutionListenerSupport {
public ExitStatus afterStep(StepExecution stepExecution) {
if (stepExecution.getReadCount() == 0) {
return ExitStatus.FAILED;
}
return null;
}
}

@ -0,0 +1,32 @@
package kr.xit.core.biz.batch.mapper;
import java.util.Optional;
import org.egovframe.rte.psl.dataaccess.mapper.Mapper;
import kr.xit.core.biz.batch.model.BatchCmmDTO;
/**
* <pre>
* description :
*
* packageName : kr.xit.core.biz.batch.mapper
* fileName : IBatchCmmMapper
* author : limju
* date : 2023-05-16
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-16 limju
*
* </pre>
*/
@Mapper
public interface IBatchCmmMapper {
Optional<BatchCmmDTO> selectBatchLockByInstanceId(final String instanceId);
int insertBatchLock(final BatchCmmDTO dto);
int updateBatchLock(final BatchCmmDTO dto);
int insertBatchLog(final BatchCmmDTO dto);
int updateBatchLog(final BatchCmmDTO dto);
}

@ -0,0 +1,68 @@
package kr.xit.core.biz.batch.model;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* <pre>
* description :
*
* packageName : kr.xit.core.biz.batch.model
* fileName : BatchCmmDTO
* author : limju
* date : 2023-05-16
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-16 limju
* 2023-06-15 limju
*
* </pre>
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BatchCmmDTO {
/**
* ID
*/
private String batchLogId;
/**
* ID
*/
private String instanceId;
/**
* API trace ID
*/
private String traceId;
/**
*
*/
private String result;
/**
*
*/
private String message;
/**
*
*/
private String useYn;
//@Convert(converter = Jsr310.LocalDateTimeConverter.class)
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd kk:mm:ss.SSS")
private LocalDateTime registDt;
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd kk:mm:ss.SSS")
private LocalDateTime updateDt;
}

@ -0,0 +1,65 @@
package kr.xit.core.biz.batch.service;
import java.util.Optional;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import kr.xit.core.biz.batch.mapper.IBatchCmmMapper;
import kr.xit.core.biz.batch.model.BatchCmmDTO;
/**
* <pre>
* description :
*
* packageName : kr.xit.core.biz.service
* fileName : BatchLockService
* author : limju
* date : 2023-05-18
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-18 limju
*
* </pre>
*/
@Service
public class BatchCmmService extends EgovAbstractServiceImpl implements IBatchCmmService {
private final IBatchCmmMapper mapper;
public BatchCmmService(IBatchCmmMapper mapper) {
this.mapper = mapper;
}
@Override
@Transactional(readOnly = true)
public Optional<BatchCmmDTO> findById(final String instanceId) {
return mapper.selectBatchLockByInstanceId(instanceId);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addBatchLock(final BatchCmmDTO dto) {
mapper.insertBatchLock(dto);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void modifyBatchLock(final BatchCmmDTO dto) {
mapper.updateBatchLock(dto);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addBatchLog(final BatchCmmDTO dto) {
mapper.insertBatchLog(dto);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void modifyBatchLog(final BatchCmmDTO dto) {
mapper.updateBatchLog(dto);
}
}

@ -0,0 +1,29 @@
package kr.xit.core.biz.batch.service;
import java.util.Optional;
import kr.xit.core.biz.batch.model.BatchCmmDTO;
/**
* <pre>
* description :
*
* packageName : kr.xit.core.biz.service
* fileName : IBatchCmmService
* author : limju
* date : 2023-05-18
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-18 limju
*
* </pre>
*/
public interface IBatchCmmService {
Optional<BatchCmmDTO> findById(final String instanceId);
void addBatchLock(final BatchCmmDTO dto);
void modifyBatchLock(final BatchCmmDTO dto);
void addBatchLog(final BatchCmmDTO dto);
void modifyBatchLog(final BatchCmmDTO dto);
}

@ -0,0 +1,57 @@
package kr.xit.core.biz.batch.task;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import kr.xit.core.biz.batch.model.BatchCmmDTO;
import kr.xit.core.biz.batch.service.IBatchCmmService;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : lock table update
* (use_yn) (N)
*
* packageName : kr.xit.core.biz.batch.task
* fileName : BatchEndTasklet
* author : limju
* date : 2023-05-16
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-16 limju
*
* </pre>
* @see kr.xit.core.aop.TraceLoggerAspect
*/
@Slf4j
public class BatchEndTasklet implements Tasklet {
private final IBatchCmmService batchCmmService;
public BatchEndTasklet(IBatchCmmService batchCmmService) {
this.batchCmmService = batchCmmService;
}
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
String jobName = contribution.getStepExecution().getJobExecution().getJobInstance().getJobName();
contribution.setExitStatus(ExitStatus.COMPLETED);
BatchCmmDTO dto = BatchCmmDTO
.builder()
.instanceId(jobName)
.result(contribution.getExitStatus().getExitCode())
.useYn("N")
.batchLogId(MDC.get("batch_log_id"))
.traceId(StringUtils.defaultString(MDC.get("request_trace_id"), MDC.get("request_trace_batch_id")))
.build();
log.info("@@@@@@@@@@@@@{}", MDC.getCopyOfContextMap());
batchCmmService.modifyBatchLock(dto);
batchCmmService.modifyBatchLog(dto);
return RepeatStatus.FINISHED;
}
}

@ -0,0 +1,59 @@
package kr.xit.core.biz.batch.task;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import kr.xit.core.biz.batch.model.BatchCmmDTO;
import kr.xit.core.biz.batch.service.IBatchCmmService;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : lock table update
* (use_yn) (N)
*
* packageName : kr.xit.core.biz.batch.task
* fileName : BatchEndTasklet
* author : limju
* date : 2023-05-16
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-16 limju
*
* </pre>
* @see kr.xit.core.aop.TraceLoggerAspect
*/
@Slf4j
public class BatchFailEndTasklet implements Tasklet {
private final IBatchCmmService batchCmmService;
public BatchFailEndTasklet(IBatchCmmService batchCmmService) {
this.batchCmmService = batchCmmService;
}
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
String jobName = contribution.getStepExecution().getJobExecution().getJobInstance().getJobName();
contribution.setExitStatus(ExitStatus.FAILED);
BatchCmmDTO dto = BatchCmmDTO
.builder()
.instanceId(jobName)
.result(contribution.getExitStatus().getExitCode())
.useYn("N")
.batchLogId(MDC.get("batch_log_id"))
.traceId(StringUtils.defaultString(MDC.get("request_trace_id"), MDC.get("request_trace_batch_id")))
.build();
log.info("@@@@@@@@@@@@@{}", MDC.getCopyOfContextMap());
batchCmmService.modifyBatchLock(dto);
batchCmmService.modifyBatchLog(dto);
return RepeatStatus.FINISHED;
}
}

@ -0,0 +1,75 @@
package kr.xit.core.biz.batch.task;
import org.slf4j.MDC;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import kr.xit.core.biz.batch.model.BatchCmmDTO;
import kr.xit.core.biz.batch.service.IBatchCmmService;
import kr.xit.core.support.utils.Checks;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : lock table insert or update
* (use_yn) (Y) ()
* -> (Y)
*
* packageName : kr.xit.core.biz.batch.task
* fileName : BatchStartTasklet
* author : limju
* date : 2023-05-16
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-16 limju
* 2023-08-21 limju lock release :
* </pre>
* @see kr.xit.core.aop.TraceLoggerAspect
*/
@Slf4j
public class BatchStartTasklet implements Tasklet {
private final IBatchCmmService batchCmmService;
public BatchStartTasklet(IBatchCmmService batchCmmService) {
this.batchCmmService = batchCmmService;
}
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
String jobName = contribution.getStepExecution().getJobExecution().getJobInstance().getJobName();
BatchCmmDTO dto = batchCmmService.findById(jobName)
.orElseGet(()-> BatchCmmDTO
.builder()
.build());
if(Checks.isEmpty(dto.getInstanceId())){
dto.setInstanceId(jobName);
dto.setResult(null);
dto.setUseYn("Y");
batchCmmService.addBatchLock(dto);
batchCmmService.addBatchLog(dto);
MDC.put("batch_log_id", dto.getBatchLogId());
}else{
if("Y".equals(dto.getUseYn())){
log.error("======= [{}] 배치 실행(사용) 중으로 종료 =======", jobName);
contribution.setExitStatus(ExitStatus.FAILED);
dto.setResult("배치 실행(사용) 중으로 종료");
// lock이 걸린 경우 reset : 2023-08-21
dto.setUseYn("N");
batchCmmService.modifyBatchLock(dto);
batchCmmService.addBatchLog(dto);
}else{
dto.setUseYn("Y");
dto.setResult(null);
batchCmmService.modifyBatchLock(dto);
batchCmmService.addBatchLog(dto);
MDC.put("batch_log_id", dto.getBatchLogId());
}
}
return RepeatStatus.FINISHED;
}
}

@ -0,0 +1,28 @@
package kr.xit.core.biz.mapper;
import org.egovframe.rte.psl.dataaccess.mapper.Mapper;
import egovframework.com.cmm.LoginVO;
/**
* <pre>
* description :
*
* packageName : kr.xit.core.biz.mapper
* fileName : IAuthApiMapper
* author : limju
* date : 2023-05-11
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-11 limju
*
* </pre>
*/
@Mapper
public interface IAuthApiMapper {
LoginVO actionLogin(LoginVO vo);
// LoginVO searchId(LoginVO vo);
// LoginVO searchPassword(LoginVO vo);
// void updatePassword(LoginVO vo);
}

@ -0,0 +1,32 @@
package kr.xit.core.biz.mapper;
import java.util.List;
import org.egovframe.rte.psl.dataaccess.mapper.Mapper;
import kr.xit.core.biz.model.LoggingDTO;
/**
* <pre>
* description : API db logging
*
* packageName : kr.xit.core.biz.mapper
* fileName : ILoggingMapper
* author : limju
* date : 2023-05-11
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-11 limju
*
* </pre>
*/
@Mapper
public interface ILoggingMapper {
List<LoggingDTO> selectLogging(final LoggingDTO reqDTO);
LoggingDTO selectLogging();
void saveLogging(final LoggingDTO reqDTO);
void updateLogging(final LoggingDTO reqDTO);
}

@ -0,0 +1,57 @@
package kr.xit.core.biz.model;
import java.io.Serializable;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
/**
* <pre>
* description : Audit column ( )
*
* packageName : kr.xit.core.biz.model
* fileName : AuditFields
* author : limju
* date : 2023-06-05
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-05 limju
*
* </pre>
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class AuditFields {
/**
*
*/
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd kk:mm:ss")
private LocalDateTime registDt;
/**
*
*/
private String register;
/**
*
*/
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd kk:mm:ss")
private LocalDateTime updtDt;
/**
*
*/
private String updusr;
}

@ -0,0 +1,103 @@
package kr.xit.core.biz.model;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* <pre>
* description :
*
* packageName : kr.xit.core.biz.model
* fileName : LoggingDTO
* author : limju
* date : 2023-05-11
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-11 limju
*
* </pre>
*/
@Schema(name = "LoggingDTO", description = "API 로깅 DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class LoggingDTO {
/**
* ID
*/
private String requestId;
/**
* ID(ENS|FIMS)
*/
private String systemId;
/**
* ID(KAKAO|KT)
*/
private String reqSystemId;
/**
* (GET|PUT|POST|DELETE)
*/
private String method;
/**
* URI
*/
private String uri;
/**
* /(true|false)
*/
private String success;
/**
*
*/
private String param;
/**
*
*/
private String response;
/**
* ()
*/
private String message;
/**
* IP
*/
private String ip;
/**
*
*/
private String accessToken;
/**
* ID
*/
private String sessionId;
/**
* (now(3)-)
*/
//private java.sql.Timestamp updtDt;
/**
*
*/
private String updtId;
/**
* (now(3)-)
*/
//private java.sql.Timestamp registDt;
/**
*
*/
//@Convert(converter = Jsr310.LocalDateTimeConverter.class)
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd kk:mm:ss.SSS")
private String registId;
}

@ -0,0 +1,42 @@
package kr.xit.core.biz.service;
import javax.annotation.Resource;
import egovframework.com.cmm.util.EgovFileScrty;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.stereotype.Service;
import egovframework.com.cmm.LoginVO;
import kr.xit.core.biz.mapper.IAuthApiMapper;
@Service
public class AuthApiService extends EgovAbstractServiceImpl implements IAuthApiService {
@Resource
private IAuthApiMapper mapper;
/**
*
* @param vo LoginVO
* @return LoginVO
*/
@Override
public LoginVO actionLogin(LoginVO vo) {
// 1. 입력한 비밀번호를 암호화한다.
String enpassword = EgovFileScrty.encryptPassword(vo.getPassword(), vo.getId());
vo.setPassword(enpassword);
// 2. 아이디와 암호화된 비밀번호가 DB와 일치하는지 확인한다.
LoginVO loginVO = mapper.actionLogin(vo); //loginDAO.actionLogin(vo);
// 3. 결과를 리턴한다.
if (loginVO != null && !loginVO.getId().equals("") && !loginVO.getPassword().equals("")) {
return loginVO;
} else {
loginVO = new LoginVO();
}
return loginVO;
}
}

@ -0,0 +1,32 @@
package kr.xit.core.biz.service;
import egovframework.com.cmm.LoginVO;
/**
*
* @author
* @since 2009.03.06
* @version 1.0
* @see
*
* <pre>
* << (Modification Information) >>
*
*
* ------- -------- ---------------------------
* 2009.03.06
* 2011.08.31 JJY 릿
*
* </pre>
*/
public interface IAuthApiService {
/**
*
*
* @param vo LoginVO
* @return LoginVO
*/
LoginVO actionLogin(LoginVO vo);
}

@ -0,0 +1,27 @@
package kr.xit.core.biz.service;
import java.util.List;
import kr.xit.core.biz.model.LoggingDTO;
/**
* <pre>
* description :
*
* packageName : kr.xit.core.biz.service
* fileName : ILoggingService
* author : limju
* date : 2023-05-11
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-11 limju
*
* </pre>
*/
public interface ILoggingService {
List<LoggingDTO> findLogging(LoggingDTO reqDTO);
LoggingDTO findLogging();
void saveLogging(LoggingDTO reqDTO);
void modifyLogging(LoggingDTO reqDTO);
}

@ -0,0 +1,60 @@
package kr.xit.core.biz.service;
import java.util.List;
import javax.transaction.Transactional;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.stereotype.Service;
import kr.xit.core.biz.mapper.ILoggingMapper;
import kr.xit.core.biz.model.LoggingDTO;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description :
*
* packageName : kr.xit.core.biz.service
* fileName : LoggingServiceImpl
* author : limju
* date : 2023-05-11
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-11 limju
*
* </pre>
*/
@Slf4j
@Service
public class LoggingService extends EgovAbstractServiceImpl implements ILoggingService{
private final ILoggingMapper mapper;
public LoggingService(ILoggingMapper mapper) {
this.mapper = mapper;
}
@Transactional
@Override
public void saveLogging(final LoggingDTO reqDTO){
mapper.saveLogging(reqDTO);
}
@Transactional
@Override
public void modifyLogging(final LoggingDTO reqDTO){
mapper.updateLogging(reqDTO);
}
@Override
public List<LoggingDTO> findLogging(final LoggingDTO reqDTO) {
return mapper.selectLogging(reqDTO);
}
@Override
public LoggingDTO findLogging() {
return mapper.selectLogging();
}
}

@ -0,0 +1,184 @@
package kr.xit.core.biz.web;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import egovframework.com.cmm.EgovMessageSource;
import egovframework.com.cmm.LoginVO;
import egovframework.com.cmm.jwt.config.EgovJwtTokenUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.tags.Tag;
import kr.xit.core.model.ApiResponseDTO;
import kr.xit.core.biz.service.IAuthApiService;
import kr.xit.core.consts.Constants;
import lombok.RequiredArgsConstructor;
/**
* <pre>
* description :
*
* packageName : kr.xit.biz.auth
* fileName : AuthApiController
* author : limju
* date : 2023-04-26
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-04-26 limju
*
* </pre>
* @see
*/
//FIXME::세션에서 인증 관리 할지 여부 결정 필요
@Tag(name = "AuthApiController", description = "인증 관리")
@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/api/core/auth")
public class AuthApiController {
@Value("${app.token.saveType:header}")
private String authSaveType;
/** EgovLoginService */
private final IAuthApiService loginService;
/** EgovMessageSource */
private final EgovMessageSource egovMessageSource;
private final EgovJwtTokenUtil egovJwtTokenUtil;
/**
*
* @param loginVO , LoginVO
* @param request HttpServletRequest
* @return ()
*/
@Operation(summary = "로그인" , description = "로그인")
@io.swagger.v3.oas.annotations.parameters.RequestBody(
required = true,
content = {
@Content(
mediaType = "application/json",
examples = {
@ExampleObject(
value = "{\"id\":\"admin\",\"password\":\"1\",\"userSe\":\"USR\"}")
}
)
}
)
@PostMapping(value = "/login", consumes = {MediaType.APPLICATION_JSON_VALUE , MediaType.TEXT_HTML_VALUE})
public ApiResponseDTO<?> login(@RequestBody final LoginVO loginVO, HttpServletRequest request) {
// 1. 일반 로그인 처리
LoginVO loginResultVO = loginService.actionLogin(loginVO);
if (loginResultVO != null && loginResultVO.getId() != null && !loginResultVO.getId().equals("")) {
request.getSession().setAttribute(Constants.AuthSaveSession.LOGIN_VO.getCode(), loginResultVO);
return ApiResponseDTO.success(loginResultVO);
}
return ApiResponseDTO.success(egovMessageSource.getMessage("fail.common.login"));
}
@Operation(summary = "로그인(JWT)" , description = "로그인(JWT)")
@io.swagger.v3.oas.annotations.parameters.RequestBody(
required = true,
content = {
@Content(
mediaType = "application/json",
examples = {
@ExampleObject(
name = "admin",
description = "admin",
value = "{\"id\":\"admin\",\"password\":\"1\",\"userSe\":\"USR\"}"),
@ExampleObject(
name = "admin1",
description = "admin1",
value = "{\"id\":\"admin1\",\"password\":\"1\",\"userSe\":\"USR\"}"),
@ExampleObject(
name = "admin2",
description = "admin2",
value = "{\"id\":\"admin2\",\"password\":\"1\",\"userSe\":\"USR\"}")
}
)
}
)
@PostMapping(value = "/loginJwt")
public ApiResponseDTO<?> loginJWT(@RequestBody final LoginVO loginVO, HttpServletRequest request) {
HashMap<String, Object> resultMap = new HashMap<String, Object>();
// 1. 일반 로그인 처리
LoginVO loginResultVO = loginService.actionLogin(loginVO);
if (loginResultVO != null && loginResultVO.getId() != null && !loginResultVO.getId().equals("")) {
Map<String, Object> claimsMap = new HashMap<>();
// claimsMap.put("dkkdk", "kdkkdkdkd");
String jwtToken = egovJwtTokenUtil.generateToken(loginVO, claimsMap);
// String jwtToken = egovJwtTokenUtil.generateToken(loginVO.getId());
String username = egovJwtTokenUtil.getUsernameFromToken(jwtToken);
// System.out.println("Dec jwtToken username = "+username);
//서버사이드 권한 체크 통과를 위해 삽입
//EgovUserDetailsHelper.isAuthenticated() 가 그 역할 수행. DB에 정보가 없으면 403을 돌려 줌. 로그인으로 튕기는 건 프론트 쪽에서 처리
request.getSession().setAttribute(Constants.AuthSaveSession.LOGIN_VO.getCode(), loginResultVO);
// UsernamePasswordAuthenticationToken authenticationToken = jwtTokenProvider.toAuthentication(loginVO.getId(), loginVO.getPassword());
// Authentication authentication = authenticationManager.authenticate(authenticationToken);
//
//
// // Authentication 저장
// if(Objects.equals(authSaveType, Constants.AuthSaveType.SECURITY.getCode())){
// // TODO :: SessionCreationPolicy.STATELESS 인 경우 사용 불가
// SecurityContextHolder.getContext().setAuthentication(authentication);
//
// }else if(Objects.equals(authSaveType, Constants.AuthSaveType.SESSION.getCode())){
// session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
// }
//Map<String, Object> infoMap = new HashMap<>();
//infoMap.put(Constants.JwtToken.TOKEN_USER_ID.getCode(), loginVO.getId());
//infoMap.put(Constants.JwtToken.TOKEN_USER_MAIL.getCode(), loginVO.getEmail());
//String jwtToken = jwtTokenProvider.generateJwtAccessToken(authentication, infoMap);
//String jwtToken = jwtTokenProvider.generateJwtAccessToken(loginVO.getId(), "ROLE_USER");
resultMap.put("resultVO", loginResultVO);
resultMap.put("token", jwtToken);
return ApiResponseDTO.success(resultMap);
}
return ApiResponseDTO.error(egovMessageSource.getMessage("fail.common.login") );
}
/**
* .
* @return resultVO
* @exception Exception
*/
@Operation(summary = "logout" , description = "로그아웃")
@GetMapping(value = "/logout")
public ApiResponseDTO<?> actionLogoutJSON(HttpServletRequest request) {
RequestContextHolder.currentRequestAttributes().removeAttribute(Constants.AuthSaveSession.LOGIN_VO.getCode(), RequestAttributes.SCOPE_SESSION);
return ApiResponseDTO.empty();
}
}

@ -0,0 +1,60 @@
package kr.xit.core.biz.web;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.tags.Tag;
import kr.xit.core.model.ApiResponseDTO;
import kr.xit.core.biz.model.LoggingDTO;
import kr.xit.core.biz.service.ILoggingService;
import lombok.RequiredArgsConstructor;
/**
* <pre>
* description : API Logging cotroller
*
* packageName : kr.xit.core.biz.web
* fileName : LoggingController
* author : limju
* date : 2023-05-11
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-11 limju
*
* </pre>
*/
@Tag(name = "LoggingController", description = "API logging 관리")
@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/api/core/log")
public class LoggingController {
private final ILoggingService loggingService;
@Operation(summary = "로깅 정보 저장" , description = "로깅 정보 저장")
@io.swagger.v3.oas.annotations.parameters.RequestBody(
required = true,
content = {
@Content(
mediaType = "application/json",
examples = {
@ExampleObject(
value = "{\"requestId\":\"7f1f5978fc1941c4a6d37351a6d692e6\"}")
}
)
}
)
@PostMapping(value = "/save", consumes = {MediaType.APPLICATION_JSON_VALUE , MediaType.TEXT_HTML_VALUE})
public ApiResponseDTO saveLogging(@RequestBody final LoggingDTO reqDTO){
loggingService.saveLogging(reqDTO);
return ApiResponseDTO.empty();
}
}

@ -0,0 +1,55 @@
package kr.xit.core.spring.config;
import org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* <pre>
* description : Spring batch Meta table :
* @EnableBatchProcessing -> BatchConfigurationSelector import
* BatchConfigurationSelector -> SimpleBatchConfiguration
* SimpleBatchConfiguration -> BatchConfigurer
* - jobRepository, jobLauncher, transactionManager, jobRegistry, jobExplorer
* - @Bean
* - DefaultBatchConfigurer
*
* - DefaultBatchConfigurer
* - EnableBatchProcessing
* - setDatasource override
*
* fileName : CustomBatchConfigurer
* author : julim
* date : 2023-04-28
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-04-28 julim
*
* </pre>
*/
@Configuration
@EnableBatchProcessing
@EnableScheduling
public class CustomBatchConfigurer extends DefaultBatchConfigurer {
/*
@Override
public void setDataSource(DataSource dataSource) {
// Do nothing
}
*/
/**
* FIXME::JPA
* @param emf
* @return
*/
// @Bean
//@Primary // SimpleBatchConfiguration 에서 생성된 빈 대체
// public PlatformTransactionManager jpaTransactionManager(EntityManagerFactory emf) {
// return new JpaTransactionManager(emf);
// }
}

@ -0,0 +1,90 @@
package kr.xit.core.spring.config;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* <pre>
* description : Springdoc(swagger)
*
* packageName : kr.xit.core.spring.config
* fileName : SpringDocsConfig
* author : julim
* date : 2023-04-28
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-04-28 julim
*
* </pre>
*/
@ConditionalOnProperty(value = "springdoc.swagger-ui.enabled", havingValue = "true", matchIfMissing = false)
@Configuration
public class SpringDocsApiConfig {
@Bean
public GroupedOpenApi authentification() {
return GroupedOpenApi.builder()
.group("1. Core API")
.pathsToMatch(
"/api/core/**"
)
.build();
}
@Bean
public GroupedOpenApi kakaopayEltrcDoc() {
return GroupedOpenApi.builder()
.group("2. 카카오페이 MyDoc API")
.pathsToMatch(
"/api/kakaopay/v1/**",
"/api/kakaopay/v2/**"
)
.build();
}
@Bean
public GroupedOpenApi kakaopayEltrcDocBatch() {
return GroupedOpenApi.builder()
.group("3. 전자고지 통합발송 연계 Batch WEB")
.pathsToMatch(
"/api/batch/v1/**"
)
.build();
}
@Bean
public GroupedOpenApi bizDoc() {
return GroupedOpenApi.builder()
.group("4. 전자고지 통합발송 연계 API")
.pathsToMatch(
"/api/ens/v1/**"
)
.build();
}
@Bean
public GroupedOpenApi pniDoc() {
return GroupedOpenApi.builder()
.group("6. 사전고지 API")
.pathsToMatch(
"/api/pni/v1/**"
)
.build();
}
@Bean
public GroupedOpenApi kakaopayDummyTest() {
return GroupedOpenApi.builder()
.group("9. 카카오페이 MyDoc dummy 테스트")
.pathsToMatch(
"/api/kakaopay/test/**",
"/api/sample/**"
)
.build();
}
}

@ -0,0 +1,216 @@
package kr.xit.core.spring.config;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.filter.CommonsRequestLoggingFilter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import kr.xit.core.consts.Constants;
import kr.xit.core.spring.config.auth.AuthentificationInterceptor;
import kr.xit.core.spring.config.properties.CorsProperties;
import kr.xit.core.spring.filter.LoggingFilter;
import kr.xit.core.spring.filter.ReadableRequestWrapperFilter;
import kr.xit.core.spring.filter.SimpleCORSFilter;
import kr.xit.core.spring.resolver.CustomArgumentResolver;
import kr.xit.core.spring.resolver.PageableArgumentResolver;
import lombok.RequiredArgsConstructor;
/**
* <pre>
* description : Spring MVC
* - filter, interceptor
* - AuthentificationInterceptor :
* - CommonsRequestLoggingFilter : request logging
* - ReadableRequestWrapperFilter : post logging
* - SimpleCORSFilter : cors
* packageName : kr.xit.core.spring.config
* fileName : WebMvcConfig
* author : julim
* date : 2023-04-28
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-04-28 julim
*
* </pre>
* @see AuthentificationInterceptor
* @see CommonsRequestLoggingFilter
* @see ReadableRequestWrapperFilter
* @see SimpleCORSFilter
* @see LoggingFilter
*/
@RequiredArgsConstructor
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* logging exclude path
*/
@Value("${app.param.log.exclude-patterns}")
private List<String> EXCLUDE_URL_REGEXS;
private final CorsProperties corsProperties;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthentificationInterceptor())
.addPathPatterns("/**/*")
.excludePathPatterns(
"/api/core/*"
// "/api/v1/kakaopay/*"
);
}
// -------------------------------------------------------------
// RequestMappingHandlerMapping 설정 View Controller 추가
// -------------------------------------------------------------
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/cmmn/validator.do")
.setViewName("cmmn/validator");
registry.addViewController("/").setViewName("forward:/index.html");
}
//TODO :: ArgumentResolver add
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new CustomArgumentResolver());
resolvers.add(new PageableArgumentResolver());
//WebMvcConfigurer.super.addArgumentResolvers(resolvers);
}
/**
* CommonsRequestLoggingFiler
* app.param.log.enabled: true
* @return
*/
@ConditionalOnProperty(value = "app.param.log.enabled", havingValue = "true", matchIfMissing = false)
@Bean
public FilterRegistrationBean requestLoggingFilter() {
CommonsRequestLoggingFilter loggingFilter = new CommonsRequestLoggingFilter(){
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
String path = request.getServletPath();
return EXCLUDE_URL_REGEXS.stream().anyMatch(regex -> path.matches(regex));
}
};
loggingFilter.setIncludeClientInfo(true);
loggingFilter.setIncludeHeaders(false);
loggingFilter.setBeforeMessagePrefix("\n//========================== Request(Before) ================================\n");
loggingFilter.setBeforeMessageSuffix("\n//===========================================================================");
loggingFilter.setIncludeQueryString(true);
loggingFilter.setIncludePayload(true);
loggingFilter.setMaxPayloadLength(1024* 1024);
loggingFilter.setAfterMessagePrefix("\n//=========================== Request(After) ================================\n");
loggingFilter.setAfterMessageSuffix("\n//===========================================================================");
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>(loggingFilter);
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
bean.addUrlPatterns(Constants.API_URL_PATTERNS);
return bean;
}
/**
* exclude pattern
* @return FilterRegistrationBean
*/
@ConditionalOnProperty(value = "app.param.log.custom.enabled", havingValue = "true", matchIfMissing = false)
@Bean
public FilterRegistrationBean loggingFilter() {
Map<String, String> initMap = new HashMap<>();
initMap.put("excludedUrls", StringUtils.join(EXCLUDE_URL_REGEXS,","));
FilterRegistrationBean<Filter> frb = new FilterRegistrationBean<>(new LoggingFilter());
frb.setOrder(1);
frb.addUrlPatterns(Constants.API_URL_PATTERNS);
frb.setInitParameters(initMap);
frb.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC);
return frb;
}
/**
* Post request(stream) logging
* @return
*/
@ConditionalOnProperty(value = "app.param.log.custom.enabled", havingValue = "true", matchIfMissing = false)
@Bean
public FilterRegistrationBean readableRequestWrapperFilter() {
ReadableRequestWrapperFilter readableRequestWrapperFilter = new ReadableRequestWrapperFilter();
FilterRegistrationBean bean = new FilterRegistrationBean(readableRequestWrapperFilter);
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
bean.addUrlPatterns(Constants.API_URL_PATTERNS);
return bean;
}
/**
* CORS Filter
* @return
*/
@Bean
public FilterRegistrationBean simpleCorsFilter() {
SimpleCORSFilter corsFilter = new SimpleCORSFilter();
FilterRegistrationBean bean = new FilterRegistrationBean(corsFilter);
bean.setOrder(Ordered.LOWEST_PRECEDENCE);
bean.addUrlPatterns(Constants.API_URL_PATTERNS);
return bean;
// CorsConfiguration config = new CorsConfiguration();
// config.setAllowedOrigins(Collections.singletonList(corsProperties.getAllowedOrigins()));
// config.setAllowedMethods(Collections.singletonList(corsProperties.getAllowedMethods()));
// config.setAllowedHeaders(Collections.singletonList(corsProperties.getAllowedHeaders()));
// config.setAllowCredentials(corsProperties.getAllowCredentials());
// config.setMaxAge(corsProperties.getMaxAge());
// config.setExposedHeaders(Collections.singletonList(corsProperties.getExposeHeader()));
//
// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// source.registerCorsConfiguration("/**", config);
//
// FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
// bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
// return bean;
}
// @Override
// public void addCorsMappings(CorsRegistry registry) {
// //WebMvcConfigurer.super.addCorsMappings(registry);
// registry.addMapping("/**")
// .allowedOrigins(String.valueOf(Collections.singletonList(corsProperties.getAllowedOrigins())))
// .allowedMethods(String.valueOf(Collections.singletonList(corsProperties.getAllowedMethods())));
// }
// /**
// * HandlerExceptionResolver 를 상속받은 resolver 등록
// * @param resolvers the list of configured resolvers to extend
// */
// @Override
// public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
// HandlerExceptionResolver exceptionHandlerExceptionResolver = resolvers.stream().filter(x -> x instanceof ExceptionHandlerExceptionResolver).findAny().get();
// int index = resolvers.indexOf(exceptionHandlerExceptionResolver);
// resolvers.add(index, new CustomRuntimeResolver());
// WebMvcConfigurer.super.extendHandlerExceptionResolvers(resolvers);
// }
}

@ -0,0 +1,73 @@
package kr.xit.core.spring.config.support;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import kr.xit.core.consts.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;
import javax.sql.DataSource;
//FIXME :: 재설정이 필요한 경우 해당 프로젝트에 동일한 파일로 재정의 하여 사용
/**
* <pre>
* description : Datasource - FIXME:: spring.datasource loading
* - : spring.datasource
*
* LazyConnectionDataSourceProxy
* Datasource
* - Spring get
* - ehcache, hibernate 1 get
* - multi-datasource datasource
* packageName : kr.xit.core.spring.config.support
* fileName : DatasourceConfig
* author : julim
* date : 2023-04-28
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-04-28 julim
*
* </pre>
* @see PrimaryMybatisConfig
*/
//@ConditionalOnProperty(value = "spring", havingValue = "datasource", matchIfMissing = false)
@Slf4j
@Configuration
public class DatasourceConfig {
@Bean(name = "primaryHikariConfig")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.hikari.maria")
public HikariConfig primaryHikariConfig() {
// HikariConfig hikariConfig = new HikariConfig("spring.datasource.hikari");
// hikariConfig.setAutoCommit(false);
// return hikariConfig;
return new HikariConfig();
}
@Bean(Constants.PRIMARY_DATA_SOURCE)
@Primary
public DataSource primaryDataSource() throws Exception{
//return new LazyConnectionDataSourceProxy(new HikariDataSource(primaryHikariConfig()));
return new HikariDataSource(primaryHikariConfig());
}
@Bean(name = "secondaryHikariConfig")
@ConfigurationProperties(prefix = "spring.datasource.hikari.oracle")
public HikariConfig secondaryHikariConfig() {
// HikariConfig hikariConfig = new HikariConfig("spring.datasource.hikari");
// hikariConfig.setAutoCommit(false);
// return hikariConfig;
return new HikariConfig();
}
@Bean(name = Constants.SECONDARY_DATA_SOURCE)
public DataSource secondaryDataSource() throws Exception{
//return new LazyConnectionDataSourceProxy(new HikariDataSource(secondaryHikariConfig()));
return new HikariDataSource(secondaryHikariConfig());
}
}

@ -0,0 +1,76 @@
package kr.xit.core.spring.config.support;
import kr.xit.core.consts.Constants;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.*;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
//FIXME :: 재설정이 필요한 경우 해당프로젝트에 동일한 파일로 재정의 하여 사용
/**
* <pre>
* description : Mybatis - FIXME:: @DependsOn(value = {"dataSource"}) loading
* - : @DependsOn(value = {"dataSource"})
* packageName : kr.xit.core.spring.config.support
* fileName : MybatisConfig
* author : julim
* date : 2023-04-28
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-04-28 julim
*
* </pre>
* @see DatasourceConfig
*/
@Slf4j
@Configuration
@MapperScan(
basePackages = {
"kr.xit.core.biz.mapper",
"kr.xit.core.biz.batch.mapper",
"kr.xit.biz.ens.mapper",
"kr.xit.biz.pni.mapper",
"kr.xit.biz.sample.mapper",
},
sqlSessionFactoryRef = Constants.PRIMARY_SQL_SESSION
)
public class PrimaryMybatisConfig {
static final String MYBATIS_CONFIG_FILE = "classpath:/egovframework/mapper/mapper-config.xml";
@ConditionalOnMissingBean
@Bean
@Lazy
public DefaultLobHandler lobHandler() {
return new DefaultLobHandler();
}
@Primary
@Bean(name = Constants.PRIMARY_SQL_SESSION)
public SqlSessionFactory primarySqlSession(@Qualifier(Constants.PRIMARY_DATA_SOURCE)DataSource dataSource) throws Exception {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setConfigLocation(resolver.getResource(MYBATIS_CONFIG_FILE));
sessionFactory.setMapperLocations(resolver.getResources("classpath:/egovframework/mapper/**/*-mysql-mapper.xml"));
return sessionFactory.getObject();
}
@Primary
@Bean(name = "primarySqlSessionTemplate")
public SqlSessionTemplate primarySqlSessionTemplate(@Qualifier(Constants.PRIMARY_SQL_SESSION) SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}

@ -0,0 +1,53 @@
package kr.xit.core.spring.config.support;
import kr.xit.core.exception.BizRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler;
import java.io.IOException;
/**
* <pre>
* description : RestTemplate Error Handler
* packageName : kr.xit.core.spring.config.support
* fileName : RestResponseErrorHandler
* author : julim
* date : 2023-04-28
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-04-28 julim
*
* </pre>
* @see kr.xit.core.spring.config.RestTemplateConfig#restTemplate
*/
@Slf4j
public class RestResponseErrorHandler extends DefaultResponseErrorHandler {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
String seriesName = "UNKNOWN_ERROR";
if (response.getStatusCode() == HttpStatus.PRECONDITION_FAILED) {
seriesName = String.valueOf(HttpStatus.PRECONDITION_FAILED.value());
}else if (response.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR) {
// handle SERVER_ERROR
seriesName = HttpStatus.Series.SERVER_ERROR.name();
} else if (response.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR) {
// handle CLIENT_ERROR
seriesName = HttpStatus.Series.CLIENT_ERROR.name();
if (response.getStatusCode() == HttpStatus.NOT_FOUND) {
seriesName = String.valueOf(HttpStatus.NOT_FOUND.value());
}
}
log.error("========================== RestResponse Error ==============================");
log.error("SERIES NAME : {}", seriesName);
log.error("STATUS CODE : {}", response.getStatusCode());
log.error("=============================================================================");
//throw BizRuntimeException.create(String.valueOf(response.getStatusCode().value()), null);
}
}

@ -0,0 +1,66 @@
package kr.xit.core.spring.config.support;
import kr.xit.core.consts.Constants;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
//FIXME :: 재설정이 필요한 경우 해당프로젝트에 동일한 파일로 재정의 하여 사용
/**
* <pre>
* description : Mybatis - FIXME:: @DependsOn(value = {"dataSource"}) loading
* - : @DependsOn(value = {"dataSource"})
* packageName : kr.xit.core.spring.config.support
* fileName : MybatisConfig
* author : julim
* date : 2023-04-28
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-04-28 julim
*
* </pre>
* @see DatasourceConfig
*/
@Slf4j
@Configuration
@MapperScan(
basePackages = {"kr.xit.biz.sms.mapper"},
sqlSessionFactoryRef = Constants.SECONDARY_SQL_SESSION
)
public class SecondaryMybatisConfig {
static final String MYBATIS_CONFIG_FILE = "classpath:/egovframework/mapper/mapper-config.xml";
@Bean(name = Constants.SECONDARY_SQL_SESSION)
public SqlSessionFactory secondarySqlSession(@Qualifier(Constants.SECONDARY_DATA_SOURCE) DataSource dataSource) throws Exception {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setConfigLocation(resolver.getResource(MYBATIS_CONFIG_FILE));
sessionFactory.setMapperLocations(resolver.getResources("classpath:/egovframework/mapper/**/*-oracle-mapper.xml"));
return sessionFactory.getObject();
}
@Bean(name = "secondarySqlSessionTemplate")
public SqlSessionTemplate secondarySqlSessionTemplate(@Qualifier(Constants.SECONDARY_SQL_SESSION) SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}

@ -0,0 +1,64 @@
package kr.xit.core.spring.config.support;
import kr.xit.core.consts.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.transaction.ChainedTransactionManager;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
//FIXME :: 재설정이 필요한 경우 해당프로젝트에 동일한 파일로 재정의 하여 사용
/**
* <pre>
* description : Mybatis - FIXME:: @DependsOn(value = {"dataSource"}) loading
* - : @DependsOn(value = {"dataSource"})
* packageName : kr.xit.core.spring.config.support
* fileName : MybatisConfig
* author : julim
* date : 2023-04-28
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-04-28 julim
*
* </pre>
* @see DatasourceConfig
*/
@Slf4j
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
////////////////////////////////////////////////////////////////////////////////////////////
// ChainedTransactionManager : trsnsaction binding
///////////////////////////////////////////////////////////////////////////////////////////
/**
* mariaDB & oracleDB Transaction binding
* @param mariaDS Maria DataSource
* @param oracleDS Oracle DataSource
* @return PlatformTransactionManager
* @throws Exception Exception
*/
@Primary
@Bean
public PlatformTransactionManager transactionManager(@Qualifier(Constants.PRIMARY_DATA_SOURCE)DataSource mariaDS,
@Qualifier(Constants.SECONDARY_DATA_SOURCE)DataSource oracleDS) {
DataSourceTransactionManager mariaTm = new DataSourceTransactionManager(mariaDS);
mariaTm.setGlobalRollbackOnParticipationFailure(false);
mariaTm.setNestedTransactionAllowed(true);
DataSourceTransactionManager oracleTm = new DataSourceTransactionManager(oracleDS);
oracleTm.setGlobalRollbackOnParticipationFailure(false);
oracleTm.setNestedTransactionAllowed(true);
// creates chained transaction manager
return new ChainedTransactionManager(mariaTm, oracleTm);
}
/////////////////////////////////////////////////////////////////////////////////////
}

@ -0,0 +1,66 @@
package kr.xit.core.spring.util;
import kr.xit.biz.ens.service.ISendMessageLinkService;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.core.env.Environment;
import egovframework.com.cmm.jwt.config.EgovJwtTokenUtil;
import egovframework.com.cmm.jwt.config.JwtVerification;
import kr.xit.core.biz.batch.service.IBatchCmmService;
import kr.xit.core.biz.service.ILoggingService;
import kr.xit.core.spring.config.properties.CorsProperties;
import kr.xit.core.spring.config.support.ApplicationContextProvider;
import kr.xit.core.support.slack.SlackWebhookPush;
/**
* <pre>
* description : Get Bean Object
* Filter / Interceptor Bean
* (Bean @Autowired / @Resource )
* packageName : kr.xit.core.spring.util
* fileName : SpringUtils
* author : julim
* date : 2023-04-28
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-04-28 julim
*
* </pre>
* @see ApplicationContextProvider
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ApiSpringUtils {
public static ApplicationContext getApplicationContext() {
return ApplicationContextProvider.getApplicationContext();
}
public static boolean containsBean(String beanName) {
return getApplicationContext().containsBean(beanName);
}
public static Object getBean(String beanName) {
return getApplicationContext().getBean(beanName);
}
public static Object getBean(Class<?> clazz) {
return getApplicationContext().getBean(clazz);
}
public static IBatchCmmService getBatchCmmService(){
return (IBatchCmmService)getBean(IBatchCmmService.class);
}
public static ISendMessageLinkService getSendMessageLinkService(){
return (ISendMessageLinkService)getBean(ISendMessageLinkService.class);
}
public static SlackWebhookPush getSlackWebhookPush(){
return (SlackWebhookPush)getBean(SlackWebhookPush.class);
}
}

@ -0,0 +1,113 @@
package kr.xit.ens.support.batch.job;
import kr.xit.biz.ens.service.IEnsCctvFileService;
import kr.xit.core.biz.batch.CustomRunIdIncrementer;
import kr.xit.core.biz.batch.listener.CustomJobListener;
import kr.xit.core.biz.batch.listener.CustomStepListener;
import kr.xit.core.biz.batch.listener.NoWorkFoundStepListener;
import kr.xit.core.biz.batch.service.IBatchCmmService;
import kr.xit.core.biz.batch.task.BatchEndTasklet;
import kr.xit.core.biz.batch.task.BatchFailEndTasklet;
import kr.xit.core.biz.batch.task.BatchStartTasklet;
import kr.xit.ens.support.batch.task.EnsCctvAcceptTasklet;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* <pre>
* description : CCTV accept
*
* packageName : kr.xit.ens.support.batch.job
* fileName : EnsCctvAcceptJobConfg
* author : seojh
* date : 2023-07-17
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-17 seojh
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Configuration
public class EnsCctvAcceptJobConfg {
private static final String JOB_NAME = "EnsCctvAcceptJob";
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final IBatchCmmService lockService;
private final IEnsCctvFileService service;
@Bean(name = JOB_NAME)
public Job ensCctvAcceptJob() {
return jobBuilderFactory.get(JOB_NAME)
.incrementer(new CustomRunIdIncrementer())
.listener(new CustomJobListener())
.listener(new CustomStepListener())
.listener(new NoWorkFoundStepListener())
// JOB 시작
// start() 결과가 FAILED 인경우 종료
.start(start())
.on("FAILED")
.end()
// start()의 결과로 부터 FAILED를 제외한 모든 경우 to() 실행
//.from(start())
.on("*")
.to(execution())
.on("FAILED")
.to(fail())
.from(execution())
// to() 실행 결과와 상관 없이 end() 실행
.on("*")
.to(end())
// JOB 종료
.end()
.build();
}
@Bean(name = JOB_NAME + "_step")
public Step execution() {
return stepBuilderFactory.get(JOB_NAME + "_step")
.tasklet(new EnsCctvAcceptTasklet(service))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_start_step")
protected Step start() {
return stepBuilderFactory.get("Job_Locking")
.tasklet(new BatchStartTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_end_step")
protected Step end() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchEndTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_fail_step")
protected Step fail() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchFailEndTasklet(lockService))
.build();
}
}

@ -0,0 +1,113 @@
package kr.xit.ens.support.batch.job;
import kr.xit.biz.ens.service.IEnsCctvFileService;
import kr.xit.core.biz.batch.CustomRunIdIncrementer;
import kr.xit.core.biz.batch.listener.CustomJobListener;
import kr.xit.core.biz.batch.listener.CustomStepListener;
import kr.xit.core.biz.batch.listener.NoWorkFoundStepListener;
import kr.xit.core.biz.batch.service.IBatchCmmService;
import kr.xit.core.biz.batch.task.BatchEndTasklet;
import kr.xit.core.biz.batch.task.BatchFailEndTasklet;
import kr.xit.core.biz.batch.task.BatchStartTasklet;
import kr.xit.ens.support.batch.task.EnsCctvFileTasklet;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* <pre>
* description : CCTV ()
*
* packageName : kr.xit.ens.support.batch.job
* fileName : EnsCctvFileJobConfg
* author : seojh
* date : 2023-07-17
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-17 seojh
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Configuration
public class EnsCctvFileJobConfg {
private static final String JOB_NAME = "EnsCctvFileJob";
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final IBatchCmmService lockService;
private final IEnsCctvFileService service;
@Bean(name = JOB_NAME)
public Job ensCctvFileJob() {
return jobBuilderFactory.get(JOB_NAME)
.incrementer(new CustomRunIdIncrementer())
.listener(new CustomJobListener())
.listener(new CustomStepListener())
.listener(new NoWorkFoundStepListener())
// JOB 시작
// start() 결과가 FAILED 인경우 종료
.start(start())
.on("FAILED")
.end()
// start()의 결과로 부터 FAILED를 제외한 모든 경우 to() 실행
//.from(start())
.on("*")
.to(execution())
.on("FAILED")
.to(fail())
.from(execution())
// to() 실행 결과와 상관 없이 end() 실행
.on("*")
.to(end())
// JOB 종료
.end()
.build();
}
@Bean(name = JOB_NAME + "_step")
public Step execution() {
return stepBuilderFactory.get(JOB_NAME + "_step")
.tasklet(new EnsCctvFileTasklet(service))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_start_step")
protected Step start() {
return stepBuilderFactory.get("Job_Locking")
.tasklet(new BatchStartTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_end_step")
protected Step end() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchEndTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_fail_step")
protected Step fail() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchFailEndTasklet(lockService))
.build();
}
}

@ -0,0 +1,114 @@
package kr.xit.ens.support.batch.job;
import kr.xit.biz.pni.service.IPniCctvFileService;
import kr.xit.core.biz.batch.CustomRunIdIncrementer;
import kr.xit.core.biz.batch.listener.CustomJobListener;
import kr.xit.core.biz.batch.listener.CustomStepListener;
import kr.xit.core.biz.batch.listener.NoWorkFoundStepListener;
import kr.xit.core.biz.batch.service.IBatchCmmService;
import kr.xit.core.biz.batch.task.BatchEndTasklet;
import kr.xit.core.biz.batch.task.BatchFailEndTasklet;
import kr.xit.core.biz.batch.task.BatchStartTasklet;
import kr.xit.ens.support.batch.task.PniCctvAcceptTasklet;
import kr.xit.ens.support.batch.task.PniCctvFileTasklet;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* <pre>
* description : CCTV accept
*
* packageName : kr.xit.ens.support.batch.job
* fileName : PniCctvAcceptJobConfg
* author : limju
* date : 2023-07-11
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-11 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Configuration
public class PniCctvAcceptJobConfg {
private static final String JOB_NAME = "PniCctvAcceptJob";
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final IBatchCmmService lockService;
private final IPniCctvFileService service;
@Bean(name = JOB_NAME)
public Job pniCctvAcceptJob() {
return jobBuilderFactory.get(JOB_NAME)
.incrementer(new CustomRunIdIncrementer())
.listener(new CustomJobListener())
.listener(new CustomStepListener())
.listener(new NoWorkFoundStepListener())
// JOB 시작
// start() 결과가 FAILED 인경우 종료
.start(start())
.on("FAILED")
.end()
// start()의 결과로 부터 FAILED를 제외한 모든 경우 to() 실행
//.from(start())
.on("*")
.to(execution())
.on("FAILED")
.to(fail())
.from(execution())
// to() 실행 결과와 상관 없이 end() 실행
.on("*")
.to(end())
// JOB 종료
.end()
.build();
}
@Bean(name = JOB_NAME + "_step")
public Step execution() {
return stepBuilderFactory.get(JOB_NAME + "_step")
.tasklet(new PniCctvAcceptTasklet(service))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_start_step")
protected Step start() {
return stepBuilderFactory.get("Job_Locking")
.tasklet(new BatchStartTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_end_step")
protected Step end() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchEndTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_fail_step")
protected Step fail() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchFailEndTasklet(lockService))
.build();
}
}

@ -0,0 +1,113 @@
package kr.xit.ens.support.batch.job;
import kr.xit.biz.pni.service.IPniCctvFileService;
import kr.xit.core.biz.batch.CustomRunIdIncrementer;
import kr.xit.core.biz.batch.listener.CustomJobListener;
import kr.xit.core.biz.batch.listener.CustomStepListener;
import kr.xit.core.biz.batch.listener.NoWorkFoundStepListener;
import kr.xit.core.biz.batch.service.IBatchCmmService;
import kr.xit.core.biz.batch.task.BatchEndTasklet;
import kr.xit.core.biz.batch.task.BatchFailEndTasklet;
import kr.xit.core.biz.batch.task.BatchStartTasklet;
import kr.xit.ens.support.batch.task.PniCctvFileTasklet;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* <pre>
* description : CCTV ()
*
* packageName : kr.xit.ens.support.batch.job
* fileName : PniCctvFileJobConfg
* author : limju
* date : 2023-07-10
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-10 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Configuration
public class PniCctvFileJobConfg {
private static final String JOB_NAME = "PniCctvFileJob";
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final IBatchCmmService lockService;
private final IPniCctvFileService service;
@Bean(name = JOB_NAME)
public Job pniCctvFileJob() {
return jobBuilderFactory.get(JOB_NAME)
.incrementer(new CustomRunIdIncrementer())
.listener(new CustomJobListener())
.listener(new CustomStepListener())
.listener(new NoWorkFoundStepListener())
// JOB 시작
// start() 결과가 FAILED 인경우 종료
.start(start())
.on("FAILED")
.end()
// start()의 결과로 부터 FAILED를 제외한 모든 경우 to() 실행
//.from(start())
.on("*")
.to(execution())
.on("FAILED")
.to(fail())
.from(execution())
// to() 실행 결과와 상관 없이 end() 실행
.on("*")
.to(end())
// JOB 종료
.end()
.build();
}
@Bean(name = JOB_NAME + "_step")
public Step execution() {
return stepBuilderFactory.get(JOB_NAME + "_step")
.tasklet(new PniCctvFileTasklet(service))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_start_step")
protected Step start() {
return stepBuilderFactory.get("Job_Locking")
.tasklet(new BatchStartTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_end_step")
protected Step end() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchEndTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_fail_step")
protected Step fail() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchFailEndTasklet(lockService))
.build();
}
}

@ -0,0 +1,113 @@
package kr.xit.ens.support.batch.job;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import kr.xit.biz.ens.service.ISendMessageLinkService;
import kr.xit.core.biz.batch.CustomRunIdIncrementer;
import kr.xit.core.biz.batch.listener.CustomJobListener;
import kr.xit.core.biz.batch.listener.CustomStepListener;
import kr.xit.core.biz.batch.service.IBatchCmmService;
import kr.xit.core.biz.batch.task.BatchEndTasklet;
import kr.xit.core.biz.batch.task.BatchFailEndTasklet;
import kr.xit.core.biz.batch.task.BatchStartTasklet;
import kr.xit.ens.support.batch.task.SndngAcceptTasklet;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : -
*
* packageName : kr.xit.ens.support.batch.job
* fileName : SndngAcceptJobConfig
* author : limju
* date : 2023-06-12
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-12 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Configuration
public class SndngAcceptJobConfig {
private static final String JOB_NAME = "SndngAcceptJob";
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final IBatchCmmService lockService;
private final ISendMessageLinkService linkService;
@Bean(name = JOB_NAME)
public Job sndngAcceptJob() {
return jobBuilderFactory.get(JOB_NAME)
.incrementer(new CustomRunIdIncrementer())
.listener(new CustomJobListener())
.listener(new CustomStepListener())
//.listener(new NoWorkFoundStepListener())
// JOB 시작
// start() 결과가 FAILED 인경우 종료
.start(start())
.on("FAILED")
.end()
// start()의 결과로 부터 FAILED를 제외한 모든 경우 to() 실행
//.from(start())
.on("*")
.to(execution())
.on("FAILED")
.to(fail())
.from(execution())
// to() 실행 결과와 상관 없이 end() 실행
.on("*")
.to(end())
// JOB 종료
.end()
.build();
}
@Bean(name = JOB_NAME + "_step")
public Step execution() {
return stepBuilderFactory.get(JOB_NAME + "_step")
.tasklet(new SndngAcceptTasklet(linkService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_start_step")
protected Step start() {
return stepBuilderFactory.get("Job_Locking")
.tasklet(new BatchStartTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_end_step")
protected Step end() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchEndTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_fail_step")
protected Step fail() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchFailEndTasklet(lockService))
.build();
}
}

@ -0,0 +1,114 @@
package kr.xit.ens.support.batch.job;
import kr.xit.biz.ens.service.ISendMessageLinkService;
import kr.xit.core.biz.batch.CustomRunIdIncrementer;
import kr.xit.core.biz.batch.listener.CustomJobListener;
import kr.xit.core.biz.batch.listener.CustomStepListener;
import kr.xit.core.biz.batch.listener.NoWorkFoundStepListener;
import kr.xit.core.biz.batch.service.IBatchCmmService;
import kr.xit.core.biz.batch.task.BatchEndTasklet;
import kr.xit.core.biz.batch.task.BatchFailEndTasklet;
import kr.xit.core.biz.batch.task.BatchStartTasklet;
import kr.xit.ens.support.batch.task.SndngCloseTasklet;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
/**
* <pre>
* description :
*
* packageName : kr.xit.ens.support.batch.job
* fileName : SndngCloseJobConfig
* author : seojh
* date : 2023-06-14
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-14 seojh
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Configuration
public class SndngCloseJobConfig {
private static final String JOB_NAME = "SndngCloseJob";
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final IBatchCmmService lockService;
private final ISendMessageLinkService linkService;
@Bean(name = JOB_NAME)
public Job sndngCloseJob() {
return jobBuilderFactory.get(JOB_NAME)
.incrementer(new CustomRunIdIncrementer())
.listener(new CustomJobListener())
.listener(new CustomStepListener())
.listener(new NoWorkFoundStepListener())
// JOB 시작
// start() 결과가 FAILED 인경우 종료
.start(start())
.on("FAILED")
.end()
// start()의 결과로 부터 FAILED를 제외한 모든 경우 to() 실행
//.from(start())
.on("*")
.to(execution())
.on("FAILED")
.to(fail())
.from(execution())
// to() 실행 결과와 상관 없이 end() 실행
.on("*")
.to(end())
// JOB 종료
.end()
.build();
}
@Bean(name = JOB_NAME + "_step")
public Step execution() {
return stepBuilderFactory.get(JOB_NAME + "_step")
.tasklet(new SndngCloseTasklet(linkService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_start_step")
protected Step start() {
return stepBuilderFactory.get("Job_Locking")
.tasklet(new BatchStartTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_end_step")
protected Step end() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchEndTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_fail_step")
protected Step fail() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchFailEndTasklet(lockService))
.build();
}
}

@ -0,0 +1,114 @@
package kr.xit.ens.support.batch.job;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import kr.xit.biz.ens.service.ISendMessageLinkService;
import kr.xit.core.biz.batch.CustomRunIdIncrementer;
import kr.xit.core.biz.batch.listener.CustomJobListener;
import kr.xit.core.biz.batch.listener.CustomStepListener;
import kr.xit.core.biz.batch.listener.NoWorkFoundStepListener;
import kr.xit.core.biz.batch.service.IBatchCmmService;
import kr.xit.core.biz.batch.task.BatchEndTasklet;
import kr.xit.core.biz.batch.task.BatchFailEndTasklet;
import kr.xit.core.biz.batch.task.BatchStartTasklet;
import kr.xit.ens.support.batch.task.SndngMakeTasklet;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : -
*
* packageName : kr.xit.ens.support.batch.job
* fileName : SndngMakeJobConfig
* author : limju
* date : 2023-06-12
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-12 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Configuration
public class SndngMakeJobConfig {
private static final String JOB_NAME = "SndngMakeJob";
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final IBatchCmmService lockService;
private final ISendMessageLinkService linkService;
@Bean(name = JOB_NAME)
public Job sndngMakeJob() {
return jobBuilderFactory.get(JOB_NAME)
.incrementer(new CustomRunIdIncrementer())
.listener(new CustomJobListener())
.listener(new CustomStepListener())
.listener(new NoWorkFoundStepListener())
// JOB 시작
// start() 결과가 FAILED 인경우 종료
.start(start())
.on("FAILED")
.end()
// start()의 결과로 부터 FAILED를 제외한 모든 경우 to() 실행
.from(start())
.on("*")
.to(execution())
.on("FAILED")
.to(fail())
.from(execution())
// to() 실행 결과와 상관 없이 end() 실행
.on("*")
.to(end())
// JOB 종료
.end()
.build();
}
@Bean(name = JOB_NAME + "_step")
public Step execution() {
return stepBuilderFactory.get(JOB_NAME + "_step")
.tasklet(new SndngMakeTasklet(linkService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_start_step")
protected Step start() {
return stepBuilderFactory.get("Job_Locking")
.tasklet(new BatchStartTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_end_step")
protected Step end() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchEndTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_fail_step")
protected Step fail() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchFailEndTasklet(lockService))
.build();
}
}

@ -0,0 +1,114 @@
package kr.xit.ens.support.batch.job;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import kr.xit.biz.ens.service.ISendMessageLinkService;
import kr.xit.core.biz.batch.CustomRunIdIncrementer;
import kr.xit.core.biz.batch.listener.CustomJobListener;
import kr.xit.core.biz.batch.listener.CustomStepListener;
import kr.xit.core.biz.batch.listener.NoWorkFoundStepListener;
import kr.xit.core.biz.batch.service.IBatchCmmService;
import kr.xit.core.biz.batch.task.BatchEndTasklet;
import kr.xit.core.biz.batch.task.BatchFailEndTasklet;
import kr.xit.core.biz.batch.task.BatchStartTasklet;
import kr.xit.ens.support.batch.task.SndngSendBulksTasklet;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : -
*
* packageName : kr.xit.ens.support.batch.job
* fileName : SndngSnedBulksJobConfig
* author : limju
* date : 2023-06-12
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-12 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Configuration
public class SndngSnedBulksJobConfig {
private static final String JOB_NAME = "SndngSendBulksJob";
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final IBatchCmmService lockService;
private final ISendMessageLinkService linkService;
@Bean(name = JOB_NAME)
public Job sndngSendBulksJob() {
return jobBuilderFactory.get(JOB_NAME)
.incrementer(new CustomRunIdIncrementer())
.listener(new CustomJobListener())
.listener(new CustomStepListener())
.listener(new NoWorkFoundStepListener())
// JOB 시작
// start() 결과가 FAILED 인경우 종료
.start(start())
.on("FAILED")
.end()
// start()의 결과로 부터 FAILED를 제외한 모든 경우 to() 실행
//.from(start())
.on("*")
.to(execution())
.on("FAILED")
.to(fail())
.from(execution())
// to() 실행 결과와 상관 없이 end() 실행
.on("*")
.to(end())
// JOB 종료
.end()
.build();
}
@Bean(name = JOB_NAME + "_step")
public Step execution() {
return stepBuilderFactory.get(JOB_NAME + "_step")
.tasklet(new SndngSendBulksTasklet(linkService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_start_step")
protected Step start() {
return stepBuilderFactory.get("Job_Locking")
.tasklet(new BatchStartTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_end_step")
protected Step end() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchEndTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_fail_step")
protected Step fail() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchFailEndTasklet(lockService))
.build();
}
}

@ -0,0 +1,114 @@
package kr.xit.ens.support.batch.job;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import kr.xit.biz.ens.service.ISendMessageLinkService;
import kr.xit.core.biz.batch.CustomRunIdIncrementer;
import kr.xit.core.biz.batch.listener.CustomJobListener;
import kr.xit.core.biz.batch.listener.CustomStepListener;
import kr.xit.core.biz.batch.listener.NoWorkFoundStepListener;
import kr.xit.core.biz.batch.service.IBatchCmmService;
import kr.xit.core.biz.batch.task.BatchEndTasklet;
import kr.xit.core.biz.batch.task.BatchFailEndTasklet;
import kr.xit.core.biz.batch.task.BatchStartTasklet;
import kr.xit.ens.support.batch.task.SndngStatusBulksTasklet;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : -
*
* packageName : kr.xit.ens.support.batch.job
* fileName : SndngStatusBulksJobConfig
* author : limju
* date : 2023-06-13
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-13 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Configuration
public class SndngStatusBulksJobConfig {
private static final String JOB_NAME = "SndngStatusBulksJob";
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final IBatchCmmService lockService;
private final ISendMessageLinkService linkService;
@Bean(name = JOB_NAME)
public Job sndngStatusBulksJob() {
return jobBuilderFactory.get(JOB_NAME)
.incrementer(new CustomRunIdIncrementer())
.listener(new CustomJobListener())
.listener(new CustomStepListener())
.listener(new NoWorkFoundStepListener())
// JOB 시작
// start() 결과가 FAILED 인경우 종료
.start(start())
.on("FAILED")
.end()
// start()의 결과로 부터 FAILED를 제외한 모든 경우 to() 실행
.from(start())
.on("*")
.to(execution())
.on("FAILED")
.to(fail())
.from(execution())
// to() 실행 결과와 상관 없이 end() 실행
.on("*")
.to(end())
// JOB 종료
.end()
.build();
}
@Bean(name = JOB_NAME + "_step")
public Step execution() {
return stepBuilderFactory.get(JOB_NAME + "_step")
.tasklet(new SndngStatusBulksTasklet(linkService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_start_step")
protected Step start() {
return stepBuilderFactory.get("Job_Locking")
.tasklet(new BatchStartTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_end_step")
protected Step end() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchEndTasklet(lockService))
.build();
}
@JobScope
@Bean(name = JOB_NAME + "_fail_step")
protected Step fail() {
return stepBuilderFactory.get("Lock_Release")
.tasklet(new BatchFailEndTasklet(lockService))
.build();
}
}

@ -0,0 +1,33 @@
package kr.xit.ens.support.batch.rowmapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
import kr.xit.core.biz.model.LoggingDTO;
/**
* <pre>
* description :
*
* packageName : kr.xit.ens.support.batch.rowmapper
* fileName : LoggingRowMapper
* author : limju
* date : 2023-05-16
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-16 limju
*
* </pre>
*/
public class LoggingRowMapper implements RowMapper<LoggingDTO> {
@Override
public LoggingDTO mapRow(ResultSet rs, int rowNum) throws SQLException {
LoggingDTO dto = new LoggingDTO();
dto.setRequestId(rs.getString("requestId"));
return dto;
}
}

@ -0,0 +1,67 @@
package kr.xit.ens.support.batch.scheduler;
import kr.xit.ens.support.batch.job.EnsCctvAcceptJobConfg;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* <pre>
* description : CCTV accept
*
* packageName : kr.xit.ens.support.batch.scheduler
* fileName : EnsCctvAcceptJobScheduler
* author : seojh
* date : 2023-07-17
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-17 seojh
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Component
public class EnsCctvAcceptJobScheduler {
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobLauncher jobLauncher;
private final EnsCctvAcceptJobConfg jobConfiguration;
@Value("${app.slack-webhook.enabled:false}")
private String isSlackEnabled;
//FIXME : 전자 고지 CCTV 파일 처리(서광) 실행 간격 정의
private final static int RUN_EXEC_MIS = 10000;
//@Scheduled(initialDelay = RUN_EXEC_MIS, fixedDelay = RUN_EXEC_MIS)
public void runJob() {
Map<String, JobParameter> confMap = new HashMap<>();
confMap.put("startDate", new JobParameter(new Date()));
confMap.put("isSlackEnabled", new JobParameter(isSlackEnabled));
JobParameters jobParameters = new JobParameters(confMap);
try {
jobLauncher.run(jobConfiguration.ensCctvAcceptJob(), jobParameters);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,67 @@
package kr.xit.ens.support.batch.scheduler;
import kr.xit.ens.support.batch.job.EnsCctvFileJobConfg;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* <pre>
* description : CCTV ()
*
* packageName : kr.xit.ens.support.batch.scheduler
* fileName : EnsCctvFileJobScheduler
* author : seojh
* date : 2023-07-17
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-17 seojh
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Component
public class EnsCctvFileJobScheduler {
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobLauncher jobLauncher;
private final EnsCctvFileJobConfg jobConfiguration;
@Value("${app.slack-webhook.enabled:false}")
private String isSlackEnabled;
//FIXME : 전자 고지 CCTV 파일 처리(서광) 실행 간격 정의
private final static int RUN_EXEC_MIS = 10000;
//@Scheduled(initialDelay = RUN_EXEC_MIS, fixedDelay = RUN_EXEC_MIS)
public void runJob() {
Map<String, JobParameter> confMap = new HashMap<>();
confMap.put("startDate", new JobParameter(new Date()));
confMap.put("isSlackEnabled", new JobParameter(isSlackEnabled));
JobParameters jobParameters = new JobParameters(confMap);
try {
jobLauncher.run(jobConfiguration.ensCctvFileJob(), jobParameters);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,68 @@
package kr.xit.ens.support.batch.scheduler;
import kr.xit.ens.support.batch.job.PniCctvAcceptJobConfg;
import kr.xit.ens.support.batch.job.PniCctvFileJobConfg;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* <pre>
* description : CCTV accept
*
* packageName : kr.xit.ens.support.batch.scheduler
* fileName : PniCctvAcceptJobScheduler
* author : limju
* date : 2023-07-11
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-11 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Component
public class PniCctvAcceptJobScheduler {
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobLauncher jobLauncher;
private final PniCctvAcceptJobConfg jobConfiguration;
@Value("${app.slack-webhook.enabled:false}")
private String isSlackEnabled;
//FIXME : 사전고지 CCTV 파일 처리(서광) 실행 간격 정의
private final static int RUN_EXEC_MIS = 10000;
//@Scheduled(initialDelay = RUN_EXEC_MIS, fixedDelay = RUN_EXEC_MIS)
public void runJob() {
Map<String, JobParameter> confMap = new HashMap<>();
confMap.put("startDate", new JobParameter(new Date()));
confMap.put("isSlackEnabled", new JobParameter(isSlackEnabled));
JobParameters jobParameters = new JobParameters(confMap);
try {
jobLauncher.run(jobConfiguration.pniCctvAcceptJob(), jobParameters);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,67 @@
package kr.xit.ens.support.batch.scheduler;
import kr.xit.ens.support.batch.job.PniCctvFileJobConfg;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* <pre>
* description : CCTV ()
*
* packageName : kr.xit.ens.support.batch.scheduler
* fileName : PniCctvFileJobScheduler
* author : limju
* date : 2023-07-10
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-10 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Component
public class PniCctvFileJobScheduler {
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobLauncher jobLauncher;
private final PniCctvFileJobConfg jobConfiguration;
@Value("${app.slack-webhook.enabled:false}")
private String isSlackEnabled;
//FIXME : 사전고지 CCTV 파일 처리(서광) 실행 간격 정의
private final static int RUN_EXEC_MIS = 10000;
//@Scheduled(initialDelay = RUN_EXEC_MIS, fixedDelay = RUN_EXEC_MIS)
public void runJob() {
Map<String, JobParameter> confMap = new HashMap<>();
confMap.put("startDate", new JobParameter(new Date()));
confMap.put("isSlackEnabled", new JobParameter(isSlackEnabled));
JobParameters jobParameters = new JobParameters(confMap);
try {
jobLauncher.run(jobConfiguration.pniCctvFileJob(), jobParameters);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,71 @@
package kr.xit.ens.support.batch.scheduler;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import kr.xit.ens.support.batch.job.SndngAcceptJobConfig;
import kr.xit.ens.support.common.ApiConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : -
* : (tb_cntc_sndng_mastr) : sndng_process_sttus = 'accept'
* (tb_cntc_sndng_detail)
* -> (tb_ens_unity_sndng_mastr)
* (tb_ens_unity_sndng_detail)
* : and tb_ens_unity_sndng_mastr - sndng_process_sttus = 'accept-ok'
* packageName : kr.xit.ens.support.batch.scheduler
* fileName : SndngAcceptJobScheduler
* author : limju
* date : 2023-06-12
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-12 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Component
public class SndngAcceptJobScheduler {
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobLauncher jobLauncher;
private final SndngAcceptJobConfig jobConfiguration;
@Value("${app.slack-webhook.enabled:false}")
private String isSlackEnabled;
//@Scheduled(cron = "${app.batch.cron.ens.accept}")
public void runJob() {
Map<String, JobParameter> confMap = new HashMap<>();
confMap.put("startDate", new JobParameter(new Date()));
confMap.put("isSlackEnabled", new JobParameter(isSlackEnabled));
confMap.put("sndngProcessSttus", new JobParameter(ApiConstants.SndngProcessStatus.ACCEPT.getCode()));
JobParameters jobParameters = new JobParameters(confMap);
try {
jobLauncher.run(jobConfiguration.sndngAcceptJob(), jobParameters);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,70 @@
package kr.xit.ens.support.batch.scheduler;
import kr.xit.ens.support.batch.job.SndngAcceptJobConfig;
import kr.xit.ens.support.batch.job.SndngCloseJobConfig;
import kr.xit.ens.support.common.ApiConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* <pre>
* description : close - send-ok
* : (tb_cntc_sndng_mastr)
* -> (tb_cntc_sndng_mastr)
* (tb_ens_unity_sndng_mastr)
* (tb_ens_sndng_mastr)
* packageName : kr.xit.ens.support.batch.scheduler
* fileName : SndngCloseJobScheduler
* author : seojh
* date : 2023-06-14
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-14 seojh
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Component
public class SndngCloseJobScheduler {
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobLauncher jobLauncher;
private final SndngCloseJobConfig jobConfiguration;
@Value("${app.slack-webhook.enabled:false}")
private String isSlackEnabled;
//@Scheduled(cron = "${app.batch.cron.ens.close}")
public void runJob() {
Map<String, JobParameter> confMap = new HashMap<>();
confMap.put("startDate", new JobParameter(new Date()));
confMap.put("isSlackEnabled", new JobParameter(isSlackEnabled));
confMap.put("sndngProcessSttus", new JobParameter(ApiConstants.SndngProcessStatus.SEND_OK.getCode()));
JobParameters jobParameters = new JobParameters(confMap);
try {
jobLauncher.run(jobConfiguration.sndngCloseJob(), jobParameters);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,70 @@
package kr.xit.ens.support.batch.scheduler;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import kr.xit.ens.support.batch.job.SndngMakeJobConfig;
import kr.xit.ens.support.common.ApiConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : -
* : (tb_ens_unity_sndng_mastr)
* * (tb_ens_unity_sndng_detail)
* -> (tb_ens_sndng_mastr)
* (tb_ens_kakao_my_doc)
* packageName : kr.xit.ens.support.batch.scheduler
* fileName : SndngMakeJobScheduler
* author : limju
* date : 2023-06-12
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-12 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Component
public class SndngMakeJobScheduler {
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobLauncher jobLauncher;
private final SndngMakeJobConfig jobConfiguration;
@Value("${app.slack-webhook.enabled:false}")
private String isSlackEnabled;
//@Scheduled(cron = "${app.batch.cron.ens.make}")
public void runJob() {
Map<String, JobParameter> confMap = new HashMap<>();
confMap.put("startDate", new JobParameter(new Date()));
confMap.put("isSlackEnabled", new JobParameter(isSlackEnabled));
confMap.put("sndngProcessSttus", new JobParameter(ApiConstants.SndngProcessStatus.ACCETP_OK.getCode()));
JobParameters jobParameters = new JobParameters(confMap);
try {
jobLauncher.run(jobConfiguration.sndngMakeJob(), jobParameters);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,69 @@
package kr.xit.ens.support.batch.scheduler;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import kr.xit.ens.support.batch.job.SndngSnedBulksJobConfig;
import kr.xit.ens.support.common.ApiConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : -
* : (tb_ens_sndng_mastr)
* (tb_ens_kakao_my_doc)
* (tb_cntc_sndng_result)
* packageName : kr.xit.ens.support.batch.scheduler
* fileName : SndngSendBulksJobScheduler
* author : limju
* date : 2023-06-12
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-12 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Component
public class SndngSendBulksJobScheduler {
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobLauncher jobLauncher;
private final SndngSnedBulksJobConfig jobConfiguration;
@Value("${app.slack-webhook.enabled:false}")
private String isSlackEnabled;
//@Scheduled(cron = "${app.batch.cron.ens.send}")
public void runJob() {
Map<String, JobParameter> confMap = new HashMap<>();
confMap.put("startDate", new JobParameter(new Date()));
confMap.put("isSlackEnabled", new JobParameter(isSlackEnabled));
confMap.put("sndngProcessSttus", new JobParameter(ApiConstants.SndngProcessStatus.MAKE_OK.getCode()));
JobParameters jobParameters = new JobParameters(confMap);
try {
jobLauncher.run(jobConfiguration.sndngSendBulksJob(), jobParameters);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,70 @@
package kr.xit.ens.support.batch.scheduler;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import kr.xit.ens.support.batch.job.SndngSnedBulksJobConfig;
import kr.xit.ens.support.batch.job.SndngStatusBulksJobConfig;
import kr.xit.ens.support.common.ApiConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description : -
* : (tb_cntc_sndng_result)
* (tb_ens_kakao_my_doc)
* packageName : kr.xit.ens.support.batch.scheduler
* fileName : SndngStatusBulksJobScheduler
* author : limju
* date : 2023-06-13
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-13 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
@Component
public class SndngStatusBulksJobScheduler {
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobLauncher jobLauncher;
private final SndngStatusBulksJobConfig jobConfiguration;
@Value("${app.slack-webhook.enabled:false}")
private String isSlackEnabled;
//@Scheduled(cron = "${app.batch.cron.ens.kko-status}")
public void runJob() {
Map<String, JobParameter> confMap = new HashMap<>();
confMap.put("startDate", new JobParameter(new Date()));
confMap.put("isSlackEnabled", new JobParameter(isSlackEnabled));
confMap.put("sndngProcessSttus", new JobParameter(ApiConstants.SndngProcessStatus.SEND_OK.getCode()));
JobParameters jobParameters = new JobParameters(confMap);
try {
jobLauncher.run(jobConfiguration.sndngStatusBulksJob(), jobParameters);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,61 @@
package kr.xit.ens.support.batch.task;
import kr.xit.biz.ens.service.IEnsCctvFileService;
import kr.xit.ens.support.batch.task.cmm.TaskCmmUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
/**
* <pre>
* description : CCTV accept
*
* packageName : kr.xit.ens.support.batch.task
* fileName : EnsCctvAcceptTasklet
* author : seojh
* date : 2023-07-17
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-17 seojh
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
public class EnsCctvAcceptTasklet implements Tasklet {
private final IEnsCctvFileService service;
@Override
@JobScope
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
final String isSlackEnabled = contribution.getStepExecution().getJobParameters().getString("isSlackEnabled");
try {
service.acceptEnsNtnccntcSndng();
}catch(Exception e){
log.error("EnsCctvAcceptTasklet error :: {}", e.getMessage());
TaskCmmUtils.taskBatchErrorLog(
contribution.getStepExecution().getJobExecution().getJobInstance().getJobName(),
e.getMessage(),
isSlackEnabled
);
TaskCmmUtils.taskEnsMessageLinkServiceUpdateErrorLog(
contribution.getStepExecution().getJobExecution().getJobInstance().getJobName(),
e.getMessage(),
isSlackEnabled
);
contribution.setExitStatus(ExitStatus.FAILED);
return RepeatStatus.FINISHED;
}
contribution.setExitStatus(ExitStatus.COMPLETED);
return RepeatStatus.FINISHED;
}
}

@ -0,0 +1,55 @@
package kr.xit.ens.support.batch.task;
import kr.xit.biz.ens.service.IEnsCctvFileService;
import kr.xit.ens.support.batch.task.cmm.TaskCmmUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
/**
* <pre>
* description : CCTV ()
*
* packageName : kr.xit.ens.support.batch.task
* fileName : EnsCctvFileTasklet
* author : seojh
* date : 2023-07-17
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-17 seojh
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
public class EnsCctvFileTasklet implements Tasklet {
private final IEnsCctvFileService service;
@Override
@JobScope
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
final String isSlackEnabled = contribution.getStepExecution().getJobParameters().getString("isSlackEnabled");
try {
service.createCctvFileOfSg();
}catch(Exception e){
log.error("EnsCctvFileTasklet error :: {}", e.getMessage());
TaskCmmUtils.taskBatchErrorLog(
contribution.getStepExecution().getJobExecution().getJobInstance().getJobName(),
e.getMessage(),
isSlackEnabled
);
contribution.setExitStatus(ExitStatus.FAILED);
return RepeatStatus.FINISHED;
}
contribution.setExitStatus(ExitStatus.COMPLETED);
return RepeatStatus.FINISHED;
}
}

@ -0,0 +1,61 @@
package kr.xit.ens.support.batch.task;
import kr.xit.biz.pni.service.IPniCctvFileService;
import kr.xit.ens.support.batch.task.cmm.TaskCmmUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
/**
* <pre>
* description : CCTV accept
*
* packageName : kr.xit.ens.support.batch.task
* fileName : PniCctvAcceptTasklet
* author : limju
* date : 2023-07-11
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-11 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
public class PniCctvAcceptTasklet implements Tasklet {
private final IPniCctvFileService service;
@Override
@JobScope
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
final String isSlackEnabled = contribution.getStepExecution().getJobParameters().getString("isSlackEnabled");
try {
service.acceptPniNtnccntcSndng();
}catch(Exception e){
log.error("PniCctvAcceptTasklet error :: {}", e.getMessage());
TaskCmmUtils.taskBatchErrorLog(
contribution.getStepExecution().getJobExecution().getJobInstance().getJobName(),
e.getMessage(),
isSlackEnabled
);
TaskCmmUtils.taskEnsMessageLinkServiceUpdateErrorLog(
contribution.getStepExecution().getJobExecution().getJobInstance().getJobName(),
e.getMessage(),
isSlackEnabled
);
contribution.setExitStatus(ExitStatus.FAILED);
return RepeatStatus.FINISHED;
}
contribution.setExitStatus(ExitStatus.COMPLETED);
return RepeatStatus.FINISHED;
}
}

@ -0,0 +1,55 @@
package kr.xit.ens.support.batch.task;
import kr.xit.biz.pni.service.IPniCctvFileService;
import kr.xit.ens.support.batch.task.cmm.TaskCmmUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
/**
* <pre>
* description : CCTV ()
*
* packageName : kr.xit.ens.support.batch.task
* fileName : PniCctvFileTasklet
* author : limju
* date : 2023-07-10
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-07-10 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
public class PniCctvFileTasklet implements Tasklet {
private final IPniCctvFileService service;
@Override
@JobScope
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
final String isSlackEnabled = contribution.getStepExecution().getJobParameters().getString("isSlackEnabled");
try {
service.createCctvFileOfSg();
}catch(Exception e){
log.error("PniCctvFileTasklet error :: {}", e.getMessage());
TaskCmmUtils.taskBatchErrorLog(
contribution.getStepExecution().getJobExecution().getJobInstance().getJobName(),
e.getMessage(),
isSlackEnabled
);
contribution.setExitStatus(ExitStatus.FAILED);
return RepeatStatus.FINISHED;
}
contribution.setExitStatus(ExitStatus.COMPLETED);
return RepeatStatus.FINISHED;
}
}

@ -0,0 +1,62 @@
package kr.xit.ens.support.batch.task;
import kr.xit.ens.support.batch.task.cmm.TaskCmmUtils;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import kr.xit.biz.ens.service.ISendMessageLinkService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description :
*
* packageName : kr.xit.ens.support.batch.task
* fileName : SndngAcceptTasklet
* author : limju
* date : 2023-06-12
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-12 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
public class SndngAcceptTasklet implements Tasklet {
private final ISendMessageLinkService service;
@Override
@JobScope
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
String sndngProcessSttus = contribution.getStepExecution().getJobParameters().getString("sndngProcessSttus");
final String isSlackEnabled = contribution.getStepExecution().getJobParameters().getString("isSlackEnabled");
try {
service.accept(sndngProcessSttus);
}catch(Exception e){
log.error("SndngAcceptTasklet error :: {}", e.getMessage());
TaskCmmUtils.taskBatchErrorLog(
contribution.getStepExecution().getJobExecution().getJobInstance().getJobName(),
e.getMessage(),
isSlackEnabled
);
TaskCmmUtils.taskEnsMessageLinkServiceUpdateErrorLog(
contribution.getStepExecution().getJobExecution().getJobInstance().getJobName(),
e.getMessage(),
isSlackEnabled
);
contribution.setExitStatus(ExitStatus.FAILED);
return RepeatStatus.FINISHED;
}
contribution.setExitStatus(ExitStatus.COMPLETED);
return RepeatStatus.FINISHED;
}
}

@ -0,0 +1,59 @@
package kr.xit.ens.support.batch.task;
import kr.xit.biz.ens.service.ISendMessageLinkService;
import kr.xit.ens.support.batch.task.cmm.TaskCmmUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
/**
* <pre>
* description :
*
* packageName : kr.xit.ens.support.batch.task
* fileName : SndngCloseTasklet
* author : seojh
* date : 2023-06-14
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-14 seojh
*
* </pre>
*/
@Slf4j
public class SndngCloseTasklet implements Tasklet {
private final ISendMessageLinkService service;
public SndngCloseTasklet(ISendMessageLinkService service) {
this.service = service;
}
@Override
@JobScope
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
String sndngProcessSttus = contribution.getStepExecution().getJobParameters().getString("sndngProcessSttus");
final String isSlackEnabled = contribution.getStepExecution().getJobParameters().getString("isSlackEnabled");
try {
service.close(sndngProcessSttus);
}catch(Exception e){
log.error("SndngCloseTasklet error :: {}", e.getMessage());
TaskCmmUtils.taskBatchErrorLog(
contribution.getStepExecution().getJobExecution().getJobInstance().getJobName(),
e.getMessage(),
isSlackEnabled
);
contribution.setExitStatus(ExitStatus.FAILED);
return RepeatStatus.FINISHED;
}
contribution.setExitStatus(ExitStatus.COMPLETED);
return RepeatStatus.FINISHED;
}
}

@ -0,0 +1,62 @@
package kr.xit.ens.support.batch.task;
import kr.xit.ens.support.batch.task.cmm.TaskCmmUtils;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import kr.xit.biz.ens.service.ISendMessageLinkService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description :
*
* packageName : kr.xit.ens.support.batch.task
* fileName : SndngMakeTasklet
* author : limju
* date : 2023-06-12
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-12 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
public class SndngMakeTasklet implements Tasklet {
private final ISendMessageLinkService service;
@Override
@JobScope
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
String sndngProcessSttus = contribution.getStepExecution().getJobParameters().getString("sndngProcessSttus");
final String isSlackEnabled = contribution.getStepExecution().getJobParameters().getString("isSlackEnabled");
try {
service.make(sndngProcessSttus);
}catch(Exception e){
log.error("SndngMakeTasklet error :: {}", e.getMessage());
TaskCmmUtils.taskBatchErrorLog(
contribution.getStepExecution().getJobExecution().getJobInstance().getJobName(),
e.getMessage(),
isSlackEnabled
);
TaskCmmUtils.taskEnsMessageLinkServiceUpdateErrorLog(
contribution.getStepExecution().getJobExecution().getJobInstance().getJobName(),
e.getMessage(),
isSlackEnabled
);
contribution.setExitStatus(ExitStatus.FAILED);
return RepeatStatus.FINISHED;
}
contribution.setExitStatus(ExitStatus.COMPLETED);
return RepeatStatus.FINISHED;
}
}

@ -0,0 +1,69 @@
package kr.xit.ens.support.batch.task;
import java.util.UUID;
import kr.xit.ens.support.batch.task.cmm.TaskCmmUtils;
import org.slf4j.MDC;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import kr.xit.biz.ens.service.ISendMessageLinkService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description :
*
* packageName : kr.xit.ens.support.batch.task
* fileName : SndngSendBulksTasklet
* author : limju
* date : 2023-06-12
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-12 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
public class SndngSendBulksTasklet implements Tasklet {
private final ISendMessageLinkService service;
@Override
@JobScope
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
String sndngProcessSttus = contribution.getStepExecution().getJobParameters().getString("sndngProcessSttus");
final String isSlackEnabled = contribution.getStepExecution().getJobParameters().getString("isSlackEnabled");
try {
MDC.put("request_trace_batch_id", UUID.randomUUID().toString().replace("-", ""));
MDC.put("uri", "/v1/documents/bulk");
MDC.put("method", "POST");
service.sendBulks(sndngProcessSttus);
}catch(Exception e){
log.error("SndngSendBulksTasklet error :: {}", e.getMessage());
TaskCmmUtils.taskBatchErrorLog(
contribution.getStepExecution().getJobExecution().getJobInstance().getJobName(),
e.getMessage(),
isSlackEnabled
);
TaskCmmUtils.taskEnsMessageLinkServiceUpdateErrorLog(
contribution.getStepExecution().getJobExecution().getJobInstance().getJobName(),
e.getMessage(),
isSlackEnabled
);
contribution.setExitStatus(ExitStatus.FAILED);
return RepeatStatus.FINISHED;
}
contribution.setExitStatus(ExitStatus.COMPLETED);
return RepeatStatus.FINISHED;
}
}

@ -0,0 +1,63 @@
package kr.xit.ens.support.batch.task;
import kr.xit.ens.support.batch.task.cmm.TaskCmmUtils;
import org.slf4j.MDC;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import kr.xit.biz.ens.service.ISendMessageLinkService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.UUID;
/**
* <pre>
* description :
*
* packageName : kr.xit.ens.support.batch.task
* fileName : SndngStatusBulksTasklet
* author : limju
* date : 2023-06-13
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-13 limju
*
* </pre>
*/
@Slf4j
@RequiredArgsConstructor
public class SndngStatusBulksTasklet implements Tasklet {
private final ISendMessageLinkService service;
@Override
@JobScope
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
final String sndngProcessSttus = contribution.getStepExecution().getJobParameters().getString("sndngProcessSttus");
final String isSlackEnabled = contribution.getStepExecution().getJobParameters().getString("isSlackEnabled");
try {
MDC.put("request_trace_batch_id", UUID.randomUUID().toString().replace("-", ""));
MDC.put("uri", "/v1/documents/bulk/status");
MDC.put("method", "POST");
service.findKkoMyDocStatusBulks(sndngProcessSttus);
}catch(Exception e){
log.error("SndngStatusBulksTasklet error :: {}", e.getMessage());
TaskCmmUtils.taskBatchErrorLog(
contribution.getStepExecution().getJobExecution().getJobInstance().getJobName(),
e.getMessage(),
isSlackEnabled
);
contribution.setExitStatus(ExitStatus.FAILED);
return RepeatStatus.FINISHED;
}
contribution.setExitStatus(ExitStatus.COMPLETED);
return RepeatStatus.FINISHED;
}
}

@ -0,0 +1,83 @@
package kr.xit.ens.support.batch.task.cmm;
import kr.xit.biz.ens.model.EnsDTO;
import kr.xit.core.biz.batch.model.BatchCmmDTO;
import kr.xit.core.spring.util.ApiSpringUtils;
import kr.xit.ens.support.common.ApiConstants;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.slf4j.MDC;
import static egovframework.com.cmm.util.EgovStringUtil.cutString;
/**
* <pre>
* description :
*
* packageName : kr.xit.ens.support.batch.task.cmm
* fileName : TaskCmmUtils
* author : limju
* date : 2023-06-19
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-06-19 limju
*
* </pre>
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TaskCmmUtils {
public static void taskBatchErrorLog(final String instanceId, final String errorMsg, final String isSlackEnabled){
ApiSpringUtils.getBatchCmmService().modifyBatchLog(
BatchCmmDTO
.builder()
.batchLogId(MDC.get("batch_log_id"))
.instanceId(instanceId)
.traceId(MDC.get("request_trace_id"))
.message(errorMsg)
.build()
);
if(Boolean.parseBoolean(isSlackEnabled)) {
ApiSpringUtils.getSlackWebhookPush().sendSlackAlertLog(
errorMsg,
MDC.get("uri"),
"batch call");
}
}
/**
*
* <p>
* @param instanceId String
* @param errorMsg String
* @param isSlackEnabled boolean push
*/
public static void taskEnsMessageLinkServiceUpdateErrorLog(final String instanceId, final String errorMsg, final String isSlackEnabled){
final String UNITY_SNDNG_MASTR_ID = "unitySndngMastrId";
String unitySndngMastrId = null;
String sndngProcessSttus = null;
String sndngMastrId = null;
if("SndngAcceptJob".equals(instanceId) || "PniCctvAcceptJob".equals(instanceId)){
unitySndngMastrId = MDC.get(UNITY_SNDNG_MASTR_ID);
sndngProcessSttus = ApiConstants.SndngProcessStatus.ACCETP_FAIL.getCode();
} else if ("SndngMakeJob".equals(instanceId)) {
unitySndngMastrId = MDC.get(UNITY_SNDNG_MASTR_ID);
sndngProcessSttus = MDC.get("sndngProcessSttus");
} else if ("SndngSendBulksJob".equals(instanceId)) {
unitySndngMastrId = MDC.get(UNITY_SNDNG_MASTR_ID);
sndngMastrId = MDC.get("sndngMastrId");
sndngProcessSttus = MDC.get("sndngProcessSttus");
}
ApiSpringUtils.getSendMessageLinkService().updateErrorLog(
EnsDTO.SndngMssageParam.builder()
.unitySndngMastrId(unitySndngMastrId)
.sndngMastrId(sndngMastrId)
.newSndngProcessSttus(sndngProcessSttus)
.errorCode(instanceId)
.errorMssage(cutString(errorMsg, 300))
.build()
);
}
}

@ -0,0 +1,224 @@
package kr.xit.ens.support.batch.web;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import kr.xit.core.support.utils.Checks;
import kr.xit.ens.support.batch.job.*;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import kr.xit.core.model.ApiResponseDTO;
import kr.xit.ens.support.batch.scheduler.SndngSendBulksJobScheduler;
import kr.xit.ens.support.common.ApiConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* <pre>
* description :
*
* packageName : kr.xit.ens.support.batch.web
* fileName : KkopayEltrcDocBatchController
* author : limju
* date : 2023-05-16
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-16 limju
*
* </pre>
*/
@Tag(name = "KkopayEltrcDocBatchController", description = "전자고지 통합발송 연계 배치 WEB")
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/api/batch/v1")
public class KkopayEltrcDocBatchController {
@Value("${app.slack-webhook.enabled:false}")
private String isSlackEnabled;
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
private final JobLauncher jobLauncher;
private final SndngAcceptJobConfig acceptJobConfig;
private final SndngMakeJobConfig makeJobConfig;
private final SndngSnedBulksJobConfig sendBulksJobConfig;
private final SndngStatusBulksJobConfig statusJobConfig;
private final SndngCloseJobConfig closeJobConfig;
private final PniCctvFileJobConfg pniCctvFileJobConfg;
private final PniCctvAcceptJobConfg pniCctvAcceptJobConfg;
@Operation(summary = "accept", description = "accept")
@GetMapping(value = "/accept", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> accept() {
try {
JobExecution jobExecution = jobLauncher.run(acceptJobConfig.sndngAcceptJob(), getJobParameters(ApiConstants.SndngProcessStatus.ACCEPT.getCode()));
while(jobExecution.isRunning()){
log.info("...");
}
printLog(jobExecution);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
}
return ApiResponseDTO.success(HttpStatus.OK);
}
@Operation(summary = "make", description = "make")
@GetMapping(value = "/make", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> make() {
try {
JobExecution jobExecution = jobLauncher.run(makeJobConfig.sndngMakeJob(), getJobParameters(ApiConstants.SndngProcessStatus.ACCETP_OK.getCode()));
while(jobExecution.isRunning()){
log.info("...");
}
printLog(jobExecution);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
}
return ApiResponseDTO.success(HttpStatus.OK);
}
@Operation(summary = "close", description = "close")
@GetMapping(value = "/close", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> close() {
try {
JobExecution jobExecution = jobLauncher.run(closeJobConfig.sndngCloseJob(), getJobParameters(ApiConstants.SndngProcessStatus.SEND_OK.getCode()));
while(jobExecution.isRunning()){
log.info("...");
}
printLog(jobExecution);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
}
return ApiResponseDTO.success(HttpStatus.OK);
}
@Operation(summary = "sendBulks", description = "sendBulks")
@GetMapping(value = "/sendBulks", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> sendBulks() {
try {
JobExecution jobExecution = jobLauncher.run(sendBulksJobConfig.sndngSendBulksJob(), getJobParameters(ApiConstants.SndngProcessStatus.MAKE_OK.getCode()));
while(jobExecution.isRunning()){
log.info("...");
}
printLog(jobExecution);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
}
return ApiResponseDTO.success(HttpStatus.OK);
}
@Operation(summary = "statusBulks", description = "statusBulks")
@GetMapping(value = "/statusBulks", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> statusBulks() {
try {
JobExecution jobExecution = jobLauncher.run(statusJobConfig.sndngStatusBulksJob(), getJobParameters(ApiConstants.SndngProcessStatus.SEND_OK.getCode()));
while(jobExecution.isRunning()){
log.info("...");
}
printLog(jobExecution);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
}
return ApiResponseDTO.success(HttpStatus.OK);
}
@Operation(summary = "pniCctvFileService", description = "CCTV 사전알림(서광)")
@GetMapping(value = "/pniCctvFileService", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> pniCctvFileService() {
try {
JobExecution jobExecution = jobLauncher.run(pniCctvFileJobConfg.pniCctvFileJob(), getJobParameters(null));
while(jobExecution.isRunning()){
log.info("...");
}
printLog(jobExecution);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
}
return ApiResponseDTO.success(HttpStatus.OK);
}
@Operation(summary = "pniCctvAccept", description = "CCTV 단속 사전알림 accept")
@GetMapping(value = "/pniCctvAccept", produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponseDTO<?> pniCctvAccept() {
try {
JobExecution jobExecution = jobLauncher.run(pniCctvAcceptJobConfg.pniCctvAcceptJob(), getJobParameters(null));
while(jobExecution.isRunning()){
log.info("...");
}
printLog(jobExecution);
} catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
log.error(e.getMessage());
}
return ApiResponseDTO.success(HttpStatus.OK);
}
private JobParameters getJobParameters(final String processStatus){
Map<String, JobParameter> confMap = new HashMap<>();
confMap.put("startDate", new JobParameter(new Date()));
confMap.put("isSlackEnabled", new JobParameter(isSlackEnabled));
if(Checks.isNotEmpty(processStatus)) confMap.put("sndngProcessSttus", new JobParameter(processStatus));
return new JobParameters(confMap);
}
private static void printLog(JobExecution jobExecution) {
log.info("Job Execution: " + jobExecution.getStatus());
log.info("Job getJobConfigurationName: " + jobExecution.getJobConfigurationName());
log.info("Job getJobId: " + jobExecution.getJobId());
log.info("Job getExitStatus: " + jobExecution.getExitStatus());
log.info("Job getJobInstance: " + jobExecution.getJobInstance());
log.info("Job getStepExecutions: " + jobExecution.getStepExecutions());
log.info("Job getLastUpdated: " + jobExecution.getLastUpdated());
log.info("Job getFailureExceptions: " + jobExecution.getFailureExceptions());
}
}

@ -0,0 +1,168 @@
package kr.xit.ens.support.common;
/**
* <pre>
* description :
*
* packageName : kr.xit.ens.support.common
* fileName : KakaoConstants
* author : xitdev
* date : 2023-05-04
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-04 xitdev
*
* </pre>
*/
public class ApiConstants {
/**
* <pre>
*
* code( ) : button_name( )- name( )
* Default : - Default
* BILL : -
* BILL_PAY : -
* NOTICE : -
* CONTRACT : -
* REPORT : -
* </pre>
*/
public enum Categories {
DEFAULT("Default")
, BILL("BILL")
, BILL_PAY("BILL_PAY")
, NOTICE("NOTICE")
, CONTRACT("CONTRACT")
, REPORT("REPORT")
;
private final String code;
Categories(String code) {
this.code = code;
}
public String getCode() {
return this.code;
}
}
/**
* <pre>
* INVALID_VALUE : http status code : 400
*
* {"error_code": "INVALID_VALUE", "error_message": "유효하지 않은 값입니다."
* UNIDENTIFIED_USER : http status code : 400
*
* {"error_code": "INVALID_VALUE", "error_message": "유효하지 않은 값입니다."
* UNAUTHORIZED : http status code : 401
* access token
* {"error_code": "UNAUTHORIZED","error_message": "접근 권한이 없습니다."
* FORBIDDEN : http status code : 403
*
* {"error_code": "FORBIDDEN","error_message": "허용되지 않는 요청입니다. 수신거부된 사용자 입니다."}
* NOT_FOUND : http status code : 404
* "Contract-Uuid" or "document_binder_uuid"
* {"error_code": "NOT_FOUND","error_message": "요청 정보를 찾을 수 없습니다."
* INTERNAL_ERROR : http status code : 500
*
* {"error_code": "INTERNAL_SERVER_ERROR","error_message": "서버 에러입니다. 다시 시도해 주세요."}
* </pre>
*
*/
public enum Error {
INVALID_VALUE("INVALID_VALUE")
, UNIDENTIFIED_USER("UNIDENTIFIED_USER")
, UNAUTHORIZED("UNAUTHORIZED")
, FORBIDDEN("FORBIDDEN")
, NOT_FOUND("NOT_FOUND")
, INTERNAL_ERROR("INTERNAL_ERROR")
;
private final String code;
Error(String code) {
this.code = code;
}
public String getCode() {
return this.code;
}
}
/**
*
* SENT() > RECEIVED() > READ()/EXPIRED( )
*/
public enum DocBoxStatus {
SENT("SENT")
, RECEIVED("RECEIVED")
, READ("READ")
, EXPIRED("EXPIRED")
;
private final String code;
DocBoxStatus(String code) {
this.code = code;
}
public String getCode() {
return this.code;
}
}
/**
* : ENS003
*/
public enum SndngProcessStatus {
ACCEPT("accept"),
ACCETP_OK("accept-ok"),
ACCETP_FAIL("accept-fail"),
MAKE_OK("make-ok"),
MAKE_FAIL1("make-fail1"),
MAKE_FAIL2("make-fail2"),
MAKE_FAIL3("make-fail3"),
SENDING1("sending1"),
SENDING2("sending2"),
SEND_OK("send-ok"),
SEND_FAIL1("send-fail1"),
SEND_FAIL2("send-fail2"),
SEND_FAIL3("send-fail3"),
CLOSE("close")
;
private final String code;
SndngProcessStatus(String code) {
this.code = code;
}
public String getCode() {
return this.code;
}
}
/**
*
*/
public enum SndngSeCode {
SMS("SMS"),
KAKAO_MY_DOC("KKO-MY-DOC"),
E_GREEN("E-GREEN")
;
private final String code;
SndngSeCode(String code) {
this.code = code;
}
public String getCode() {
return this.code;
}
}
}

@ -0,0 +1,56 @@
package kr.xit.ens.support.common.code;
import lombok.Getter;
import org.springframework.http.HttpStatus;
/**
* <pre>
* description :
* packageName : kr.xit.ens.support.common.code
* fileName : KkoReponseCode
* author : minuk
* date : 2023/05/07
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023/05/07 minuk
*
* </pre>
*/
//TODO:: KakaoConstants 로 통합
public class KkoReponseCode {
public enum ErrorCode {
// INVALID_VALUE("INVALID_VALUE", "INVALID_VALUE", "유효하지 않은 값입니다."),
// UNIDENTIFIED_USER("UNIDENTIFIED_USER", "INVALID_VALUE", "유효하지 않은 값입니다."),
// UNAUTHORIZED("UNAUTHORIZED", "UNAUTHORIZED", "접근 권한이 없습니다."),
// FORBIDDEN("FORBIDDEN", "FORBIDDEN", "허용되지 않는 요청입니다. 수신거부된 사용자 입니다."),
// NOT_FOUND("NOT_FOUND", "NOT_FOUND", "요청 정보를 찾을 수 없습니다."),
// INTERNAL_ERROR("INTERNAL_ERROR", "INTERNAL_SERVER_ERROR", "서버 에러입니다. 다시 시도해 주세요."),
// ;
INVALID_VALUE(400, "INVALID_VALUE", "유효하지 않은 값입니다."),
UNIDENTIFIED_USER(400, "INVALID_VALUE", "유효하지 않은 값입니다."),
UNAUTHORIZED(401, "UNAUTHORIZED", "접근 권한이 없습니다."),
FORBIDDEN(403, "FORBIDDEN", "허용되지 않는 요청입니다. 수신거부된 사용자 입니다."),
NOT_FOUND(404, "NOT_FOUND", "요청 정보를 찾을 수 없습니다."),
INTERNAL_ERROR(500, "INTERNAL_SERVER_ERROR", "서버 에러입니다. 다시 시도해 주세요."),
;
@Getter
private int errorCode;
@Getter
private String errorString;
@Getter
private String message;
ErrorCode(int errorCode, String errorString, String message){
this.errorCode = errorCode;
this.errorString = errorString;
this.message = message;
}
}
}

@ -0,0 +1,273 @@
package kr.xit.ens.support.kakao.model;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.Digits;
import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import kr.xit.core.model.IApiResponse;
import kr.xit.ens.support.common.ApiConstants;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* <pre>
* description : DTO
*
* packageName : kr.xit.ens.support.kakaopay.model
* fileName : KkopayDocDTO
* author : xitdev
* date : 2023-05-03
* ======================================================================
*
* ----------------------------------------------------------------------
* 2023-05-03 xitdev
*
* </pre>
*/
public class KkopayDocAttrDTO {
//------------------- requestSend ------------------------------------------------------------------------------------------------
@Schema(name = "Send", description = "RequestSend(문서발송 요청 파라메터)의 요청 파라메터 DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class Send implements IApiResponse {
/**
* :
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "발송할 문서의 제목", example = "문서 제목")
@Size(min = 1, max = 40, message = "문서제목은 필수입니다(max:40)")
private String title;
/**
* () -
* read_expired_sec
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "처리마감시간(절대시간)", example = "1617202800")
@Digits(integer = 10, fraction = 0, message = "처리마감시간(절대시간:max=10자리)")
private Long read_expired_at;
/**
* () - : 30 (2592000 sec)
* read_expired_at
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "처리마감시간(상대시간)", example = " ")
@Digits(integer = 8, fraction = 0, message = "처리마감시간(절대시간:max=8자리)")
private Integer read_expired_sec;
/**
* () hash -
*/
@Schema(title = "문서 원문(열람정보)에 대한 hash 값", example = "6EFE827AC88914DE471C621AE")
@Size(max = 99, message = "문서 원문(열람정보)에 대한 hash 값(max=99)")
private String hash;
/**
* - ( )
*/
@Schema(title = "문서의 메타정보(현재 미 제공)", example = "[\"NOTICE\"]")
private List<ApiConstants.Categories> common_categories;
/**
* -
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
@Valid
private Receiver receiver;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
@Valid
private Property property;
}
@Schema(name = "Receiver", description = "RequestSend(문서발송 요청 파라메터)의 receiver(받는이)에 대한 정보 DTO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class Receiver {
/**
* CI
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "받는이 CI", example = " ")
@Size(max = 88, message = "받는이 CI(max=88)")
private String ci;
/**
*
* ci
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "받는이 전화번호", example = "01012345678")
@Size(max = 11, message = "받는이 전화번호(max=11)")
private String phone_number;
/**
*
* ci
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "받는이 이름", example = "김페이")
@Size(max = 20, message = "받는이 이름(max=20)")
private String name;
/**
* (YYYYMMDD )
* ci
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "받는이 생년월일 (YYYYMMDD 형식)", example = "19801101")
//@Pattern(regexp = "^(19[0-9][0-9]|20\\d{2})(0[0-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$")
@Size(max = 8, message = "받는이 생년월일(YYYYMMDD:max=8)")
private String birthday;
/**
*
* CI
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "성명 검증 옵션", example = "false")
private Boolean is_required_verify_name;
}
@Schema(name = "Property", description = "RequestSend(문서발송 요청 파라메터)의 property(문서속성)에 대한 정보 DTO")
@Data
@SuperBuilder
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class Property {
/**
* :
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "본인인증 후 사용자에게 보여줄 웹페이지 주소", example = "http://ipAddress/api/kakaopay/v1/ott")
//@NotBlank
@Size(min = 10, max = 100, message = "본인인증후 사용자에게 보여줄 페이지 주소는 필수입니다(max=100)")
private String link;
/**
* :
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "고객센터 전화번호", example = "02-123-4567")
@Size(min = 10, max = 20, message = "고객센터 전화번호는 필수입니다(max=20)")
private String cs_number;
/**
* :
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "고객센터 명", example = "콜센터")
//@NotBlank
@Size(min = 1, max = 10, message = "고객센터명은 필수입니다(max=10)")
private String cs_name;
/**
*
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "이용기관에서 해당 값을 다시 받고자 할 내용의 값", example = "payload 파라미터 입니다.")
@Size(max = 200, message = "이용기관에서 해당 값을 다시 받고자 할 내용의 값(max=200)")
private String payload;
/**
* -
* : ()
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "사용자에게 전송하는 문서에 대한 설명", example = "해당 안내문은 다음과 같습니다.")
@Size(max = 500, message = "메세지(max=500)")
private String message;
}
//-------------------------------------------------------------------------------------------------------------------
@Schema(name = "DocumentBinderUuid DTO", description = "카카오페이 문서식별번호")
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class DocumentBinderUuid implements IApiResponse {
/**
* (max:40) -
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "카카오페이 문서식별번호(max:40)", example = "BIN-ff806328863311ebb61432ac599d6150")
@Size(min = 1, max = 40, message = "카카오페이 문서식별번호는 필수입니다(max:40)")
private String document_binder_uuid;
}
@Schema(name = "DocStatus DTO", description = "카카오페이 전자문서 상태 DTO")
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class DocStatus implements IApiResponse {
/**
* <pre>
* (max:20) :
* |||
* SENT|RECEIVED|READ|EXPIRED
* SENT - ( , )
* RECEIVED -
* READ - OTT API
* EXPIRED -
* </pre>
* @see ApiConstants.DocBoxStatus
*/
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, maxLength = 20, title = "진행상태(max:20)", example = " ")
@Size(min = 1, max = 20, message = "진행상태는 필수입니다(max:20)")
private ApiConstants.DocBoxStatus doc_box_status;
/**
* (long max:10)
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "송신 시간(max:10)", example = " ")
@Digits(integer = 10, fraction = 0, message = "송신시간(max:10)")
private Long doc_box_sent_at;
/**
* (long max:10)
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "수신 시간(max:10)", example = " ")
@Digits(integer = 10, fraction = 0, message = "수신시간(max:10)")
private Long doc_box_received_at;
/**
* (long max:10)
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "열람인증성공 최초 시간(max:10)", example = " ")
@Digits(integer = 10, fraction = 0, message = "열람인증성공 최초시간(max:10)")
private Long authenticated_at;
/**
* OTT (long max:10)
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "OTT 검증 성공 최초 시간(max:10)", example = " ")
@Digits(integer = 10, fraction = 0, message = "OTT 검증 성공 최초 시간(max:10)")
private Long token_used_at;
/**
* (long max:10)
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "최초 열람 시간(max:10)", example = " ")
@Digits(integer = 10, fraction = 0, message = "최초 열람 시간(max:10)")
private Long doc_box_read_at;
/**
* (long max:10)
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "알림톡 수신 시간(max:10)", example = " ")
@Digits(integer = 10, fraction = 0, message = "알림톡 수신 시간(max:10)")
private Long user_notified_at;
/**
* payload(max:200)
*/
@Schema(requiredMode = Schema.RequiredMode.AUTO, title = "이용기관 생성 payload(max:200)", example = " ")
@Size(max = 200, message = "이용기관 생성 payload(max:200)")
private String payload;
}
}

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

Loading…
Cancel
Save