diff --git a/README.md b/README.md
index 27cc935..96b4847 100644
--- a/README.md
+++ b/README.md
@@ -77,6 +77,17 @@

+- 加签失败邮件通知告警
+
+ > [此功能需手动开启](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-app/src/main/resources/application-dev.yml#L25-L43),开关属性: `spring.mail.enable`, 取值: `on/off`
+
+
+
+ 加签失败邮件通知
+
+
+
+
## 1 支持的电子口岸
> [核心加签接口](https://github.com/Weasley-J/chinaport-data-signature/blob/main/%E4%B8%AD%E5%9B%BD%E7%94%B5%E5%AD%90%E5%8F%A3%E5%B2%B8%E6%8A%A5%E6%96%87%E5%8A%A0%E7%AD%BE%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3.md#%E6%B5%B7%E5%85%B3%E6%95%B0%E6%8D%AE%E5%8A%A0%E7%AD%BE)理论上支持中国内地的大部分**地方电子口岸**
diff --git a/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/core/WebSocketClientHandler.java b/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/core/WebSocketClientHandler.java
index ced9be8..2fbea73 100644
--- a/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/core/WebSocketClientHandler.java
+++ b/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/core/WebSocketClientHandler.java
@@ -13,6 +13,7 @@ import cn.alphahub.multiple.email.EmailTemplate.SimpleMailMessageDomain;
import cn.alphahub.multiple.email.annotation.Email;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.json.JSONUtil;
+import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@@ -37,23 +38,21 @@ import java.util.concurrent.locks.LockSupport;
* @date 2022/2/15
*/
@Slf4j
+@Getter
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class WebSocketClientHandler extends TextWebSocketHandler {
/**
* 电子口岸u-key的配置参数
*/
- @Getter
private final UkeyProperties ukeyProperties;
/**
* WebSocket包装类
*/
- @Getter
private final WebSocketWrapper webSocketWrapper;
/**
* X509Certificate证书判断
*/
- @Getter
private final CertificateHandler certificateHandler;
@Autowired
private EmailTemplate emailTemplate;
@@ -79,21 +78,21 @@ public class WebSocketClientHandler extends TextWebSocketHandler {
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
super.handleTextMessage(session, message);
log.warn("收到ukey响应数据: {}", message.getPayload());
- UkeyResponse response = JSONUtil.toBean(message.getPayload(), new TypeReference<>() {
+ UkeyResponse ukeyResponse = JSONUtil.toBean(message.getPayload(), new TypeReference<>() {
}, true);
- if (Objects.equals(response.get_id(), webSocketWrapper.getRequest().getId())) {
+ if (Objects.equals(ukeyResponse.get_id(), webSocketWrapper.getRequest().getId())) {
try {
- UkeyResponse.Args responseArgs = response.get_args();
+ UkeyResponse.Args responseArgs = ukeyResponse.get_args();
if (responseArgs.getResult().equals(true) && CollectionUtils.isNotEmpty(responseArgs.getData())) {
log.warn("电子口岸u-key加签数据成功:{}", JacksonUtil.toJson(responseArgs));
webSocketWrapper.getSignResult().setSuccess(true);
webSocketWrapper.getSignResult().setSignatureValue(responseArgs.getData().get(0));
webSocketWrapper.getSignResult().setCertNo(responseArgs.getData().get(1));
if (SignHandler.isSignXml(webSocketWrapper.getRequest())) {
- webSocketWrapper.getSignResult().setX509Certificate(certificateHandler.getX509Certificate(response.get_method()));
+ webSocketWrapper.getSignResult().setX509Certificate(certificateHandler.getX509Certificate(ukeyResponse.get_method()));
}
} else {
- sendAlertSignFailure(JacksonUtil.toPrettyJson(response));
+ sendAlertSignFailure(ukeyResponse, message.getPayload());
}
} catch (Exception e) {
webSocketWrapper.getSignResult().setSuccess(false);
@@ -115,31 +114,61 @@ public class WebSocketClientHandler extends TextWebSocketHandler {
* "Result": false,
* "Data": [],
* "Error": [
- * "[读卡器底层库]复位读卡器失败:错误码\u003d50070",
+ * "[读卡器底层库]复位读卡器失败:错误码:50070",
* "Err:Custom50070"
* ]
* }
* }
*
+ *
+ *
+ *
+ * 加签失败邮件通知
+ *
+ *
*
- * @param errorMessage error message
+ * @param ukeyResponse error response
+ * @param messagePayload 消息负载
* @implNote [读卡器底层库]复位读卡器失败会自动重启u-key的Windows进程,希望能提升自我容灾机制
* @since 2023-06-10
*/
@Email
- public void sendAlertSignFailure(String errorMessage) {
+ public void sendAlertSignFailure(UkeyResponse ukeyResponse, String messagePayload) {
+ String errorMessage = JacksonUtil.toPrettyJson(ukeyResponse);
+ String subject = "电子口岸 u-key 加签失败提醒";
+ String payload = StringUtils.defaultIfBlank(messagePayload, "");
if (emailProperties.getEnable().equals(true)) {
log.warn("电子口岸u-key加签数据失败:{}", errorMessage);
SimpleMailMessageDomain message = new SimpleMailMessageDomain();
message.setTo(emailProperties.getTo());
message.setCc(StringUtils.split(emailProperties.getCc(), ","));
message.setSentDate(LocalDateTime.now());
- message.setSubject("电子口岸u-key加签失败");
- if (errorMessage.contains("[读卡器底层库]复位读卡器失败")) {
- message.setText("电子口岸u-key加签失败,原因:\n" + errorMessage + "\n\n如遇:“[读卡器底层库]复位读卡器失败”等错误,请手动重启加签exe客户端程序。");
- restartWindowsWebsocketClient(message);
+ message.setSubject(subject);
+ message.setText("");
+ boolean shouldBreak = false;
+ boolean existsError = false;
+ if (CollectionUtils.isNotEmpty(ukeyResponse.get_args().getError())) {
+ for (String errorText : ukeyResponse.get_args().getError()) {
+ for (UkeyError ukeyError : UkeyError.values()) {
+ if (StringUtils.containsIgnoreCase(errorText, ukeyError.getErr())) {
+ restartUkeyWindowsWebsocketClient(message);
+ shouldBreak = true;
+ existsError = true;
+ break;
+ }
+ }
+ if (shouldBreak) {
+ break;
+ }
+ }
+ }
+ StringBuilder text = new StringBuilder()
+ .append("原始错误:\n").append(errorMessage).append("\n")
+ .append("数据载荷:\n").append(payload).append("\n");
+ if (existsError) {
+ message.setText(text.append("提示,如遇 “[读卡器底层库]复位读卡器失败” 等错误,程序自动重启客户端后如果还是不能加签,请手动重启加签exe客户端程序。").toString());
} else {
- message.setText("电子口岸u-key加签失败,原因:\n" + errorMessage);
+ message.setText(text.toString());
}
emailTemplate.send(message);
}
@@ -150,12 +179,35 @@ public class WebSocketClientHandler extends TextWebSocketHandler {
*
* @since 1.0.9
*/
- public void restartWindowsWebsocketClient(SimpleMailMessageDomain message) {
+ public void restartUkeyWindowsWebsocketClient(SimpleMailMessageDomain message) {
ConsoleOutput output = ukeyHealthHelper.fixUkey(Command.RESTART);
String restartLog = message.getText()
.concat("\n\n加签程序【chinaport-data-signature】重启Windows Websocket客户端,cmd终端信息:\n")
- .concat(JacksonUtil.toJson(output));
+ .concat(StringUtils.defaultIfBlank(JacksonUtil.toJson(output), ""));
message.setText(restartLog);
}
+ /**
+ * 电子口岸 u-key 发生错误,需要重启“客户端控件”的情况
+ */
+ @Getter
+ @AllArgsConstructor
+ private enum UkeyError {
+ RESETTING_CARD_READER_FAILED(50070, "[读卡器底层库]复位读卡器失败:错误码=50070", "Custom50070"),
+ OPEN_THE_CARD_READER_FAILED(50200, "[读卡器底层库]打开读卡器失败:错误码=50200", "Custom50200"),
+ ;
+ /**
+ * 错误码
+ */
+ private final int code;
+ /**
+ * 描述
+ */
+ private final String desc;
+ /**
+ * Err
+ */
+ private final String err;
+ }
+
}
diff --git a/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/entity/UkeyResponse.java b/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/entity/UkeyResponse.java
index 38044d7..6b4455e 100644
--- a/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/entity/UkeyResponse.java
+++ b/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/entity/UkeyResponse.java
@@ -7,7 +7,8 @@ import java.util.Objects;
/**
* u-key加签返回结果包装类
- * 以下对象模型禁止修改
+ *
+ * 以下对象模型禁止修改
*
* @author Weasley
* @date 2022-02-12 19:7:6
@@ -146,4 +147,4 @@ public class UkeyResponse implements Serializable {
return Objects.hash(getResult(), getData(), getError());
}
}
-}
\ No newline at end of file
+}
diff --git a/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/util/ClientIPUtils.java b/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/util/ClientIPUtils.java
index f30fa27..50998b3 100644
--- a/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/util/ClientIPUtils.java
+++ b/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/util/ClientIPUtils.java
@@ -11,22 +11,25 @@ import jakarta.servlet.http.HttpServletRequest;
public final class ClientIPUtils {
static final String UNKNOWN = "unknown";
+ private ClientIPUtils() {
+ }
+
public static String getClientIP(HttpServletRequest request) {
String ipAddress = request.getHeader("X-Forwarded-For");
- if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {
+ if (null == ipAddress || ipAddress.isEmpty() || UNKNOWN.equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
- if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {
+ if (null == ipAddress || ipAddress.isEmpty() || UNKNOWN.equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
- if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {
+ if (null == ipAddress || ipAddress.isEmpty() || UNKNOWN.equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_CLIENT_IP");
}
- if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {
+ if (null == ipAddress || ipAddress.isEmpty() || UNKNOWN.equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
}
- if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {
+ if (null == ipAddress || ipAddress.isEmpty() || UNKNOWN.equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
}
diff --git a/chinaport-data-signature-app/src/test/java/cn/alphahub/eport/signature/core/WebSocketClientHandlerTest.java b/chinaport-data-signature-app/src/test/java/cn/alphahub/eport/signature/core/WebSocketClientHandlerTest.java
index 02f2086..654f3aa 100644
--- a/chinaport-data-signature-app/src/test/java/cn/alphahub/eport/signature/core/WebSocketClientHandlerTest.java
+++ b/chinaport-data-signature-app/src/test/java/cn/alphahub/eport/signature/core/WebSocketClientHandlerTest.java
@@ -5,6 +5,7 @@ import cn.hutool.core.lang.TypeReference;
import cn.hutool.json.JSONUtil;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -24,8 +25,9 @@ class WebSocketClientHandlerTest {
}
@Test
+ @DisplayName("复位读卡器失败发送邮件通知")
void handleTextMessage() {
- String json = """
+ String ukeyErrJson = """
{
"_id": 1,
"_method": "cus-sec_SpcSignDataAsPEM",
@@ -40,9 +42,63 @@ class WebSocketClientHandlerTest {
}
}
""";
- UkeyResponse response = JSONUtil.toBean(json,
- new TypeReference<>() {
- }, true);
- handler.sendAlertSignFailure(JSONUtil.toJsonPrettyStr(response));
+ String messagePayload = """
+
+
+
+ YGD8P9-WEASLEY-20230805155746-HOUDF2
+ 1
+ 20230704181028
+ 2
+ I
+ T_C5051511332138160010
+ 4601630004
+ 海南省荣誉进出口贸易有限公司
+ 4601630004
+ 海南省荣誉进出口贸易有限公司
+ 0.01
+ 0
+ 0
+ 0
+ 0.01
+ 142
+ 4
+ 袁晓雨
+ 13701727375
+ 1
+ 130435200009241538
+ 袁晓雨
+ 13701727375
+ 北京北京市东城区
+ test
+
+
+ 1
+ 1
+ LANNA兰纳
+ 10片/包
+
+ 1
+ 011
+ 1
+ 1
+ 1
+ 142
+ 136
+ test
+
+
+
+ 4601630004
+ 海南省荣誉进出口贸易有限公司
+ DXP
+ DXPENT0000530815
+ test
+
+
+ """;
+ UkeyResponse response = JSONUtil.toBean(ukeyErrJson, new TypeReference<>() {
+ }, true);
+ handler.sendAlertSignFailure(response, messagePayload);
}
}
diff --git a/chinaport-data-signature-app/src/test/java/cn/alphahub/eport/signature/util/StringSimilarityExample.java b/chinaport-data-signature-app/src/test/java/cn/alphahub/eport/signature/util/StringSimilarityExample.java
new file mode 100644
index 0000000..d0258d5
--- /dev/null
+++ b/chinaport-data-signature-app/src/test/java/cn/alphahub/eport/signature/util/StringSimilarityExample.java
@@ -0,0 +1,38 @@
+package cn.alphahub.eport.signature.util;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 计算两个词的相似度
+ */
+@SuppressWarnings("deprecation")
+public class StringSimilarityExample {
+ public static void main(String[] args) {
+ String str1 = "hello";
+
+ String str2 = "holla";
+
+ String str3 = "hello";
+
+ int distance = StringUtils.getLevenshteinDistance(str1, str2);
+ double similarity = 1.0 - (double) distance / Math.max(str1.length(), str2.length());
+ System.out.println("1 Levenshtein distance: " + distance);
+ System.out.println("1 Similarity: " + similarity);
+
+
+ int distance2 = StringUtils.getLevenshteinDistance(str1, str3);
+ double similarity2 = 1.0 - (double) distance / Math.max(str1.length(), str3.length());
+
+ System.out.println("2 Levenshtein distance: " + distance2);
+ System.out.println("2 Similarity: " + similarity2);
+
+ str1 = "你好啊, 还啊哈";
+ str3 = "你好啊!我在春天等你。";
+ distance2 = StringUtils.getLevenshteinDistance(str1, str3);
+ similarity2 = 1.0 - (double) distance / Math.max(str1.length(), str3.length());
+
+ System.out.println("3 Levenshtein distance: " + distance2);
+ System.out.println("3 Similarity: " + similarity2);
+
+ }
+}