mirror of
https://github.com/Weasley-J/chinaport-data-signature.git
synced 2024-04-21 12:41:53 +00:00
Merge branch 'feature_探索' into release_v1.1.1
This commit is contained in:
@@ -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`
|
||||
|
||||
<figure>
|
||||
<img src="https://weasley.oss-cn-shanghai.aliyuncs.com/Photos/iShot_2023-08-10_13.56.34.png" alt="邮件通知效果">
|
||||
<figcaption>加签失败邮件通知</figcaption>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
## 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)理论上支持中国内地的大部分**地方电子口岸**
|
||||
|
||||
+70
-18
@@ -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"
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* <figure>
|
||||
* <img src="https://weasley.oss-cn-shanghai.aliyuncs.com/Photos/iShot_2023-08-10_13.56.34.png" alt="邮件通知效果">
|
||||
* <figcaption>加签失败邮件通知</figcaption>
|
||||
* </figure>
|
||||
* </p>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-2
@@ -7,7 +7,8 @@ import java.util.Objects;
|
||||
|
||||
/**
|
||||
* u-key加签返回结果包装类
|
||||
* <p>以下对象模型禁止修改</p>
|
||||
* <p>
|
||||
* 以下对象模型禁止修改
|
||||
*
|
||||
* @author Weasley
|
||||
* @date 2022-02-12 19:7:6
|
||||
@@ -146,4 +147,4 @@ public class UkeyResponse implements Serializable {
|
||||
return Objects.hash(getResult(), getData(), getError());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-5
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
+61
-5
@@ -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 = """
|
||||
<ceb:CEB311Message guid="YGD8P9-WEASLEY-20230805155746-HOUDF2" version="1.0" xmlns:ceb="http://www.chinaport.gov.cn/ceb" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
|
||||
<ceb:Order>
|
||||
<ceb:OrderHead>
|
||||
<ceb:guid>YGD8P9-WEASLEY-20230805155746-HOUDF2</ceb:guid>
|
||||
<ceb:appType>1</ceb:appType>
|
||||
<ceb:appTime>20230704181028</ceb:appTime>
|
||||
<ceb:appStatus>2</ceb:appStatus>
|
||||
<ceb:orderType>I</ceb:orderType>
|
||||
<ceb:orderNo>T_C5051511332138160010</ceb:orderNo>
|
||||
<ceb:ebpCode>4601630004</ceb:ebpCode>
|
||||
<ceb:ebpName>海南省荣誉进出口贸易有限公司</ceb:ebpName>
|
||||
<ceb:ebcCode>4601630004</ceb:ebcCode>
|
||||
<ceb:ebcName>海南省荣誉进出口贸易有限公司</ceb:ebcName>
|
||||
<ceb:goodsValue>0.01</ceb:goodsValue>
|
||||
<ceb:freight>0</ceb:freight>
|
||||
<ceb:discount>0</ceb:discount>
|
||||
<ceb:taxTotal>0</ceb:taxTotal>
|
||||
<ceb:acturalPaid>0.01</ceb:acturalPaid>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:buyerRegNo>4</ceb:buyerRegNo>
|
||||
<ceb:buyerName>袁晓雨</ceb:buyerName>
|
||||
<ceb:buyerTelephone>13701727375</ceb:buyerTelephone>
|
||||
<ceb:buyerIdType>1</ceb:buyerIdType>
|
||||
<ceb:buyerIdNumber>130435200009241538</ceb:buyerIdNumber>
|
||||
<ceb:consignee>袁晓雨</ceb:consignee>
|
||||
<ceb:consigneeTelephone>13701727375</ceb:consigneeTelephone>
|
||||
<ceb:consigneeAddress>北京北京市东城区</ceb:consigneeAddress>
|
||||
<ceb:note>test</ceb:note>
|
||||
</ceb:OrderHead>
|
||||
<ceb:OrderList>
|
||||
<ceb:gnum>1</ceb:gnum>
|
||||
<ceb:itemNo>1</ceb:itemNo>
|
||||
<ceb:itemName>LANNA兰纳</ceb:itemName>
|
||||
<ceb:gmodel>10片/包</ceb:gmodel>
|
||||
<ceb:itemDescribe></ceb:itemDescribe>
|
||||
<ceb:barCode>1</ceb:barCode>
|
||||
<ceb:unit>011</ceb:unit>
|
||||
<ceb:qty>1</ceb:qty>
|
||||
<ceb:price>1</ceb:price>
|
||||
<ceb:totalPrice>1</ceb:totalPrice>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:country>136</ceb:country>
|
||||
<ceb:note>test</ceb:note>
|
||||
</ceb:OrderList>
|
||||
</ceb:Order>
|
||||
<ceb:BaseTransfer>
|
||||
<ceb:copCode>4601630004</ceb:copCode>
|
||||
<ceb:copName>海南省荣誉进出口贸易有限公司</ceb:copName>
|
||||
<ceb:dxpMode>DXP</ceb:dxpMode>
|
||||
<ceb:dxpId>DXPENT0000530815</ceb:dxpId>
|
||||
<ceb:note>test</ceb:note>
|
||||
</ceb:BaseTransfer>
|
||||
</ceb:CEB311Message>
|
||||
""";
|
||||
UkeyResponse response = JSONUtil.toBean(ukeyErrJson, new TypeReference<>() {
|
||||
}, true);
|
||||
handler.sendAlertSignFailure(response, messagePayload);
|
||||
}
|
||||
}
|
||||
|
||||
+38
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user