UI 수정, SMS 클래스 추가
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
package com.handong.smartservice.component;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.Charset;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Munja114 (www.munja114.co.kr) SMS/LMS URL integration.
|
||||
*
|
||||
* Posts an application/x-www-form-urlencoded body to the Munja114 remote endpoint.
|
||||
* SMS or LMS is chosen automatically by the EUC-KR byte length of the message:
|
||||
* up to 90 bytes is sent as SMS, longer text is sent as LMS (2000 bytes max).
|
||||
*
|
||||
* The response body is a pipe separated string: code|msg|nums|cols|etc1|etc2
|
||||
*/
|
||||
public class SMSSender {
|
||||
private static final Logger logger = LoggerFactory.getLogger(SMSSender.class);
|
||||
|
||||
// Munja114 account, issued and authorized through the Munja114 customer center
|
||||
private static final String REMOTE_ID = "";
|
||||
private static final String REMOTE_PASS = "";
|
||||
|
||||
private static final String URL_SMS = "https://www.munja114.co.kr/Remote/RemoteSms.html";
|
||||
private static final String URL_LMS = "https://www.munja114.co.kr/Remote/RemoteMms.html";
|
||||
|
||||
// Munja114 sends and expects EUC-KR encoded text
|
||||
private static final Charset CHARSET = Charset.forName("EUC-KR");
|
||||
|
||||
private static final int SMS_MAX_BYTES = 90;
|
||||
private static final int LMS_MAX_BYTES = 2000;
|
||||
|
||||
// Subject applies to LMS only, 20 characters max, no special characters
|
||||
private static final String LMS_SUBJECT = "알림";
|
||||
|
||||
private static final Duration TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
// Result codes returned by the gateway
|
||||
public static final String CODE_SUCCESS = "0000";
|
||||
public static final String CODE_CONNECT_ERROR = "0001";
|
||||
public static final String CODE_AUTH_ERROR = "0002";
|
||||
public static final String CODE_NO_CALL = "0003";
|
||||
public static final String CODE_MSG_FORMAT_ERROR = "0004";
|
||||
public static final String CODE_CALLBACK_ERROR = "0005";
|
||||
public static final String CODE_PHONE_COUNT_ERROR = "0006";
|
||||
public static final String CODE_RESERVE_TIME_ERROR = "0007";
|
||||
public static final String CODE_NOT_ENOUGH_CALL = "0008";
|
||||
public static final String CODE_SEND_FAIL = "0009";
|
||||
public static final String CODE_MSG_TOO_LONG = "0012";
|
||||
public static final String CODE_CALLBACK_NOT_REGISTERED = "0030";
|
||||
public static final String CODE_CALLBACK_TYPE_FAIL = "0033";
|
||||
public static final String CODE_SEND_LIMITED = "0080";
|
||||
public static final String CODE_BLOCKED = "6666";
|
||||
public static final String CODE_UNPAID = "9999";
|
||||
|
||||
// Local codes, never returned by the gateway
|
||||
public static final String CODE_INVALID_PARAMETER = "9001";
|
||||
public static final String CODE_EXCEPTION = "9002";
|
||||
|
||||
private static final HttpClient httpClient = HttpClient.newBuilder()
|
||||
.connectTimeout(TIMEOUT)
|
||||
.build();
|
||||
|
||||
|
||||
/** Send result parsed from the gateway response. */
|
||||
public static class Result {
|
||||
public final String code; // result code, "0000" on success
|
||||
public final String msg; // result message
|
||||
public final int sentCount; // number of messages sent
|
||||
public final int remainCount; // remaining call count of the account
|
||||
public final String rawResponse;
|
||||
|
||||
public Result(String code, String msg, int sentCount, int remainCount, String rawResponse) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.sentCount = sentCount;
|
||||
this.remainCount = remainCount;
|
||||
this.rawResponse = rawResponse;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return CODE_SUCCESS.equals(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SMSSender.Result{code=" + code + ", msg=" + msg + ", sentCount=" + sentCount + ", remainCount=" + remainCount + "}";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a text message.
|
||||
*
|
||||
* @param callback sender number, must be pre-registered with the carrier (digits only, other characters are stripped)
|
||||
* @param phone receiver number, comma separated for multiple receivers
|
||||
* @param msg message body, sent as SMS when it fits in 90 EUC-KR bytes, otherwise as LMS
|
||||
*/
|
||||
public static Result send(String callback, String phone, String msg) {
|
||||
if (!hasText(callback) || !hasText(phone) || !hasText(msg)) {
|
||||
logger.warn("send: missing parameter, callback={}, phone={}", callback, phone);
|
||||
return new Result(CODE_INVALID_PARAMETER, "invalid parameter", 0, 0, "");
|
||||
}
|
||||
|
||||
String callbackNum = callback.replaceAll("[^0-9]", "");
|
||||
String phoneList = phone.replaceAll("[^0-9,]", "");
|
||||
int receiverCount = phoneList.split(",").length;
|
||||
|
||||
byte[] msgBytes = msg.getBytes(CHARSET);
|
||||
boolean isLms = msgBytes.length > SMS_MAX_BYTES;
|
||||
|
||||
if (msgBytes.length > LMS_MAX_BYTES) {
|
||||
logger.warn("send: message too long, bytes={}", msgBytes.length);
|
||||
return new Result(CODE_MSG_TOO_LONG, "message too long", 0, 0, "");
|
||||
}
|
||||
|
||||
StringBuilder body = new StringBuilder();
|
||||
appendParam(body, "remote_id", REMOTE_ID);
|
||||
appendParam(body, "remote_pass", REMOTE_PASS);
|
||||
appendParam(body, "remote_num", String.valueOf(receiverCount));
|
||||
appendParam(body, "remote_reserve", "0");
|
||||
appendParam(body, "remote_phone", phoneList);
|
||||
appendParam(body, "remote_callback", callbackNum);
|
||||
appendParam(body, "remote_msg", msg);
|
||||
if (isLms)
|
||||
appendParam(body, "remote_subject", LMS_SUBJECT);
|
||||
|
||||
String url = isLms ? URL_LMS : URL_SMS;
|
||||
|
||||
try {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.timeout(TIMEOUT)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded;charset=ko")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(body.toString(), CHARSET))
|
||||
.build();
|
||||
|
||||
HttpResponse<byte[]> response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray());
|
||||
|
||||
if (response.statusCode() != 200) {
|
||||
logger.warn("send: http status={}, url={}", response.statusCode(), url);
|
||||
return new Result(CODE_CONNECT_ERROR, "http status " + response.statusCode(), 0, 0, "");
|
||||
}
|
||||
|
||||
String raw = new String(response.body(), CHARSET).trim();
|
||||
Result result = parseResult(raw);
|
||||
|
||||
if (result.isSuccess())
|
||||
logger.info("send: ok, phone={}, type={}, remain={}", phoneList, isLms ? "lms" : "sms", result.remainCount);
|
||||
else
|
||||
logger.warn("send: failed, phone={}, result={}", phoneList, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.warn("send: exception, url={}, msg={}", url, e.getMessage());
|
||||
return new Result(CODE_EXCEPTION, e.getMessage(), 0, 0, "");
|
||||
}
|
||||
}
|
||||
|
||||
// Response format: code|msg|nums|cols|etc1|etc2
|
||||
private static Result parseResult(String raw) {
|
||||
String[] arr = raw.split("\\|");
|
||||
|
||||
String code = arr.length > 0 ? arr[0].trim() : "";
|
||||
String msg = arr.length > 1 ? arr[1].trim() : "";
|
||||
int sentCount = arr.length > 2 ? toInt(arr[2]) : 0;
|
||||
int remainCount = arr.length > 3 ? toInt(arr[3]) : 0;
|
||||
|
||||
return new Result(code, msg, sentCount, remainCount, raw);
|
||||
}
|
||||
|
||||
private static void appendParam(StringBuilder body, String key, String value) {
|
||||
if (body.length() > 0)
|
||||
body.append('&');
|
||||
|
||||
body.append(key).append('=').append(URLEncoder.encode(value == null ? "" : value, CHARSET));
|
||||
}
|
||||
|
||||
private static boolean hasText(String str) {
|
||||
return str != null && !str.trim().isEmpty();
|
||||
}
|
||||
|
||||
private static int toInt(String str) {
|
||||
try {
|
||||
return Integer.parseInt(str.trim());
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user