최초 커밋

master
mjkhan21 1 year ago
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&amp;characterEncoding=utf8&amp;serverTimezone=Asia/Seoul&amp;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&amp;characterEncoding=utf8&amp;serverTimezone=Asia/Seoul&amp;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 &gt; 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 &gt; 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 &lt; 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…
Cancel
Save