diff --git a/src/main/java/cokr/xit/ens/core/config/SpringDocConfig.java b/src/main/java/cokr/xit/ens/core/config/SpringDocConfig.java index 2cb4c0d..3c428b5 100644 --- a/src/main/java/cokr/xit/ens/core/config/SpringDocConfig.java +++ b/src/main/java/cokr/xit/ens/core/config/SpringDocConfig.java @@ -1,19 +1,14 @@ package cokr.xit.ens.core.config; -import java.util.Arrays; -import java.util.List; +import java.util.*; -import org.springdoc.core.GroupedOpenApi; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; +import org.springdoc.core.*; +import org.springframework.beans.factory.annotation.*; +import org.springframework.context.annotation.*; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.info.Contact; -import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.info.License; -import io.swagger.v3.oas.models.servers.Server; +import io.swagger.v3.oas.models.*; +import io.swagger.v3.oas.models.info.*; +import io.swagger.v3.oas.models.servers.*; @Configuration public class SpringDocConfig { @@ -188,6 +183,14 @@ public class SpringDocConfig { .build(); } + @Profile({"local", "prod", "dev"}) + @Bean + public GroupedOpenApi niceApiDoc() { + return GroupedOpenApi.builder() + .group("전자고지-NICE 인증톡") + .pathsToMatch("/nice/talk/**") + .build(); + } // @Bean // public GroupedOpenApi adminApiDoc() { diff --git a/src/main/java/cokr/xit/ens/core/utils/DateUtil.java b/src/main/java/cokr/xit/ens/core/utils/DateUtil.java index aa1e614..37ad0ef 100644 --- a/src/main/java/cokr/xit/ens/core/utils/DateUtil.java +++ b/src/main/java/cokr/xit/ens/core/utils/DateUtil.java @@ -15,6 +15,7 @@ import lombok.extern.slf4j.*; @Slf4j public class DateUtil { + private static final String DEFAULT_YMD_DT_FMT ="yyyy-MM-dd HH:mm:ss"; /** *
메소드 설명: 절대시간(단위: milliesecond)을 현재 시간으로 반환 한다.diff --git a/src/main/java/cokr/xit/ens/modules/nice/cmm/NiceCiUtils.java b/src/main/java/cokr/xit/ens/modules/nice/cmm/NiceCiUtils.java index c9e73f1..fe50b5f 100644 --- a/src/main/java/cokr/xit/ens/modules/nice/cmm/NiceCiUtils.java +++ b/src/main/java/cokr/xit/ens/modules/nice/cmm/NiceCiUtils.java @@ -2,7 +2,6 @@ package cokr.xit.ens.modules.nice.cmm; import java.io.*; import java.nio.charset.*; -import java.util.concurrent.atomic.*; import org.apache.commons.lang3.*; @@ -138,8 +137,8 @@ public class NiceCiUtils { strRtnText.append(strText.charAt(i)); continue; } - - if (strText.charAt(i) > 127) + char c = strText.charAt(i); + if (c > 127 || c == '\n' || c == '\t') skipIdx += 2; else skipIdx++; @@ -163,20 +162,30 @@ public class NiceCiUtils { */ public static String getStringKr(String strText, int iBytes) { StringBuilder strRtnText = new StringBuilder(); - AtomicInteger iByte = new AtomicInteger(); - - strText.chars().limit(iBytes).forEach(c -> { - strRtnText.append((char) c); - iByte.addAndGet((c > 127) ? 2 : 1); - if (iByte.get() >= iBytes) return; - }); + int iByte = 0; + + // 문자열을 문자 배열로 변환하여 반복문을 통해 처리 + for (int i = 0; i < strText.length(); i++) { + char c = strText.charAt(i); + strRtnText.append(c); + // 한글 등의 2바이트 문자 처리 + if(c > 127 || c == '\n' || c == '\t') { + iByte += 2; + }else{ + iByte++; + } + // 지정된 바이트 수를 넘으면 반복 종료 + if (iByte >= iBytes) { + break; // for 루프 종료 + } + } return strRtnText.toString(); } public static int lengthKr(String strText) { return strText.chars() - .map(ch -> ch > 127 ? 2 : 1) // 한글(또는 다른 비 ASCII 문자)인 경우 2바이트, 아니면 1바이트 + .map(ch -> (ch > 127 || ch == '\n' || ch == '\t') ? 2 : 1) // 한글(또는 다른 비 ASCII 문자)인 경우 2바이트, 아니면 1바이트 .sum(); } @@ -203,6 +212,13 @@ public class NiceCiUtils { .readLine(); } + // 특수 문자를 2바이트로 간주하는 메서드 + private static boolean isToByte(char c) { + // 추가적으로 2바이트로 계산할 특수 문자 정의 + // 필요에 따라 추가 가능 + return "\t\n".indexOf(c) >= 0; + } + public static void main(String[] args) throws IOException { final String tgt = new String("123ab한글이ㅂa들어있다열자ㅁ".getBytes(), StandardCharsets.UTF_8); final String tgt2 = new String("123ab한글이ㅂa들어있다열자ㅁ".getBytes(), StandardCharsets.ISO_8859_1); @@ -224,8 +240,6 @@ public class NiceCiUtils { System.out.println(substringKr("gks한글이시작되는데", 8)); System.out.println(substringKr("한글로abcdefgh계속", 12)); System.out.println(substringKr("1한글2ㅇ ab", 10)); - //System.out.println(substringKor(tgt, 11)); -; System.out.println(leftKr(tgt, 18)); System.out.println(leftKr(tgt2, 18)); diff --git a/src/main/java/cokr/xit/ens/modules/nice/model/NiceCiCommon.java b/src/main/java/cokr/xit/ens/modules/nice/model/NiceCiCommon.java index 78e9bc1..5b5aa89 100644 --- a/src/main/java/cokr/xit/ens/modules/nice/model/NiceCiCommon.java +++ b/src/main/java/cokr/xit/ens/modules/nice/model/NiceCiCommon.java @@ -89,7 +89,7 @@ public class NiceCiCommon { * */ @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "단말기구분(3자리)", example = "503") - @Size(min = 1, max = 1, message = "단말기 구분은 3자리 입니다.") + @Size(min = 3, max = 3, message = "단말기 구분은 3자리 입니다.") private String deviceClassification = "503"; public void setDeviceClassification(String deviceClassification) { this.deviceClassification = StringUtils.rightPad(nvl(deviceClassification), 3, StringUtils.SPACE); @@ -111,7 +111,7 @@ public class NiceCiCommon { * */ @Schema(title = "응답코드(4자리)", example = " ") - @Pattern(regexp = "^$|^[P|E|S][\\d]{3}$", message = "응답코드(4자리)는 4자리 입니다") + @Pattern(regexp = "^\\s{4}$|$|^[P|E|S][\\d]{3}$", message = "응답코드(4자리)는 4자리 입니다") private String rsltCode = StringUtils.rightPad(StringUtils.EMPTY, 4, StringUtils.SPACE); public void setRsltCode(String rsltCode) { this.rsltCode = StringUtils.rightPad(nvl(rsltCode), 4, StringUtils.SPACE); diff --git a/src/main/java/cokr/xit/ens/modules/nice/model/NiceCiDTO.java b/src/main/java/cokr/xit/ens/modules/nice/model/NiceCiDTO.java index 7896660..92524b4 100644 --- a/src/main/java/cokr/xit/ens/modules/nice/model/NiceCiDTO.java +++ b/src/main/java/cokr/xit/ens/modules/nice/model/NiceCiDTO.java @@ -48,8 +48,8 @@ public class NiceCiDTO { * 전문길이 set * */ - @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "TR Code", example = " ") - @Size(min = 10, max = 10, message = "트랜잭션 코드는 10자리 입니다.") + //@Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "TR Code", example = " ") + //@Size(min = 10, max = 10, message = "트랜잭션 코드는 10자리 입니다.") private String trCode = StringUtils.EMPTY; public void setTrCode(String trCode) { this.trCode = StringUtils.leftPad(nvl(trCode), 10, StringUtils.SPACE); @@ -94,6 +94,7 @@ public class NiceCiDTO { */ @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "조회사유", example = " ") @Size(min = 2, max = 2, message = "조회사유는 2자리 입니다") + //@Pattern(regexp = "^\\s{2}$|s{1,2}", message = "조회사유는 2자리 입니다") private String queryReason = StringUtils.rightPad(StringUtils.EMPTY, 2, StringUtils.SPACE); public void setQueryReason(String queryReason) { this.queryReason = StringUtils.rightPad(nvl(queryReason), 2, StringUtils.SPACE); @@ -106,7 +107,7 @@ public class NiceCiDTO { * */ @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "조회요청건수", example = " ") - @Max(value = 48, message = "조회요청 최대건수는 48입니다") + @Pattern(regexp = "^\\s{2}$|\\d{1,2}", message = "조회요청 최대건수는 48입니다(2자리)") private String queryReqCnt = StringUtils.rightPad(StringUtils.EMPTY, 2, StringUtils.SPACE); public void setQueryReqCnt(Integer queryReqCnt) { this.queryReqCnt = StringUtils.rightPad(nvl(queryReqCnt == null? "": queryReqCnt.toString()), 2, StringUtils.SPACE); @@ -124,7 +125,7 @@ public class NiceCiDTO { */ @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "SMS발송요청구분코드", example = " ", allowableValues = {"0", "1", "2", "3"}) @Pattern(regexp = "[0-3]", message = "SMS발송요청구분코드 0 ~ 3 입니다(1자리)") - private String smsSndReqCode = StringUtils.SPACE;; + private String smsSndReqCode = "1"; /** *
@@ -133,7 +134,7 @@ public class NiceCiDTO { **/ @Schema(title = "SMS 발송메세지", example = " ") - @Pattern(regexp = "^$|[\\s]{10,2000}", message = "SMS발송메세지는 10 ~ 2000자리 입니다") + @Pattern(regexp = "^[\\s\\S]{10,2000}$", message = "SMS발송메세지는 10 ~ 2000자리 입니다") private String sndMessage = StringUtils.rightPad(StringUtils.EMPTY, 2000, StringUtils.SPACE); public void setSndMessage(String sndMessage) { this.sndMessage = NiceCiUtils.rightPadKr(nvl(sndMessage), 2000, StringUtils.SPACE); @@ -146,7 +147,7 @@ public class NiceCiDTO { * */ @Schema(title = "SMS 발신번호", example = " ") - @Pattern(regexp = "^$|[\\d]{3,12}", message = "SMS 발신번호는 3 ~ 12자리 입니다") + @Pattern(regexp = "^\\s{12}$|[\\d]{3,12}", message = "SMS 발신번호는 3 ~ 12자리 입니다") private String sndPhoneNo = StringUtils.rightPad(StringUtils.EMPTY, 12, StringUtils.SPACE); public void setSndPhoneNo(String sndPhoneNo) { this.sndPhoneNo = StringUtils.rightPad(nvl(sndPhoneNo), 12, StringUtils.SPACE); @@ -196,10 +197,10 @@ public class NiceCiDTO { * */ @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "버튼요청건수", example = " ") - @Max(value = 5, message = "버튼요청건수는 1자리 입니다") + @Pattern(regexp = "^ |[0-5]$", message = "버튼요청건수는 1자리로 최대 5 입니다.") private String btnReqCnt = StringUtils.SPACE; public void setBtnReqCnt(Integer btnReqCnt) { - this.btnReqCnt = StringUtils.rightPad(nvl(btnReqCnt == null ? "" : btnReqCnt.toString()), 2, StringUtils.SPACE); + this.btnReqCnt = StringUtils.rightPad(nvl(btnReqCnt == null ? "" : btnReqCnt.toString()), 1, StringUtils.SPACE); } /** @@ -464,11 +465,12 @@ public class NiceCiDTO { // return sb.toString(); // } - public static Response parse(String tgtStr) { + public static Response parse(String tgtString) { final int repeatLength = 110; final int[] parseLength = { 10, // tr-code 83, // 공통부 + // FIXME: spec과 상이 - 확인 필요 : "1"이 들어오고 있다 17, // 공란 2, // 응답건수 1, // SMS발송요청구분코드 @@ -478,9 +480,10 @@ public class NiceCiDTO { 84, // 공란 }; // FIXME: 인코딩확인후 적용 - String tgtString = NiceCiUtils.covertCharset(tgtStr, "EUC-KR"); + //String tgtString = NiceCiUtils.covertCharset(tgtStr, "EUC-KR"); - if (StringUtils.isNotBlank(tgtString) && tgtString.length() >= 2319) { + //if (StringUtils.isNotBlank(tgtString) && NiceCiUtils.lengthKr(tgtString) >= 2210) { + if (StringUtils.isNotBlank(tgtString)) { Response response = new Response(); int idx = 0; response.setTrCode(StringUtils.left(tgtString, parseLength[idx])); @@ -510,12 +513,13 @@ public class NiceCiDTO { response.setPrivateEmptyField(StringUtils.left(tgtString, parseLength[idx])); tgtString = tgtString.substring(parseLength[idx]); - if(tgtString.length() % repeatLength == 0){ - int repeat = tgtString.length() / repeatLength; + if(NiceCiUtils.lengthKr(tgtString) % repeatLength == 0){ + int repeat = NiceCiUtils.lengthKr(tgtString) / repeatLength; String finalTgtString = tgtString; List
+ * description : + * packageName : cokr.xit.ens.modules.nice.presentation + * fileName : NiceCiController + * author : limju + * date : 2024 9월 27 + * ====================================================================== + * 변경일 변경자 변경 내용 + * ---------------------------------------------------------------------- + * 2024 9월 27 limju 최초 생성 + * + *+ */ +@Tag(name = "NiceCiController", description = "NICE CI 인증톡") +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/nice/talk") +public class NiceCiController { + private final NiceCiService niceCiService; + + @Operation(summary = "(대량)전송요청") + @PostMapping(value = "/send/bulk", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity> sendBulk() { + return new ResponseEntity<>(niceCiService.requestSendBulk(), HttpStatus.OK); + } +} diff --git a/src/main/java/cokr/xit/ens/modules/nice/service/NiceCiService.java b/src/main/java/cokr/xit/ens/modules/nice/service/NiceCiService.java new file mode 100644 index 0000000..46a14fe --- /dev/null +++ b/src/main/java/cokr/xit/ens/modules/nice/service/NiceCiService.java @@ -0,0 +1,104 @@ +package cokr.xit.ens.modules.nice.service; + +import org.apache.commons.lang3.*; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.*; + +import cokr.xit.ens.core.aop.*; +import cokr.xit.ens.modules.nice.cmm.*; +import cokr.xit.ens.modules.nice.model.*; +import cokr.xit.ens.modules.nice.service.support.*; +import lombok.*; +import lombok.extern.slf4j.*; + +/** + *
+ * description : + * packageName : cokr.xit.ens.modules.nice.service + * fileName : NiceCiService + * author : limju + * date : 2024 9월 27 + * ====================================================================== + * 변경일 변경자 변경 내용 + * ---------------------------------------------------------------------- + * 2024 9월 27 limju 최초 생성 + * + *+ */ +@Slf4j +@Service +@RequiredArgsConstructor +public class NiceCiService { + @Value("${contract.niceCi.orgId}") + private String ORG_ID; + + @Value("${contract.niceCi.clientId}") + private String CLIENT_ID; + + private final NiceCiApiService niceCiApiService; + + final String msg = "민자도로 관리지원센터에서 김해찬님께 발송한 미납통행료 고지서가 도착했습니다.\n" + + "\n" + + "민자도로 미납통행료 고지서\n" + + "\n" + + "□ 차량번호 : 19너0914\n" + + "□ 미납발생 노선 : 서울-문산\n" + + "□ 미납발생 기간 : 2021년 04월 12일~2023년 08월 30일\n" + + "□ 납부금액 : 819,500원(42건)\n" + + "□ 납부기한 : 2024년10월01일\n" + + "□ 납부방법 : \n" + + "① 하단의 (납부하기) 클릭\n" + + "② 가상계좌 납부\n" + + "-(가상계좌) : 농협은행 792000-36-986609\n" + + "국민은행 731190-72-253083\n" + + "우리은행 283752-73-918780\n" + + "신한은행 562146-27-470101\n" + + "\n" + + "※ 알림톡 수신 시 종이고지서는 발송되지 않습니다.\n" + + "\n" + + "문의처 : 044-211-3377"; + + public EnsResponseVO> requestSendBulk() { + NiceCiDTO.Request ciRequest = new NiceCiDTO.Request(); + //ciRequest.setTrCode("0000006150"); + // // 공통부 + // + // // 개별요청부 + // nr.setQueryReason(StringUtils.EMPTY); + // nr.setQueryReqCnt(46); + // nr.setSmsSndReqCode("1"); + String tmp = NiceCiUtils.rightPadKr(msg, 2000, StringUtils.SPACE); + System.out.println( + String.format("[%s] kr length - %d, utf-8 length - %d", tmp, NiceCiUtils.lengthKr(tmp), tmp.length())); + ciRequest.setSndMessage(msg); + // nr.setSndPhoneNo("010"); + // nr.setContactSearchCode("1"); + + // 공통부 + NiceCiCommon nc = new NiceCiCommon(); + // nc.setGrpCode("grpCode"); + //nc.setTrType("Type"); + nc.setTrClassification("31895"); // 거래구분 + nc.setOrgId(ORG_ID); // 참가기관Id - property 에서 + nc.setOrgMngNo("0000000103"); // 기관관리번호 + nc.setOrgSndDt("20240919"); + + NiceCiDTO.QueryRequest qr = new NiceCiDTO.QueryRequest(); + NiceCiDTO.ButtonRequest br = new NiceCiDTO.ButtonRequest(); + + ciRequest.setNiceCommon(nc); + ciRequest.getQueryRequests().add(qr); + ciRequest.getButtonRequests().add(br); + // nc.setNiceMngNo(StringUtils.EMPTY); + // nc.setNiceSndDt(StringUtils.EMPTY); + String ciTxt = ciRequest.ofString(); + String ft = String.format("%s%s", StringUtils.leftPad(String.valueOf(NiceCiUtils.lengthKr(ciTxt)), 10, "0"), + ciTxt); + System.out.println( + String.format("[%s] kr length - %d, utf-8 length - %d", ft, NiceCiUtils.lengthKr(ft), ft.length())); + + // String rtnMsg = niceCiApiService.requestSendBulk(ciRequest); + // NiceCiDTO.Response resDTO = NiceCiDTO.Response.parse(rtnMsg); + return niceCiApiService.requestSendBulk(ciRequest); + } +} diff --git a/src/main/java/cokr/xit/ens/modules/nice/service/support/NiceCiApiService.java b/src/main/java/cokr/xit/ens/modules/nice/service/support/NiceCiApiService.java new file mode 100644 index 0000000..3601347 --- /dev/null +++ b/src/main/java/cokr/xit/ens/modules/nice/service/support/NiceCiApiService.java @@ -0,0 +1,127 @@ +package cokr.xit.ens.modules.nice.service.support; + +import java.io.*; +import java.net.*; +import java.nio.charset.*; +import java.util.*; +import java.util.stream.*; + +import javax.validation.*; + +import org.apache.commons.lang3.*; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.*; + +import cokr.xit.ens.core.aop.*; +import cokr.xit.ens.core.exception.*; +import cokr.xit.ens.core.exception.code.*; +import cokr.xit.ens.modules.nice.cmm.*; +import cokr.xit.ens.modules.nice.model.*; +import lombok.*; +import lombok.extern.slf4j.*; + +/** + *
+ * description : + * packageName : cokr.xit.ens.modules.nice.service.support + * fileName : NiceCiService + * author : limju + * date : 2024 9월 27 + * ====================================================================== + * 변경일 변경자 변경 내용 + * ---------------------------------------------------------------------- + * 2024 9월 27 limju 최초 생성 + * + *+ */ +@Slf4j +@Service +@RequiredArgsConstructor +public class NiceCiApiService { + + @Value("${contract.niceCi.host}") + private String HOST; + + @Value("${contract.niceCi.port}") + private int PORT; + + @Value("${contract.niceCi.timeout}") + private int TIMEOUT; + + private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + + public EnsResponseVO> requestSendBulk(final NiceCiDTO.Request reqDTO) { + List