diff --git a/README.md b/README.md index 27cc935..96b4847 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,17 @@ ![image-20230802165954038](https://weasley.oss-cn-shanghai.aliyuncs.com/Photos/image-20230802165954038.png) +- 加签失败邮件通知告警 + + > [此功能需手动开启](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); + + } +}