최초 커밋
commit
e0aabf80fd
@ -0,0 +1,158 @@
|
||||
<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>
|
||||
|
||||
<groupId>cokr.xit.interfaces</groupId>
|
||||
<artifactId>xit-lntris</artifactId>
|
||||
<version>23.04.01-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>xit-lntris</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>mvn2s</id>
|
||||
<url>https://repo1.maven.org/maven2/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>egovframe</id>
|
||||
<url>https://maven.egovframe.go.kr/maven/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>maven-public</id>
|
||||
<url>https://nas.xit.co.kr:8888/repository/maven-public/</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cokr.xit.interfaces</groupId>
|
||||
<artifactId>xit-filejob</artifactId>
|
||||
<version>23.04.01-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
<version>1.7.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-javadoc</artifactId>
|
||||
<version>1.7.0</version>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-security</artifactId>
|
||||
<version>1.7.0</version>
|
||||
</dependency>
|
||||
-->
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
<directory>${basedir}/target</directory>
|
||||
<finalName>${artifactId}-${version}</finalName>
|
||||
|
||||
<resources>
|
||||
<resource><directory>${basedir}/src/main/resources</directory></resource>
|
||||
</resources>
|
||||
<testResources>
|
||||
<testResource><directory>${basedir}/src/test/resources</directory></testResource>
|
||||
<testResource><directory>${basedir}/src/main/resources</directory></testResource>
|
||||
</testResources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*.class</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- EMMA -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
<reportFormat>xml</reportFormat>
|
||||
<excludes>
|
||||
<exclude>**/Abstract*.java</exclude>
|
||||
<exclude>**/*Suite.java</exclude>
|
||||
</excludes>
|
||||
<includes>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>emma-maven-plugin</artifactId>
|
||||
<inherited>true</inherited>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Javadoc -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<!-- Nexus deploy -->
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>maven-snapshot</id>
|
||||
<url>https://nas.xit.co.kr:8888/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
|
||||
<repository>
|
||||
<id>maven-release</id>
|
||||
<url>https://nas.xit.co.kr:8888/repository/maven-releases/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
<!-- Nexus deploy -->
|
||||
|
||||
</project>
|
@ -0,0 +1,160 @@
|
||||
package cokr.xit.interfaces.lntris;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import cokr.xit.foundation.Assert;
|
||||
|
||||
public class DataFileSupport<T extends InterfaceInfo<?, ?>> {
|
||||
private static final String FIELD = "||";
|
||||
private static final String CR = "^||";
|
||||
|
||||
private Charset charset;
|
||||
private Supplier<T> supplier;
|
||||
private ArrayList<T> messages;
|
||||
private DecimalFormat seqFormat = new DecimalFormat("000");
|
||||
|
||||
public DataFileSupport<T> setCharset(Charset charset) {
|
||||
this.charset = charset;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DataFileSupport<T> setSupplier(Supplier<T> supplier) {
|
||||
this.supplier = supplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<T> getMessages() {
|
||||
return Assert.ifEmpty(messages, Collections::emptyList);
|
||||
}
|
||||
|
||||
public T getMessage() {
|
||||
List<T> msgs = getMessages();
|
||||
return !msgs.isEmpty() ? msgs.get(0) : null;
|
||||
}
|
||||
|
||||
public DataFileSupport<T> read(Path path) {
|
||||
InterfaceHeader header = new InterfaceHeader();
|
||||
messages = new ArrayList<>();
|
||||
try {
|
||||
Files.lines(path, charset).forEach(line -> {
|
||||
String[] fields = line
|
||||
.replace("\uFEFF", "") // UTF-8(BOM) -> UTF-8
|
||||
.replace(CR, "")
|
||||
.split("\\|\\|", -1);
|
||||
|
||||
if (header.empty())
|
||||
read(header, fields);
|
||||
else {
|
||||
T info = read(fields);
|
||||
info.setHeader(header);
|
||||
messages.add(info);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
} catch (Exception e) {
|
||||
throw Assert.runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void read(InterfaceHeader header, String[] fields) {
|
||||
header.setIfId(supplier.get().interfaceID());
|
||||
|
||||
List<Consumer<String>> setters = header.setters();
|
||||
if (setters.size() > fields.length)
|
||||
throw new RuntimeException("header setters.size() > fields.length");
|
||||
|
||||
for (int i = 0; i < setters.size(); ++i) {
|
||||
Consumer<String> setter = setters.get(i);
|
||||
String field = fields[i];
|
||||
if (field != null)
|
||||
field = field.trim();
|
||||
setter.accept(field);
|
||||
}
|
||||
}
|
||||
|
||||
private T read(String[] fields) {
|
||||
T t = supplier.get();
|
||||
List<Consumer<String>> setters = t.setters();
|
||||
if (setters.size() != fields.length)
|
||||
throw new RuntimeException("info setters.size() > fields.length");
|
||||
|
||||
for (int i = 0; i < setters.size(); ++i) {
|
||||
Consumer<String> setter = setters.get(i);
|
||||
String field = fields[i];
|
||||
if (field != null)
|
||||
field = field.trim();
|
||||
setter.accept(field);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
public List<Path> write(String dir, List<T> intfList) {
|
||||
if (intfList == null || intfList.isEmpty())
|
||||
return Collections.emptyList();
|
||||
|
||||
ArrayList<Path> paths = new ArrayList<>();
|
||||
Map<String, List<T>> bySource = intfList.stream().collect(
|
||||
Collectors.groupingBy(info -> info.getSourceMessage().getHeader().getSource())
|
||||
);
|
||||
|
||||
File d = new File(dir);
|
||||
if (!d.exists())
|
||||
d.mkdirs();
|
||||
|
||||
for (List<T> intfs: bySource.values()) {
|
||||
InterfaceInfo<?, ?> info = intfs.get(0);
|
||||
InterfaceHeader header = info.getSourceMessage().getHeader().setIfFormat("D");
|
||||
String filename = String.format(
|
||||
"%s_%s_%s@%s", //인터페이스 아이디(30)_파일생성일시(14)_순번(3)@수신 자치단체 코드(7)수신 시스템 코드(3)
|
||||
header.getIfId(), header.getIfDate(), seqFormat.format(paths.size() + 1), header.getSource() //header.getTarget()
|
||||
);
|
||||
Path path = Paths.get(dir, filename);
|
||||
|
||||
|
||||
try (FileWriter writer = new FileWriter(path.toFile());) {
|
||||
write(writer, header);
|
||||
for (T t: intfs) {
|
||||
write(writer, t);
|
||||
}
|
||||
writer.flush();
|
||||
paths.add(path);
|
||||
} catch (Exception e) {
|
||||
throw Assert.runtimeException(e);
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
private void write(FileWriter writer, InterfaceHeader header) throws Exception {
|
||||
String line = List.of(
|
||||
header.getIfDate(),
|
||||
header.getIfMsgKey(),
|
||||
header.getIfId(),
|
||||
header.getSource(),
|
||||
header.getTarget(),
|
||||
header.getIfType(),
|
||||
header.getIfFormat()
|
||||
).stream().collect(Collectors.joining(FIELD));
|
||||
writer.append(line + CR + "\n");
|
||||
}
|
||||
|
||||
private void write(FileWriter writer, T t) throws Exception {
|
||||
String line = t.getters().stream()
|
||||
.map(getter -> Assert.ifEmpty(getter.get(), ""))
|
||||
.collect(Collectors.joining(FIELD));
|
||||
writer.append(line + CR + "\n");
|
||||
}
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
package cokr.xit.interfaces.lntris;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import cokr.xit.foundation.AbstractComponent;
|
||||
import cokr.xit.foundation.data.JSON;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**차세대 세외수입 연계 설정을 로드하고 제공한다.
|
||||
* 설정은 클래스패스 상의 intf-conf/lntris.conf 파일에 지정하며
|
||||
* 지정하는 내용은 다음과 같다.
|
||||
* <pre><code> {
|
||||
* "local": [
|
||||
* {"organization": "지역 자치단체 코드(7자리)", "systems": ["지역 시스템 코드(3자리)"}, ...],
|
||||
* ...
|
||||
* ],
|
||||
* "remote": {
|
||||
* "organization": "지방세외수입 기관 코드(7자리)",
|
||||
* "systems": ["지방세외수입 시스템 코드(3자리)"],
|
||||
* "url": "지방세외수입 시스템 url"
|
||||
* }
|
||||
* }</code></pre>
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class InterfaceConfig extends AbstractComponent {
|
||||
private static final InterfaceConfig conf;
|
||||
|
||||
static {
|
||||
try {
|
||||
conf = new JSON().parse(new ClassPathResource("intf-conf/lntris.conf").getInputStream(), InterfaceConfig.class);
|
||||
} catch (Exception e) {
|
||||
throw runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean useDatabase = true;
|
||||
|
||||
/**데이터베이스 사용여부를 설정한다.
|
||||
* @param useDatabase 데이터베이스 사용여부
|
||||
*/
|
||||
public void setUseDatabase(String useDatabase) {
|
||||
this.useDatabase = !"false".equals(useDatabase);
|
||||
}
|
||||
|
||||
/** 지자체 시스템 설정 */
|
||||
private List<EndPoint> locals;
|
||||
/** 지방세외수입 시스템 설정 */
|
||||
private EndPoint remote;
|
||||
|
||||
/**지자체 설정을 반환한다.
|
||||
* @return 지자체 설정
|
||||
*/
|
||||
public List<EndPoint> getLocals(String... orgs) {
|
||||
return locals;
|
||||
}
|
||||
|
||||
/**지자체 설정을 설정한다.
|
||||
* @param locals 지자체 설정
|
||||
*/
|
||||
public void setLocals(List<EndPoint> locals) {
|
||||
this.locals = locals;
|
||||
}
|
||||
|
||||
/**지방세외수입 시스템 설정을 반환한다.
|
||||
* @return 지방세외수입 시스템 설정
|
||||
*/
|
||||
public EndPoint getRemote() {
|
||||
return remote;
|
||||
}
|
||||
|
||||
/**지방세외수입 시스템 설정을 설정한다.
|
||||
* @param remote 지방세외수입 시스템 설정
|
||||
*/
|
||||
public void setRemote(EndPoint remote) {
|
||||
this.remote = remote;
|
||||
}
|
||||
|
||||
public static boolean useDatabase() {
|
||||
return conf.useDatabase;
|
||||
}
|
||||
|
||||
public static void databaseActive(Runnable task) {
|
||||
if (task == null) return;
|
||||
if (conf.useDatabase)
|
||||
task.run();
|
||||
}
|
||||
|
||||
public static <T> T databaseActive(Supplier<T> on, Supplier<T> off) {
|
||||
if (conf.useDatabase) {
|
||||
return on != null ? on.get() : null;
|
||||
} else {
|
||||
return off != null ? off.get() : null;
|
||||
}
|
||||
}
|
||||
|
||||
/**지정하는 지자체 설정 목록을 반환한다.
|
||||
* @param orgs 단체/기관 코드
|
||||
* @return 지자체 설정 목록
|
||||
*/
|
||||
public static List<EndPoint> locals(String... orgs) {
|
||||
if (isEmpty(orgs))
|
||||
return conf.locals;
|
||||
|
||||
List<String> keys = List.of(orgs);
|
||||
return conf.locals.stream()
|
||||
.filter(local -> keys.contains(local.organization))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**지정하는 지자체 설정을 반환한다.
|
||||
* @param org 단체/기관 코드
|
||||
* @return 지자체 설정
|
||||
*/
|
||||
public static EndPoint local(String org) {
|
||||
List<EndPoint> locals = locals(org);
|
||||
if (locals.isEmpty())
|
||||
throw new IllegalArgumentException("local not found for " + org);
|
||||
return locals.get(0);
|
||||
}
|
||||
|
||||
public static EndPoint remote() {
|
||||
return conf.remote;
|
||||
}
|
||||
|
||||
public static String organizationSystem(String org, String sys) {
|
||||
return org + sys;
|
||||
}
|
||||
|
||||
/**단체/기관의 접속정보
|
||||
* <ul><li>organization - 단체/기관 코드(7자리)</li>
|
||||
* <li>systems - 시스템 코드(3자리) 목록</li>
|
||||
* <li>url - 시스템 url(선택)</li>
|
||||
* </ul>
|
||||
* @author mjkhan
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public static class EndPoint {
|
||||
private String organization;
|
||||
private List<String> systems;
|
||||
private String url;
|
||||
|
||||
/**단체/기관의 첫번째 시스템 코드를 반환한다.
|
||||
* @return 단체/기관의 첫번째 시스템 코드
|
||||
*/
|
||||
public String getSystem() {
|
||||
return !isEmpty(systems) ? systems.get(0) : "";
|
||||
}
|
||||
|
||||
/**단체/기관코드 + 시스템 코드 목록을 반환한다.
|
||||
* 시스템 코드를 지정하지 않으면 설정된 모든 시스템에 대해 목록을 반환한다.
|
||||
* @param sysCodes 시스템 코드
|
||||
* @return 단체/기관코드 + 시스템 코드 목록
|
||||
*/
|
||||
public List<String> getOrganizationSystems(String... sysCodes) {
|
||||
List<String> keys = isEmpty(sysCodes) ? systems : List.of(sysCodes);
|
||||
return keys.stream().map(key -> organizationSystem(organization, key)).toList();
|
||||
}
|
||||
|
||||
public String getOrganizationSystem() {
|
||||
List<String> orgSys = getOrganizationSystems();
|
||||
return !orgSys.isEmpty() ? orgSys.get(0) : "";
|
||||
}
|
||||
|
||||
/**단체/기관의 시스템 url이 로컬 시스템에 대한 것인지 반환한다.
|
||||
* @return 단체/기관의 시스템 url의 로컬 시스템 여부
|
||||
* <ul><li>단체/기관의 시스템 url이 로컬 시스템에 대한 것이면 true</li>
|
||||
* <li>그렇지 않으면 false</li>
|
||||
* </ul>
|
||||
*/
|
||||
public boolean isLocal() {
|
||||
return ifEmpty(url, "").contains("localhost");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,259 @@
|
||||
package cokr.xit.interfaces.lntris;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import cokr.xit.foundation.Assert;
|
||||
|
||||
/**연계 표준 헤더
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class InterfaceHeader {
|
||||
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
|
||||
|
||||
/** 전송일자 */
|
||||
private String ifDate;
|
||||
/** 연계 메시지키 */
|
||||
private String ifMsgKey;
|
||||
/** 인터페이스 ID */
|
||||
private String ifId;
|
||||
/** 출발지 시스템 코드 */
|
||||
private String source;
|
||||
/** 도착지 시스템 코드 */
|
||||
private String target;
|
||||
/** 데이터 구분 */
|
||||
private String ifType;
|
||||
/** 데이터 형식 */
|
||||
private String ifFormat;
|
||||
/** 연계처리 결과명 */
|
||||
private String retName;
|
||||
/** 연계처리 결과코드 */
|
||||
private String retCode;
|
||||
|
||||
/**헤더가 비어있는지 반환한다.
|
||||
* @return
|
||||
* <ul><li>헤더가 비어있으면 true</li>
|
||||
* <li>그렇지 않으면 false</li>
|
||||
* </ul>
|
||||
*/
|
||||
public boolean empty() {
|
||||
return Assert.isEmpty(ifDate) && Assert.isEmpty(ifMsgKey) && Assert.isEmpty(ifId);
|
||||
}
|
||||
|
||||
/**전송일자(yyyyMMddHHmmss)를 반환한다.
|
||||
* @return 전송일자(yyyyMMddHHmmss)
|
||||
*/
|
||||
public String getIfDate() {
|
||||
return Assert.ifEmpty(ifDate, () -> ifDate = dateFormat.format(new Date()).substring(0, 14));
|
||||
}
|
||||
/**전송일자(yyyyMMddHHmmss)를 설정한다.
|
||||
* @param ifDate 전송일자(yyyyMMddHHmmss)
|
||||
*/
|
||||
public void setIfDate(String ifDate) {
|
||||
this.ifDate = ifDate;
|
||||
}
|
||||
|
||||
/**연계 메시지키를 반환한다.
|
||||
* @return 연계 메시지키
|
||||
*/
|
||||
public String getIfMsgKey() {
|
||||
if (Assert.isEmpty(ifMsgKey))
|
||||
setMsgKey("Z");
|
||||
return ifMsgKey;
|
||||
}
|
||||
/**연계 메시지키를 설정한다.
|
||||
* @param ifMsgKey 연계 메시지키
|
||||
* @return 현재 InterfaceHeader
|
||||
*/
|
||||
public InterfaceHeader setIfMsgKey(String ifMsgKey) {
|
||||
this.ifMsgKey = ifMsgKey;
|
||||
return this;
|
||||
}
|
||||
/**지정한 시스템 코드로 연계 메시지키를 생성한다.
|
||||
* @param systemCode 시스템 코드
|
||||
* <ul><li>T - 세무행정</li>
|
||||
* <li>W - 대국민</li>
|
||||
* <li>N - 세외수입</li>
|
||||
* <li>A - 행정지원</li>
|
||||
* <li>L - 연계</li>
|
||||
* <li>E - 유관기관</li>
|
||||
* <li>Z - 개별시스템</li>
|
||||
* </ul>
|
||||
* @return 현재 InterfaceHeader
|
||||
*/
|
||||
public InterfaceHeader setMsgKey(String systemCode) {
|
||||
String now = dateFormat.format(new Date()).substring(2);
|
||||
String guid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
|
||||
String msgKey = String.format("%s%s-%s", systemCode, now, guid);
|
||||
return setIfMsgKey(msgKey);
|
||||
}
|
||||
|
||||
/**인터페이스 ID를 반환한다.
|
||||
* @return 인터페이스 ID
|
||||
*/
|
||||
public String getIfId() {
|
||||
return ifId;
|
||||
}
|
||||
/**인터페이스 ID를 설정한다.
|
||||
* @param ifId 인터페이스 ID
|
||||
* @return 현재 InterfaceHeader
|
||||
*/
|
||||
public InterfaceHeader setIfId(String ifId) {
|
||||
this.ifId = ifId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**출발지 시스템 코드를 반환한다.
|
||||
* 디폴트는 lntris.conf의 local 설정
|
||||
* @return 출발지 시스템 코드
|
||||
*/
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**출발지 시스템 코드를 설정한다.
|
||||
* @param source 출발지 시스템 코드
|
||||
* @return 현재 InterfaceHeader
|
||||
*/
|
||||
public InterfaceHeader setSource(String source) {
|
||||
this.source = source;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**도착지 시스템 코드를 반환한다.
|
||||
* 디폴트는 lntris.conf의 lntris 설정
|
||||
* @return 도착지 시스템 코드
|
||||
*/
|
||||
public String getTarget() {
|
||||
return Assert.ifEmpty(target, () -> target = InterfaceConfig.remote().getOrganizationSystem());
|
||||
}
|
||||
/**도착지 시스템 코드를 설정한다.
|
||||
* @param target 도착지 시스템 코드
|
||||
* @return 현재 InterfaceHeader
|
||||
*/
|
||||
public InterfaceHeader setTarget(String target) {
|
||||
this.target = target;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**데이터 구분을 반환한다.
|
||||
* @return 데이터 구분
|
||||
* <ul><li>S - 송신(디폴트)</li>
|
||||
* <li>R - 수신</li>
|
||||
* </ul>
|
||||
*/
|
||||
public String getIfType() {
|
||||
return Assert.ifEmpty(ifType, () -> ifType = "S");
|
||||
}
|
||||
/**데이터 구분을 설정한다.
|
||||
* @param ifType 데이터 구분
|
||||
* <ul><li>S - 요청, 송신(디폴트)</li>
|
||||
* <li>R - 응답, 수신</li>
|
||||
* </ul>
|
||||
* @return 현재 InterfaceHeader
|
||||
*/
|
||||
public InterfaceHeader setIfType(String ifType) {
|
||||
this.ifType = ifType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**데이터 형식을 반환한다.
|
||||
* @return 데이터 형식
|
||||
* <ul><li>D - 구분자</li>
|
||||
* <li>F - 고정길이</li>
|
||||
* <li>X - XML</li>
|
||||
* <li>J - JSON(디폴트)</li>
|
||||
* </ul>
|
||||
*/
|
||||
public String getIfFormat() {
|
||||
return Assert.ifEmpty(ifFormat, () -> ifFormat = "J");
|
||||
}
|
||||
/**데이터 형식을 설정한다.
|
||||
* @param ifFormat 데이터 형식
|
||||
* <ul><li>D - 구분자</li>
|
||||
* <li>F - 고정길이</li>
|
||||
* <li>X - XML</li>
|
||||
* <li>J - JSON(디폴트)</li>
|
||||
* </ul>
|
||||
* @return 현재 InterfaceHeader
|
||||
*/
|
||||
public InterfaceHeader setIfFormat(String ifFormat) {
|
||||
this.ifFormat = ifFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**연계처리 결과명을 반환한다.
|
||||
* @return 연계처리 결과명
|
||||
*/
|
||||
public String getRetName() {
|
||||
return retName;
|
||||
}
|
||||
/**연계처리 결과명을 설정한다.
|
||||
* @param retName 연계처리 결과명
|
||||
* @return 현재 InterfaceHeader
|
||||
*/
|
||||
public InterfaceHeader setRetName(String retName) {
|
||||
this.retName = retName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**연계처리 결과코드를 반환한다.
|
||||
* @return 연계처리 결과코드
|
||||
* <ul><li>200 - 성공(디폴트)</li>
|
||||
* <li>500 - 실패</li>
|
||||
* </ul>
|
||||
*/
|
||||
public String getRetCode() {
|
||||
if (Assert.isEmpty(retCode)) {
|
||||
if ("R".equals(getIfType()))
|
||||
retCode = "200";
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
/**연계처리 결과코드를 설정한다.
|
||||
* @param retCode 연계처리 결과코드
|
||||
* <ul><li>200 - 성공(디폴트)</li>
|
||||
* <li>500 - 실패</li>
|
||||
* </ul>
|
||||
* @return 현재 InterfaceHeader
|
||||
*/
|
||||
public InterfaceHeader setRetCode(String retCode) {
|
||||
this.retCode = retCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**헤더 필드값의 setter 메소드 목록을 반환한다.
|
||||
* @return 헤더 필드값의 setter 메소드 목록
|
||||
*/
|
||||
public List<Consumer<String>> setters() {
|
||||
return List.of(
|
||||
this::setIfDate,
|
||||
this::setIfMsgKey,
|
||||
this::setIfId,
|
||||
this::setSource,
|
||||
this::setTarget,
|
||||
this::setIfType,
|
||||
this::setIfFormat
|
||||
);
|
||||
}
|
||||
|
||||
/**현재 InterfaceHeader를 복사하여 반환한다.
|
||||
* @return 현재 InterfaceHeader의 복사본
|
||||
*/
|
||||
public InterfaceHeader copy() {
|
||||
InterfaceHeader copy = new InterfaceHeader();
|
||||
copy.ifDate = ifDate;
|
||||
copy.ifMsgKey = ifMsgKey;
|
||||
copy.ifId = ifId;
|
||||
copy.source = source;
|
||||
copy.target = target;
|
||||
copy.ifType = ifType;
|
||||
copy.ifFormat = ifFormat;
|
||||
copy.retName = retName;
|
||||
copy.retCode = retCode;
|
||||
return copy;
|
||||
}
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
package cokr.xit.interfaces.lntris;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
import cokr.xit.foundation.AbstractEntity;
|
||||
import cokr.xit.foundation.Assert;
|
||||
import cokr.xit.foundation.data.JSON;
|
||||
|
||||
/**세외수입 연계정보.<br />
|
||||
* 연계 표준헤더, 출발지 메시지, 도착지 메시지로 구성되며 데이터베이스의 해당 연계 테이블에 저장된다.
|
||||
* @param <Q> 출발지 메시지의 요청정보 유형
|
||||
* @param <R> 도착지 메시지의 응답정보 유형
|
||||
* @author mjkhan
|
||||
*/
|
||||
public abstract class InterfaceInfo<Q extends SourceMessage.Request, R extends TargetMessage.Response> extends AbstractEntity {
|
||||
/** 업무(시스템) 코드 */
|
||||
private String taskSeCd;
|
||||
/** 연계 표준 헤더 */
|
||||
private InterfaceHeader header;
|
||||
/** 출발지가 보낸 메시지 */
|
||||
protected SourceMessage<Q> sourceMessage;
|
||||
/** 도착지가 보낸 메시지 */
|
||||
protected TargetMessage<R> targetMessage;
|
||||
private JSON json;
|
||||
|
||||
/**업무(시스템) 코드를 반환한다.
|
||||
* @return 업무(시스템) 코드
|
||||
*/
|
||||
public String getTaskSeCd() {
|
||||
return taskSeCd;
|
||||
}
|
||||
/**업무(시스템) 코드를 설정한다.
|
||||
* @param taskSeCd 업무(시스템) 코드
|
||||
*/
|
||||
public void setTaskSeCd(String taskSeCd) {
|
||||
this.taskSeCd = taskSeCd;
|
||||
}
|
||||
|
||||
/**인터페이스 아이디를 반환한다.
|
||||
* @return 인터페이스 아이디
|
||||
*/
|
||||
public abstract String interfaceID();
|
||||
|
||||
/**연계 표준 헤더를 반환한다.
|
||||
* @return 연계 표준 헤더
|
||||
*/
|
||||
public InterfaceHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
/**연계 표준 헤더를 설정한다.
|
||||
* @param header 연계 표준 헤더
|
||||
*/
|
||||
public InterfaceInfo<Q, R> setHeader(InterfaceHeader header) {
|
||||
this.header = header;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**출발지가 보낸 메시지를 반환한다.
|
||||
* @return 출발지가 보낸 메시지
|
||||
*/
|
||||
public SourceMessage<Q> getSourceMessage() {
|
||||
if (sourceMessage == null) {
|
||||
sourceMessage = new SourceMessage<Q>();
|
||||
sourceMessage.setHeader(
|
||||
new InterfaceHeader().setIfId(interfaceID())
|
||||
);
|
||||
sourceMessage.setBody(newRequest());
|
||||
}
|
||||
return sourceMessage;
|
||||
}
|
||||
|
||||
protected Q newRequest() {
|
||||
throw new UnsupportedOperationException(String.format("Implement the %s.newRequest() method", getClass().getName()));
|
||||
}
|
||||
|
||||
/**출발지가 보낸 메시지를 설정한다.
|
||||
* @param sourceMessage 출발지가 보낸 메시지메시지
|
||||
* @return 현재 InterfaceInfo
|
||||
*/
|
||||
public InterfaceInfo<Q, R> setSourceMessage(SourceMessage<Q> request) {
|
||||
this.sourceMessage = request;
|
||||
return this;
|
||||
}
|
||||
/**출발지가 보낸 요청정보를 반환한다.
|
||||
* @return 요청정보
|
||||
*/
|
||||
public <T> T getRequest() {
|
||||
return (T)getSourceMessage().getBody().getReqVo();
|
||||
}
|
||||
|
||||
/**출발지 메시지 헤더의 source를 설정한다.
|
||||
* @return 현재 InterfaceInfo
|
||||
*/
|
||||
public InterfaceInfo<Q, R> setSourceHeaderCodes() {
|
||||
throw new UnsupportedOperationException(String.format("Implement the %s.setSourceHeaderCodes() method", getClass().getName()));
|
||||
}
|
||||
|
||||
/**도착지가 보낸 메시지를 반환한다.
|
||||
* @return 도착지가 보낸 메시지
|
||||
*/
|
||||
public TargetMessage<R> getTargetMessage() {
|
||||
if (targetMessage == null) {
|
||||
targetMessage = new TargetMessage<>();
|
||||
targetMessage.setHeader(
|
||||
getSourceMessage()
|
||||
.getHeader().copy()
|
||||
.setIfType("R")
|
||||
);
|
||||
targetMessage.setBody(newResponse());
|
||||
}
|
||||
return targetMessage;
|
||||
}
|
||||
|
||||
protected R newResponse() {
|
||||
throw new UnsupportedOperationException(String.format("Implement the %s.newResponse() method", getClass().getName()));
|
||||
}
|
||||
|
||||
/**도착지가 보낸 메시지를 설정한다.
|
||||
* @param targetMessage 도착지가 보낸 메시지
|
||||
* @return 현재 InterfaceInfo
|
||||
*/
|
||||
public InterfaceInfo<Q, R> setTargetMessage(TargetMessage<R> response) {
|
||||
this.targetMessage = response;
|
||||
return this;
|
||||
}
|
||||
/**도착지가 보낸 응답정보를 반환한다.
|
||||
* @return 요청정보
|
||||
*/
|
||||
public R getResponse() {
|
||||
return getTargetMessage().getBody();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public List<Supplier<String>> getters() {
|
||||
throw new UnsupportedOperationException(String.format("Implement the %s.getters() method", getClass().getName()));
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public List<Consumer<String>> setters() {
|
||||
throw new UnsupportedOperationException(String.format("Implement the %s.setters() method", getClass().getName()));
|
||||
}
|
||||
|
||||
/**json 문자열을 파싱하여 도착지 메시지를 설정한다.
|
||||
* @param json json 문자열
|
||||
*/
|
||||
public void parseTargetMessage(String json) {
|
||||
setTargetMessage(json().parse(json, targetMessageType()));
|
||||
}
|
||||
|
||||
/**도착지 메시지 타입을 반환한다.
|
||||
* @return 도착지 메시지 타입
|
||||
*/
|
||||
protected abstract TypeReference<TargetMessage<R>> targetMessageType();
|
||||
|
||||
protected TypeReference<TargetMessage<TargetMessage.Response>> defaultTargetMessageType() {
|
||||
return new TypeReference<TargetMessage<TargetMessage.Response>>() {};
|
||||
}
|
||||
|
||||
/**데이터 파싱에 사용할 JSON을 반환한다.
|
||||
* @return 데이터 파싱에 사용할 JSON
|
||||
*/
|
||||
protected JSON json() {
|
||||
return Assert.ifEmpty(json, () -> json = new JSON());
|
||||
}
|
||||
|
||||
/**데이터 파싱에 사용할 JSON을 설정한다.
|
||||
* @param json 데이터 파싱에 사용할 JSON
|
||||
* @return 현재 InterfaceInfo
|
||||
*/
|
||||
public InterfaceInfo<Q, R> json(JSON json) {
|
||||
this.json = json;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static class Detail implements Assert.Support {}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package cokr.xit.interfaces.lntris;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import cokr.xit.interfaces.filejob.service.bean.FileJobBean;
|
||||
|
||||
public abstract class InterfaceInfoReader<T extends InterfaceInfo<?, ?>> extends FileJobBean {
|
||||
protected abstract Supplier<T> interfaceInfoSupplier();
|
||||
protected abstract Consumer<T> insertInterfaceInfo();
|
||||
|
||||
/**수신 연계정보 파일을 읽어들여 저장한다.
|
||||
* 시스템 코드를 지정하지 않으면 해당 단체의 모든 시스템의 수신전문을 처리한다.
|
||||
* @param orgs 자치단체 코드 목록
|
||||
* @param sysCodes 시스템 코드 목록
|
||||
*/
|
||||
public List<T> read(String[] orgs, String... sysCodes) {
|
||||
String interfaceID = interfaceInfoSupplier().get().interfaceID();
|
||||
List<String> tails = InterfaceConfig.locals(orgs).stream()
|
||||
.flatMap(org -> org.getOrganizationSystems(sysCodes).stream())
|
||||
.map(orgSys -> "@" + orgSys)
|
||||
.toList();
|
||||
|
||||
List<Path> paths = getReceivedFilePaths(path -> {
|
||||
String str = path.toString();
|
||||
|
||||
for (String tail: tails)
|
||||
if (str.contains(interfaceID) && str.contains(tail))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
});
|
||||
if (paths.isEmpty()) return Collections.emptyList();
|
||||
|
||||
List<FileStatus> fileStatus = processReceived(paths);
|
||||
|
||||
Map<Boolean, List<FileStatus>> successFail = fileStatus.stream() //처리 결과를 성공 / 실패로 분류
|
||||
.collect(Collectors.groupingBy(file -> file.isSuccess()));
|
||||
|
||||
List<FileStatus> success = successFail.get(true);
|
||||
move(FileStatus.getPaths(success), successDir()); // 성공 디렉토리로 이동
|
||||
move(FileStatus.getPaths(successFail.get(false)), failDir()); // 실패 디렉토리로 이동
|
||||
|
||||
return isEmpty(success) ?
|
||||
Collections.emptyList() :
|
||||
success.stream().flatMap(status -> ((List<T>)status.get("messages")).stream()).toList();
|
||||
}
|
||||
|
||||
protected List<FileStatus> processReceived(List<Path> paths) {
|
||||
DataFileSupport<T> reader = new DataFileSupport<T>()
|
||||
.setSupplier(interfaceInfoSupplier())
|
||||
.setCharset(Charset.forName("UTF-8"));
|
||||
return isEmpty(paths) ?
|
||||
Collections.emptyList() :
|
||||
paths.stream()
|
||||
.map(path -> {
|
||||
FileStatus status = new FileStatus().setPath(path);
|
||||
try {
|
||||
reader
|
||||
.read(path)
|
||||
.getMessages()
|
||||
.forEach(info -> {
|
||||
insertInterfaceInfo().accept(info);
|
||||
List<T> msgs = (List<T>)status.get("messages");
|
||||
if (msgs == null)
|
||||
status.set("messages", msgs = new ArrayList<>());
|
||||
msgs.add(info);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
status.setCause(e);
|
||||
}
|
||||
return status;
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package cokr.xit.interfaces.lntris;
|
||||
|
||||
public class JsonParamSupport<T> {
|
||||
private String taskSeCd;
|
||||
|
||||
/**업무(시스템) 코드를 반환한다.
|
||||
* @return 업무(시스템) 코드
|
||||
*/
|
||||
public String getTaskSeCd() {
|
||||
return taskSeCd;
|
||||
}
|
||||
|
||||
/**업무(시스템) 코드를 설정한다.
|
||||
* @param taskSeCd 업무(시스템) 코드
|
||||
*/
|
||||
public void setTaskSeCd(String taskSeCd) {
|
||||
this.taskSeCd = taskSeCd;
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
package cokr.xit.interfaces.lntris;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
|
||||
import cokr.xit.foundation.Assert;
|
||||
import cokr.xit.foundation.Log;
|
||||
import cokr.xit.foundation.data.JSON;
|
||||
import cokr.xit.foundation.web.WebClient;
|
||||
|
||||
/**연계 메시지를 출발지에서 도착지로 전송하고, 응답을 수신한다.
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class Requestor {
|
||||
public static final String NAME = "requestor";
|
||||
private static final SSLContext sslContext;
|
||||
static {
|
||||
try {
|
||||
TrustManager[] trustAll = {
|
||||
new X509ExtendedTrustManager() {
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[0];
|
||||
}
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] arg0, String arg1, SSLEngine arg2) throws CertificateException {}
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] arg0, String arg1, Socket arg2) throws CertificateException {}
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] arg0, String arg1, SSLEngine arg2) throws CertificateException {}
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] arg0, String arg1, Socket arg2) throws CertificateException {}
|
||||
}
|
||||
};
|
||||
sslContext = SSLContext.getInstance("SSL");
|
||||
sslContext.init(null, trustAll, new SecureRandom());
|
||||
} catch (Exception e) {
|
||||
throw Assert.runtimeException(e);
|
||||
}
|
||||
}
|
||||
private JSON json = new JSON();
|
||||
private WebClient webClient;
|
||||
|
||||
/**세외수입 연계정보의 출발지 메시지를 전송하고 도착지 메시지와 응답을 설정한다.
|
||||
* @param intfInfo 세외수입 연계정보
|
||||
*/
|
||||
public void request(InterfaceInfo<?, ?> intfInfo) {
|
||||
try {
|
||||
init();
|
||||
|
||||
String src = intfInfo.getSourceMessage().getHeader().getSource();
|
||||
HttpResponse<String> hresp = webClient.post(req -> req
|
||||
.uri(InterfaceConfig.remote().getUrl())
|
||||
|
||||
.json(json)
|
||||
.header("Accept-Charset", "UTF-8")
|
||||
|
||||
.header("User-Agent", "Mozila/5.0")
|
||||
.header("Accept-Language", "en-US,en;q=0.5")
|
||||
|
||||
.header("IF_ID", intfInfo.interfaceID())
|
||||
.header("SRC_ORG_CD", src.substring(0, 7))
|
||||
.header("SRC_SYS_CD", src.substring(8))
|
||||
|
||||
.bodyData(intfInfo.getSourceMessage())
|
||||
);
|
||||
|
||||
if (WebClient.Request.SUCCESS != hresp.statusCode())
|
||||
throw new RuntimeException(intfInfo.interfaceID() + ": " + hresp.statusCode());
|
||||
|
||||
Log.get(Requestor.class).debug("targetMessage:\n{}", hresp.body());
|
||||
|
||||
intfInfo
|
||||
.json(json)
|
||||
.parseTargetMessage(hresp.body());
|
||||
} catch (Exception e) {
|
||||
throw Assert.runtimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void init() throws Exception {
|
||||
if (webClient != null) return;
|
||||
|
||||
webClient = new WebClient()
|
||||
.timeout(3)
|
||||
.sslContext(sslContext);
|
||||
|
||||
json.getObjectMapper().setSerializationInclusion(Include.NON_NULL);
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package cokr.xit.interfaces.lntris;
|
||||
|
||||
/**출발지가 보내는 메시지
|
||||
* @param <Q> 요청정보 유형
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class SourceMessage<Q extends SourceMessage.Request> {
|
||||
private InterfaceHeader header;
|
||||
private Q body;
|
||||
|
||||
/**연계 표준헤더를 반환한다.
|
||||
* @return 연계 표준헤더
|
||||
*/
|
||||
public InterfaceHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
/**연계 표준헤더를 설정한다.
|
||||
* @param header 연계 표준헤더
|
||||
* @return 현재 SourceMessage
|
||||
*/
|
||||
public SourceMessage<Q> setHeader(InterfaceHeader header) {
|
||||
this.header = header;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**메시지의 본문을 반환한다.
|
||||
* @return 메시지 본문
|
||||
*/
|
||||
public Q getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
/**메시지의 본문을 설정한다.
|
||||
* @param body 메시지 본문
|
||||
* @return 현재 SourceMessage
|
||||
*/
|
||||
public SourceMessage<Q> setBody(Q body) {
|
||||
this.body = body;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**요청정보
|
||||
* @author mjkhan
|
||||
*/
|
||||
public static abstract class Request {
|
||||
/**reqVo을(를) 반환한다.
|
||||
* @return reqVo
|
||||
*/
|
||||
public abstract Object getReqVo();
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package cokr.xit.interfaces.lntris;
|
||||
|
||||
/**도착지가 보낸 메시지
|
||||
* @param <Q> 요청정보 유형
|
||||
* @author mjkhan
|
||||
*/
|
||||
public class TargetMessage<R extends TargetMessage.Response> {
|
||||
private InterfaceHeader header;
|
||||
private R body;
|
||||
|
||||
/**연계 표준헤더를 반환한다.
|
||||
* @return 연계 표준헤더
|
||||
*/
|
||||
public InterfaceHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
/**연계 표준헤더를 설정한다.
|
||||
* @param header 연계 표준헤더
|
||||
*/
|
||||
public TargetMessage<R> setHeader(InterfaceHeader header) {
|
||||
this.header = header;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**본문을 반환한다.
|
||||
* @return 본문
|
||||
*/
|
||||
public R getBody() {
|
||||
return body;
|
||||
}
|
||||
/**본문을 설정한다.
|
||||
* @param body 본문
|
||||
*/
|
||||
public TargetMessage<R> setBody(R body) {
|
||||
this.body = body;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**도착지의 처리가 성공했는지 반환한다.
|
||||
* @return 도착지 처리의 성공여부
|
||||
* <ul><li>도착지의 처리가 성공했으면 true</li>
|
||||
* <li>그렇지 않으면 false</li>
|
||||
* </ul>
|
||||
*/
|
||||
public boolean success() {
|
||||
return body != null && body.success();
|
||||
}
|
||||
|
||||
/**응답정보(도착지 메시지의 본문)
|
||||
* @author mjkhan
|
||||
*/
|
||||
public static class Response {
|
||||
/** 연계결과 코드 */
|
||||
private String linkRstCd;
|
||||
/** 연계결과 메시지 */
|
||||
private String linkRstMsg;
|
||||
|
||||
/**연계결과 코드를 반환한다.
|
||||
* @return 연계결과 코드
|
||||
*/
|
||||
public String getLinkRstCd() {
|
||||
return linkRstCd;
|
||||
}
|
||||
/**연계결과 코드를 설정한다.
|
||||
* @param linkRstCd 연계결과 코드
|
||||
*/
|
||||
public void setLinkRstCd(String linkRstCd) {
|
||||
this.linkRstCd = linkRstCd;
|
||||
}
|
||||
|
||||
/**연계결과 메시지를 반환한다.
|
||||
* @return 연계결과 메시지
|
||||
*/
|
||||
public String getLinkRstMsg() {
|
||||
return linkRstMsg;
|
||||
}
|
||||
/**연계결과 메시지를 설정한다.
|
||||
* @param linkRstMsg 연계결과 메시지
|
||||
*/
|
||||
public void setLinkRstMsg(String linkRstMsg) {
|
||||
this.linkRstMsg = linkRstMsg;
|
||||
}
|
||||
|
||||
/**도착지의 처리가 성공했는지 반환한다.
|
||||
* @return 도착지 처리의 성공여부
|
||||
* <ul><li>도착지의 처리가 성공했으면 true</li>
|
||||
* <li>그렇지 않으면 false</li>
|
||||
* </ul>
|
||||
*/
|
||||
public boolean success() {
|
||||
return "000".equals(getLinkRstCd());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
{
|
||||
"locals": [
|
||||
{"organization": "4060000",/* 지역 자치단체 코드(7자리) */
|
||||
/* 지역 시스템 코드(3자리) */
|
||||
"systems": ["DPV"]
|
||||
}
|
||||
],
|
||||
|
||||
"remote": {
|
||||
"organization": "1741000", /* 지방세외수입 기관 코드(7자리) */
|
||||
"systems": ["NIS"], /* 지방세외수입 시스템 코드(3자리) */
|
||||
/*"url": "https://10.60.75.57:22411/mediate/ltis" /* 연계 운영 url */
|
||||
"url": "https://10.60.75.138:22411/mediate/ltis" /* 연계 검증 url */
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package cokr.xit.interfaces.lntris;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import cokr.xit.foundation.test.TestSupport;
|
||||
|
||||
public class InterfaceConfigTest extends TestSupport {
|
||||
@Test
|
||||
void config() {
|
||||
System.out.println("useDatabase: " + InterfaceConfig.useDatabase());
|
||||
InterfaceConfig.EndPoint remote = InterfaceConfig.remote();
|
||||
System.out.println("========== remote ==========");
|
||||
System.out.println("organization: " + remote.getOrganization());
|
||||
System.out.println("systems: " + remote.getSystems().stream().collect(Collectors.joining(", ")));
|
||||
System.out.println("organizationSystem: " + remote.getOrganizationSystem());
|
||||
System.out.println("url: " + remote.getUrl());
|
||||
|
||||
System.out.println("========== locals ==========");
|
||||
InterfaceConfig.locals().forEach(local -> {
|
||||
System.out.println("organization: " + local.getOrganization());
|
||||
System.out.println("systems: " + local.getSystems().stream().collect(Collectors.joining(", ")));
|
||||
System.out.println("organizationSystems: " + local.getOrganizationSystems().stream().collect(Collectors.joining(", ")));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void header() {
|
||||
InterfaceHeader header = new InterfaceHeader();
|
||||
System.out.println("ifDate: " + header.getIfDate());
|
||||
System.out.println("ifMsgKey: " + header.getIfMsgKey());
|
||||
System.out.println("ifId: " + header.getIfId());
|
||||
System.out.println("source: " + header.getSource());
|
||||
System.out.println("target: " + header.getTarget());
|
||||
System.out.println("ifType: " + header.getIfType());
|
||||
System.out.println("ifFormat: " + header.getIfFormat());
|
||||
System.out.println("retName: " + header.getRetName());
|
||||
System.out.println("retCode: " + header.getRetCode());
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
|
||||
|
||||
log4jdbc.dump.sql.maxlinelength=0
|
||||
log4jdbc.drivers=org.mariadb.jdbc.Driver
|
@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- 60초마다 설정 파일의 변경을 확인 하여 변경시 갱신 -->
|
||||
<configuration scan="true" scanPeriod="60 seconds">
|
||||
<property name="applicationName" value="lntris"/>
|
||||
<property name="LOG_PATH" value="logs"/>
|
||||
<property name="LOG_FILE_NAME" value="${applicationName}"/>
|
||||
<property name="ERR_LOG_FILE_NAME" value="${LOG_FILE_NAME}-error"/>
|
||||
<property name="LOG_PATTERN" value="%d{HH:mm:ss.SSS} %-5level [%logger{0}:%line] - %msg%n"/>
|
||||
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_PATH}/${LOG_FILE_NAME}.log</file>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- .gz,.zip 등을 넣으면 자동 일자별 로그파일 압축 -->
|
||||
<fileNamePattern>${LOG_PATH}/${LOG_FILE_NAME}.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
|
||||
<maxHistory>30</maxHistory><!-- 로그파일 보관주기(일)-->
|
||||
<!--<MinIndex>1</MinIndex>
|
||||
<MaxIndex>10</MaxIndex>-->
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
|
||||
<appender name="Error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>error</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
|
||||
<file>${LOG_PATH}/${ERR_LOG_FILE_NAME}.log</file>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- .gz,.zip 등을 넣으면 자동 일자별 로그파일 압축 -->
|
||||
<fileNamePattern>${LOG_PATH}/${ERR_LOG_FILE_NAME}.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<!-- 파일당 최고 용량 kb, mb, gb -->
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<!-- 일자별 로그파일 최대 보관주기(~일), 해당 설정일 이상된 파일은 자동으로 제거-->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="FILE"/>
|
||||
<appender-ref ref="Error"/>
|
||||
</root>
|
||||
|
||||
<logger name="jdbc" level="OFF" additivity="false"/>
|
||||
<logger name="jdbc.sqltiming" level="OFF" additivity="false"/>
|
||||
<logger name="jdbc.resultsettable" level="OFF" additivity="false"/>
|
||||
<logger name="jdbc.sqlonly" level="OFF"/>
|
||||
<logger name="jdbc.resultset" level="OFF" additivity="false"/>
|
||||
<logger name="jdbc.connection" level="OFF" additivity="false"/>
|
||||
<logger name="jdbc.audit" level="OFF" additivity="false"/>
|
||||
<logger name="org.apache" level="OFF" additivity="false"/>
|
||||
<logger name="com.zaxxer" level="OFF" additivity="false"/>
|
||||
|
||||
<logger name="org.apache.commons" level="OFF" additivity="false"/>
|
||||
<logger name="org.mariadb" level="OFF" additivity="false"/>
|
||||
<logger name="jdbc.connection" level="OFF" additivity="false"/>
|
||||
<logger name="jdbc.sqlonly" level="OFF" additivity="false"/>
|
||||
<logger name="jdbc.resultset" level="OFF" additivity="false"/>
|
||||
<logger name="jdbc.resultsettable" level="OFF" additivity="false"/>
|
||||
<!--
|
||||
<logger name="jdbc" level="OFF" additivity="false"/>
|
||||
<logger name="jdbc.sqltiming" level="DEBUG" />
|
||||
<logger name="jdbc.audit" level="OFF" additivity="false"/>
|
||||
-->
|
||||
|
||||
<!-- 특정패키지 로깅레벨 설정 -->
|
||||
<logger name="cokr.xit" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="FILE"/>
|
||||
<appender-ref ref="Error"/>
|
||||
</logger>
|
||||
|
||||
</configuration>
|
@ -0,0 +1,2 @@
|
||||
# see https://projectlombok.org/features/constructor lombok.copyableAnnotations
|
||||
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier
|
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
|
||||
|
||||
<context:component-scan base-package="cokr.xit">
|
||||
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
|
||||
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
|
||||
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
|
||||
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
|
||||
</context:component-scan>
|
||||
|
||||
<bean id="antPathMatcher" class="org.springframework.util.AntPathMatcher" />
|
||||
|
||||
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
|
||||
<property name="basenames">
|
||||
<list>
|
||||
<value>classpath:message/message-common</value>
|
||||
<value>classpath:message/authentication-message</value>
|
||||
<value>classpath:org/egovframe/rte/fdl/property/messages/properties</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="defaultEncoding" value="UTF-8"/>
|
||||
<property name="cacheSeconds">
|
||||
<value>60</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
|
||||
<property name="dateFormat" ref="dateFormat"/>
|
||||
</bean>
|
||||
<bean id="dateFormat" class="java.text.SimpleDateFormat">
|
||||
<constructor-arg index="0" value="yyyy-MM-dd HH:mm"/>
|
||||
</bean>
|
||||
|
||||
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
|
||||
<property name="defaultLocale" value="ko_KR"/>
|
||||
</bean>
|
||||
|
||||
<bean name="propertyService" class="org.egovframe.rte.fdl.property.impl.EgovPropertyServiceImpl" destroy-method="destroy">
|
||||
<property name="properties">
|
||||
<map>
|
||||
<entry key="tempDir" value="D:/workspace/temp"/>
|
||||
|
||||
<entry key="pageUnit" value="10"/>
|
||||
<entry key="pageSize" value="10"/>
|
||||
</map>
|
||||
</property>
|
||||
|
||||
<property name="extFileName">
|
||||
<set>
|
||||
<map>
|
||||
<entry key="encoding" value="UTF-8"/>
|
||||
<entry key="filename" value="classpath*:properties/xit-lvis.properties"/>
|
||||
</map>
|
||||
</set>
|
||||
</property>
|
||||
|
||||
</bean>
|
||||
|
||||
<bean id="leaveaTrace" class="org.egovframe.rte.fdl.cmmn.trace.LeaveaTrace" />
|
||||
|
||||
</beans>
|
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
|
||||
">
|
||||
|
||||
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
|
||||
<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"/>
|
||||
<property name="url" value="jdbc:log4jdbc:mariadb://211.119.124.9:4407/platform?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Seoul&useSSL=false" />
|
||||
<property name="username" value="fimsweb"/>
|
||||
<property name="password" value="fimsweb!@"/>
|
||||
<!--
|
||||
<property name="url" value="jdbc:log4jdbc:mariadb://localhost:3306/xit-base?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Seoul&useSSL=false" />
|
||||
<property name="username" value="root"/>
|
||||
<property name="password" value="mjkhan"/>
|
||||
-->
|
||||
</bean>
|
||||
|
||||
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean"
|
||||
p:dataSource-ref="dataSource"
|
||||
p:configLocation="classpath:sql/mybatis-config.xml"
|
||||
p:mapperLocations="classpath:sql/mapper/**/*.xml"
|
||||
/>
|
||||
|
||||
<bean id="mapperConfigurer" class="org.egovframe.rte.psl.dataaccess.mapper.MapperConfigurer">
|
||||
<property name="basePackage" value="cokr.xit" />
|
||||
<property name="sqlSessionFactoryBeanName" value="sqlSession"/>
|
||||
</bean>
|
||||
|
||||
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
</bean>
|
||||
|
||||
<tx:advice id="txAdvice" transaction-manager="txManager">
|
||||
<tx:attributes>
|
||||
<tx:method name="*" rollback-for="Exception"/>
|
||||
</tx:attributes>
|
||||
</tx:advice>
|
||||
|
||||
<aop:config>
|
||||
<aop:pointcut id="serviceMethod" expression="execution(* cokr.xit..service.bean..*ServiceBean.*(..))" />
|
||||
<aop:pointcut id="requiredTx" expression="execution(* cokr.xit..service.bean..*ServiceBean.*(..))"/>
|
||||
<aop:advisor advice-ref="txAdvice" pointcut-ref="requiredTx" />
|
||||
</aop:config>
|
||||
|
||||
</beans>
|
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cokr.xit.foundation.test.TestMapper">
|
||||
|
||||
<insert id="insert" parameterType="map">${sql}</insert>
|
||||
|
||||
<update id="update" parameterType="map">${sql}</update>
|
||||
|
||||
<delete id="delete" parameterType="map">${sql}</delete>
|
||||
|
||||
<update id="commit">COMMIT</update>
|
||||
|
||||
</mapper>
|
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="utility">
|
||||
|
||||
<!-- For Maria DB -->
|
||||
<sql id="paging-prefix"><if test="fetchSize != null and fetchSize > 0">
|
||||
SELECT QROWS.* FROM (
|
||||
SELECT ROW_NUMBER() OVER(<include refid="utility.sortBy" />) ROW_NUM
|
||||
, COUNT(*) OVER() TOT_CNT, QBODY.*
|
||||
FROM (</if></sql>
|
||||
|
||||
<sql id="paging-suffix"><if test="fetchSize != null and fetchSize > 0"> ) QBODY
|
||||
) QROWS
|
||||
WHERE ROW_NUM BETWEEN ((#{pageNum} - 1) * #{fetchSize}) + 1 AND (#{pageNum} * #{fetchSize})</if></sql>
|
||||
|
||||
<select id="foundRows" resultType="dataobject">/* 전체 결과수 가져오기(utility.foundRows) */
|
||||
SELECT FOUND_ROWS() TOT_CNT</select>
|
||||
|
||||
<sql id="sortBy"><if test="orderBy != null and orderBy != ''">ORDER BY ${orderBy}</if></sql>
|
||||
|
||||
<sql id="orderBy"><if test="fetchSize == null or fetchSize < 1"><include refid="utility.sortBy" /></if></sql>
|
||||
|
||||
<sql id="now">DATE_FORMAT(CURRENT_TIMESTAMP(), '%Y%m%d%H%i%s')</sql>
|
||||
|
||||
<sql id="selectNow">SELECT<include refid="utility.now" />NOW</sql>
|
||||
|
||||
<sql id="today">DATE_FORMAT(CURRENT_DATE, '%Y%m%d')</sql>
|
||||
|
||||
<sql id="selectToday">SELECT<include refid="utility.today" />TODAY</sql>
|
||||
|
||||
<sql id="thisDay">IFNULL(#{thisDay},<include refid="utility.today" />)</sql>
|
||||
|
||||
<sql id="selectThisDay">SELECT<include refid="utility.thisDay" />THIS_DAY</sql>
|
||||
|
||||
</mapper>
|
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
|
||||
<configuration>
|
||||
|
||||
<settings>
|
||||
<setting name="mapUnderscoreToCamelCase" value="false"/>
|
||||
<setting name="cacheEnabled" value="false" />
|
||||
<setting name="jdbcTypeForNull" value="NULL" />
|
||||
<setting name="callSettersOnNulls" value="true"/>
|
||||
</settings>
|
||||
|
||||
<typeAliases>
|
||||
<typeAlias alias="egovMap" type="org.egovframe.rte.psl.dataaccess.util.EgovMap"/>
|
||||
<typeAlias alias="dataobject" type="cokr.xit.foundation.data.DataObject"/>
|
||||
</typeAliases>
|
||||
|
||||
<typeHandlers>
|
||||
<typeHandler handler="cokr.xit.foundation.data.RowValueHandler" javaType="java.lang.Object"/>
|
||||
</typeHandlers>
|
||||
|
||||
<plugins>
|
||||
<plugin interceptor="cokr.xit.foundation.data.paging.PagingSupport" />
|
||||
</plugins>
|
||||
|
||||
</configuration>
|
Loading…
Reference in New Issue