package com.eactive.ext.kjb.ums; import com.eactive.apim.portal.template.entity.MessageCode; import com.eactive.apim.portal.template.entity.MessageRequest; import com.eactive.ext.kjb.common.KjbProperty; import com.eactive.ext.kjb.common.KjbUmsException; import com.eactive.ext.kjb.ums.gson.IUmsGsonObject; import com.eactive.ext.kjb.ums.gson.UmsRequestPayload; import com.eactive.ext.kjb.ums.gson.VerifyPhoneUmsTemplate; import com.eactive.ext.kjb.utils.JsonUtil; import com.eactive.ext.kjb.utils.ValidationUtil; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.entity.EntityBuilder; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.util.Timeout; import org.springframework.util.StreamUtils; import java.io.IOException; import java.nio.charset.Charset; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.concurrent.*; @Slf4j public class KjbUmsService { public final static int[] ALLOW_CHANNELS = { MessageCode.Channels.KAKAO_ALIMTALK.getValue() }; private final static String SEND_URL = "/ums/openapi/send"; private final static String CONTENT_TYPE = "application/json; charset=utf-8"; private final static String AUTHORIZATION_FMT = "Bearer %s"; private final KjbProperty property; private final boolean isRealMode; private final CloseableHttpClient httpClient; private final RequestConfig requestConfig; public KjbUmsService(KjbProperty property) { if (property == null) { log.error("[KJB-UMS] 파라미터 설정 되지 않음"); throw new IllegalArgumentException("property is null"); } isRealMode = property.getUmsHostUrl() != null && StringUtils.isNotEmpty(property.getUmsHostUrl().trim()); if (isRealMode) { try { ValidationUtil.validateOrThrow(property); } catch (Exception e) { log.error("[KJB-UMS] 파라미터 설정 오류", e); throw e; } httpClient = HttpClients.createDefault(); requestConfig = RequestConfig.custom() .setConnectTimeout(Timeout.ofSeconds(property.getEaiBatchConnectionTimeout())) .setResponseTimeout(Timeout.ofSeconds(property.getEaiBatchResponseTimeout())) .build(); } else { httpClient = HttpClients.createDefault(); requestConfig = null; } this.property = property; } public static boolean isAllowChannel(MessageRequest message) { for (int channel : ALLOW_CHANNELS) { if ((message.getMessageCode().getChannels() & channel) == channel) { return true; } } return false; } public boolean send(MessageRequest messageRequest) throws KjbUmsException { if (messageRequest == null) { throw new IllegalArgumentException("messageRequest is null"); } if (MessageCode.Channels.KAKAO_ALIMTALK.getValue() != (messageRequest.getMessageCode().getChannels() & MessageCode.Channels.KAKAO_ALIMTALK.getValue())) { log.error("지원하지 않는 발송 채널 - {}", messageRequest.getMessageCode().toString()); throw new IllegalArgumentException("지원하지 않는 발송 채널 - " + messageRequest.getMessageCode().toString()); } IUmsGsonObject content = null; content = VerifyPhoneUmsTemplate.builder().authNumber(messageRequest.getMessage()).build(); String umsMessageId = this.sendKakaoAlimTalk(messageRequest.getId(), UmsBizWorkCode.VERIFY_PHONE, messageRequest.getPhone(), content ); log.info("MessageRequest sent - {}", umsMessageId); return true; } public String sendKakaoAlimTalk(String guid, UmsBizWorkCode bizWorkCode, String cpno, IUmsGsonObject content) throws KjbUmsException { if (content == null) { throw new IllegalArgumentException("content is null"); } if (StringUtils.isEmpty(guid)) { guid = String.format("APM-%s", DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(LocalDateTime.now())); } UmsRequestPayload payload = UmsRequestPayload.builder() .messageIdentityNo(guid) .msgBizWorkCode(bizWorkCode.getCode()) .content(content) .cellphone(cpno) .msgSndChnlCd(UmsRequestPayload.ChannelCode.KM) .build(); try { ValidationUtil.validateOrThrow(payload); } catch (Exception ex) { log.debug("UMS Request payload validation failed - {}", JsonUtil.toPrettyString(payload)); throw ex; } UmsCallResult result = call(SEND_URL, payload); if (!result.isSuccess()) { throw new KjbUmsException(result.getThrowable().getMessage()); } return result.getUmsMessageId(); } private UmsCallResult call(String url, UmsRequestPayload requestPayload) throws KjbUmsException { ExecutorService executor = Executors.newSingleThreadExecutor(); Gson gson = new Gson(); String json = gson.toJson(requestPayload); HttpPost httpPost = new HttpPost(property.getUmsHostUrl() + url); httpPost.setHeader("Content-Type", CONTENT_TYPE); httpPost.setHeader("Authorization", String.format(AUTHORIZATION_FMT, property.getUmsApiKey())); httpPost.setEntity(EntityBuilder.create().setText(json).setContentType(ContentType.APPLICATION_JSON).build()); if (log.isDebugEnabled()) { try { log.debug("Header - {}", httpPost.getHeader("Content-Type")); log.debug("Header - {}", httpPost.getHeader("Authorization")); log.debug("Request Body : {}", StreamUtils.copyToString(httpPost.getEntity().getContent(), Charset.defaultCharset())); } catch (ProtocolException | IOException e) { throw new RuntimeException(e); } } httpPost.setConfig(requestConfig); if (isRealMode) { Future future = executor.submit(() -> { return httpClient.execute(httpPost, res -> { try { UmsCallResult result; String body = EntityUtils.toString(res.getEntity()); if (log.isDebugEnabled()) { log.debug("Response - {}", body); } result = gson.fromJson(body, UmsCallResult.class); result.setUmsMessageId(requestPayload.getMessageIdentityNo()); result.setSuccess(true); return result; } catch (JsonSyntaxException e) { log.error("UMS Call error(JSON Syntax)", e); return UmsCallResult.builder() .umsMessageId(requestPayload.getMessageIdentityNo()) .isSuccess(false) .throwable(e) .build(); } catch (Exception e) { log.error("UMS Call error", e); return UmsCallResult.builder() .umsMessageId(requestPayload.getMessageIdentityNo()) .isSuccess(false) .throwable(e) .build(); } finally { log.debug("Response status - {}", res.getCode()); if (res.getCode() != 200) { Arrays.asList(res.getHeaders()).forEach(o -> { log.warn("[Header] {}: {}", o.getName(), o.getValue()); }); } } }); }); try { return future.get(property.getUmsTimeout(), TimeUnit.SECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { log.error("EAI Batch call exception", e); throw new KjbUmsException("EAI Ums call failed", e); } } else { log.warn("UMS URL 미지정으로 실제 호출을 진행하지 않음."); return UmsCallResult.builder().isSuccess(true).build(); } } }