feat:报告签名处理
This commit is contained in:
@@ -17,6 +17,7 @@ import com.zt.plat.framework.common.pojo.PageResult;
|
|||||||
public interface ConfigUserSignatureService {
|
public interface ConfigUserSignatureService {
|
||||||
|
|
||||||
ConfigUserSignatureDO getByUserId(Long userId);
|
ConfigUserSignatureDO getByUserId(Long userId);
|
||||||
|
List<ConfigUserSignatureDO> getByIdList(List<Long> ids);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建手写签名配置
|
* 创建手写签名配置
|
||||||
|
|||||||
@@ -43,6 +43,13 @@ public class ConfigUserSignatureServiceImpl implements ConfigUserSignatureServic
|
|||||||
return CollUtil.isEmpty(list) ? null : list.get(0);
|
return CollUtil.isEmpty(list) ? null : list.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ConfigUserSignatureDO> getByIdList(List<Long> ids) {
|
||||||
|
LambdaQueryWrapper<ConfigUserSignatureDO> query = new LambdaQueryWrapper<>();
|
||||||
|
query.in(ConfigUserSignatureDO::getId, ids);
|
||||||
|
return configUserSignatureMapper.selectList(query);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigUserSignatureRespVO createConfigUserSignature(ConfigUserSignatureSaveReqVO createReqVO) {
|
public ConfigUserSignatureRespVO createConfigUserSignature(ConfigUserSignatureSaveReqVO createReqVO) {
|
||||||
// 插入
|
// 插入
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import com.zt.plat.framework.common.pojo.PageResult;
|
|||||||
import com.zt.plat.framework.common.pojo.vo.BatchDeleteReqVO;
|
import com.zt.plat.framework.common.pojo.vo.BatchDeleteReqVO;
|
||||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||||
import com.zt.plat.framework.excel.core.util.ExcelUtils;
|
import com.zt.plat.framework.excel.core.util.ExcelUtils;
|
||||||
|
import com.zt.plat.module.qms.business.config.dal.dataobject.ConfigUserSignatureDO;
|
||||||
|
import com.zt.plat.module.qms.business.config.service.ConfigUserSignatureService;
|
||||||
import com.zt.plat.module.qms.business.reportdoc.controller.vo.*;
|
import com.zt.plat.module.qms.business.reportdoc.controller.vo.*;
|
||||||
import com.zt.plat.module.qms.business.reportdoc.dal.dataobject.ReportDocumentMainDO;
|
import com.zt.plat.module.qms.business.reportdoc.dal.dataobject.ReportDocumentMainDO;
|
||||||
import com.zt.plat.module.qms.business.reportdoc.dal.dataobject.ReportDocumentTypeDO;
|
import com.zt.plat.module.qms.business.reportdoc.dal.dataobject.ReportDocumentTypeDO;
|
||||||
@@ -24,10 +26,12 @@ import jakarta.annotation.Resource;
|
|||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static com.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
import static com.zt.plat.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||||
@@ -51,6 +55,7 @@ public class ReportDocumentMainController extends AbstractFileUploadController i
|
|||||||
|
|
||||||
@Resource private ReportDocumentMainService reportDocumentMainService;
|
@Resource private ReportDocumentMainService reportDocumentMainService;
|
||||||
@Resource private ReportDocumentTypeService reportDocumentTypeService;
|
@Resource private ReportDocumentTypeService reportDocumentTypeService;
|
||||||
|
@Resource private ConfigUserSignatureService configUserSignatureService;
|
||||||
|
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
@Operation(summary = "创建检测报告")
|
@Operation(summary = "创建检测报告")
|
||||||
@@ -146,7 +151,39 @@ public class ReportDocumentMainController extends AbstractFileUploadController i
|
|||||||
//@PreAuthorize("@ss.hasPermission('qms:report-document-main:query')")
|
//@PreAuthorize("@ss.hasPermission('qms:report-document-main:query')")
|
||||||
public CommonResult<ReportDocumentMainRespVO> getReportDocumentMain(@RequestParam("id") Long id) {
|
public CommonResult<ReportDocumentMainRespVO> getReportDocumentMain(@RequestParam("id") Long id) {
|
||||||
ReportDocumentMainDO reportDocumentMain = reportDocumentMainService.getReportDocumentMain(id);
|
ReportDocumentMainDO reportDocumentMain = reportDocumentMainService.getReportDocumentMain(id);
|
||||||
return success(BeanUtils.toBean(reportDocumentMain, ReportDocumentMainRespVO.class));
|
|
||||||
|
ReportDocumentMainRespVO vo = BeanUtils.toBean(reportDocumentMain, ReportDocumentMainRespVO.class);
|
||||||
|
|
||||||
|
//处理签名
|
||||||
|
String docSig = vo.getDocumentSignature();
|
||||||
|
if(!ObjectUtils.isEmpty(docSig)){
|
||||||
|
JSONObject docSigJson = JSONObject.parseObject(docSig);
|
||||||
|
List<Long> sigIds = new ArrayList<>();
|
||||||
|
docSigJson.forEach((key, value) -> {
|
||||||
|
JSONObject obj = docSigJson.getJSONObject( key);
|
||||||
|
String signatureId = obj.getString("signatureId");
|
||||||
|
if(!ObjectUtils.isEmpty(signatureId))
|
||||||
|
sigIds.add(Long.parseLong(signatureId));
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!sigIds.isEmpty()){
|
||||||
|
List<ConfigUserSignatureDO> sigList = configUserSignatureService.getByIdList(sigIds);
|
||||||
|
docSigJson.forEach((key, value) -> {
|
||||||
|
JSONObject obj = docSigJson.getJSONObject( key);
|
||||||
|
String signatureId = obj.getString("signatureId");
|
||||||
|
if(!ObjectUtils.isEmpty(signatureId)){
|
||||||
|
ConfigUserSignatureDO sig = sigList.stream().filter(item -> item.getId().equals(Long.parseLong(signatureId))).findFirst().orElse(null);
|
||||||
|
if(sig != null){
|
||||||
|
String base64 = sig.getSignatureContent();
|
||||||
|
obj.put("signatureIdBase64", base64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
vo.setDocumentSignature(docSigJson.toJSONString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return success(vo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
|
|||||||
@@ -337,6 +337,7 @@ public class ReportDocumentMainServiceImpl implements ReportDocumentMainService,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void assembleSignature(String currentActivityId, ReportDocumentMainDO entity){
|
private void assembleSignature(String currentActivityId, ReportDocumentMainDO entity){
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
String sign = entity.getDocumentSignature();
|
String sign = entity.getDocumentSignature();
|
||||||
JSONObject signObj = new JSONObject();
|
JSONObject signObj = new JSONObject();
|
||||||
if(!ObjectUtils.isEmpty( sign))
|
if(!ObjectUtils.isEmpty( sign))
|
||||||
@@ -350,9 +351,10 @@ public class ReportDocumentMainServiceImpl implements ReportDocumentMainService,
|
|||||||
String nickName = SecurityFrameworkUtils.getLoginUserNickname();
|
String nickName = SecurityFrameworkUtils.getLoginUserNickname();
|
||||||
ConfigUserSignatureDO configUserSignatureDO = configUserSignatureService.getByUserId(userId);
|
ConfigUserSignatureDO configUserSignatureDO = configUserSignatureService.getByUserId(userId);
|
||||||
if(configUserSignatureDO != null)
|
if(configUserSignatureDO != null)
|
||||||
obj.put("fileId", configUserSignatureDO.getFileId());
|
obj.put("signatureId", configUserSignatureDO.getId());
|
||||||
obj.put("userId", userId);
|
obj.put("userId", userId);
|
||||||
obj.put("userName", nickName);
|
obj.put("userName", nickName);
|
||||||
|
obj.put("signTime", sdf.format(new Date()));
|
||||||
signObj.put(currentActivityId, obj);
|
signObj.put(currentActivityId, obj);
|
||||||
entity.setDocumentSignature(signObj.toJSONString());
|
entity.setDocumentSignature(signObj.toJSONString());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,277 @@
|
|||||||
|
package com.zt.plat.module.qms;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.zt.plat.framework.common.util.security.CryptoSignatureUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
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.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class DatabusApiInvocationExample {
|
||||||
|
public static final String TIMESTAMP = Long.toString(System.currentTimeMillis());
|
||||||
|
private static final String APP_ID = "iwork";
|
||||||
|
private static final String APP_SECRET = "lpGXiNe/GMLk0vsbYGLa8eYxXq8tGhTbuu3/D4MJzIk=";
|
||||||
|
// private static final String APP_ID = "test";
|
||||||
|
// private static final String APP_SECRET = "RSYtKXrXPLMy3oeh0cOro6QCioRUgqfnKCkDkNq78sI=";
|
||||||
|
// private static final String APP_ID = "testAnnoy";
|
||||||
|
// private static final String APP_SECRET = "jyGCymUjCFL2i3a4Tm3qBIkUrUl4ZgKPYvOU/47ZWcM=";
|
||||||
|
private static final String ENCRYPTION_TYPE = CryptoSignatureUtils.ENCRYPT_TYPE_AES;
|
||||||
|
private static final String TARGET_API = "http://172.16.46.63:30081/admin-api/databus/api/portal/qms.ytjc.iwork/v1";
|
||||||
|
// private static final String TARGET_API = "http://127.0.0.1:48080/admin-api/databus/api/portal/lgstOpenApi/v1";
|
||||||
|
private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder()
|
||||||
|
.connectTimeout(Duration.ofSeconds(5))
|
||||||
|
.build();
|
||||||
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||||
|
private static final PrintStream OUT = buildConsolePrintStream();
|
||||||
|
public static final String ZT_APP_ID = "ZT-App-Id";
|
||||||
|
public static final String ZT_TIMESTAMP = "ZT-Timestamp";
|
||||||
|
public static final String ZT_NONCE = "ZT-Nonce";
|
||||||
|
public static final String ZT_SIGNATURE = "ZT-Signature";
|
||||||
|
public static final String ZT_AUTH_TOKEN = "ZT-Auth-Token";
|
||||||
|
public static final String CONTENT_TYPE = "Content-Type";
|
||||||
|
|
||||||
|
private DatabusApiInvocationExample() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
OUT.println("=== GET 请求示例 ===");
|
||||||
|
// executeGetExample();
|
||||||
|
// OUT.println();
|
||||||
|
// OUT.println("=== POST 请求示例 ===");
|
||||||
|
executePostExample();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void executeGetExample() throws Exception {
|
||||||
|
Map<String, Object> queryParams = new LinkedHashMap<>();
|
||||||
|
queryParams.put("businessCode", "11");
|
||||||
|
queryParams.put("fileId", "11");
|
||||||
|
queryParams.put("null", null);
|
||||||
|
String signature = generateSignature(queryParams, Map.of());
|
||||||
|
URI requestUri = buildUri(TARGET_API, queryParams);
|
||||||
|
String nonce = "171615676c7d4d96b9f55f3d90ad27e0";
|
||||||
|
|
||||||
|
HttpRequest request = HttpRequest.newBuilder(requestUri)
|
||||||
|
.timeout(Duration.ofSeconds(10))
|
||||||
|
.header(ZT_APP_ID, APP_ID)
|
||||||
|
.header(ZT_TIMESTAMP, TIMESTAMP)
|
||||||
|
.header(ZT_NONCE, nonce)
|
||||||
|
.header(ZT_SIGNATURE, signature)
|
||||||
|
// .header("ZT-Auth-Token", "a75c0ea94c7f4a88b86b60bbc0b432c3")
|
||||||
|
.GET()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpResponse<String> response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||||
|
printResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void executePostExample() throws Exception {
|
||||||
|
Map<String, Object> queryParams = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
JSONObject jsonObject = new JSONObject();
|
||||||
|
|
||||||
|
jsonObject.put("id", "1968843568287817730");
|
||||||
|
jsonObject.put("sampleName", "545(委检样)");
|
||||||
|
jsonObject.put("materialName", "ff");
|
||||||
|
jsonObject.put("materialId", "123");
|
||||||
|
jsonObject.put("dictionaryBusinessId", "1965289399255388162");
|
||||||
|
|
||||||
|
long extraTimestamp = 1761556157185L;
|
||||||
|
String bodyJson = String.format(jsonObject.toJSONString(), extraTimestamp);
|
||||||
|
|
||||||
|
Map<String, Object> bodyParams = parseBodyJson(bodyJson);
|
||||||
|
String signature = generateSignature(queryParams, bodyParams);
|
||||||
|
URI requestUri = buildUri(TARGET_API, queryParams);
|
||||||
|
String nonce = randomNonce();
|
||||||
|
String cipherBody = encryptPayload(bodyJson);
|
||||||
|
OUT.println("原始 Request Body: " + bodyJson);
|
||||||
|
OUT.println("加密 Request Body: " + cipherBody);
|
||||||
|
|
||||||
|
HttpRequest request = HttpRequest.newBuilder(requestUri)
|
||||||
|
.timeout(Duration.ofSeconds(10))
|
||||||
|
.header(ZT_APP_ID, APP_ID)
|
||||||
|
.header(ZT_TIMESTAMP, TIMESTAMP)
|
||||||
|
.header(ZT_NONCE, nonce)
|
||||||
|
.header(ZT_SIGNATURE, signature)
|
||||||
|
.header(ZT_AUTH_TOKEN, "843304444f65423f901a3efe2f252a75")
|
||||||
|
.header(CONTENT_TYPE, "application/json")
|
||||||
|
.header("visit-company-id", "101")
|
||||||
|
.header("visit-dept-id", "103")
|
||||||
|
.POST(HttpRequest.BodyPublishers.ofString(cipherBody, StandardCharsets.UTF_8))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpResponse<String> response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||||
|
printResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String encryptPayload(String plaintext) {
|
||||||
|
try {
|
||||||
|
return CryptoSignatureUtils.encrypt(plaintext, APP_SECRET, ENCRYPTION_TYPE);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new IllegalStateException("Failed to encrypt request body", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printResponse(HttpResponse<String> response) {
|
||||||
|
OUT.println("HTTP Status: " + response.statusCode());
|
||||||
|
String cipherText = response.body();
|
||||||
|
OUT.println("加密 Response: " + cipherText);
|
||||||
|
String plain = tryDecrypt(cipherText);
|
||||||
|
OUT.println("原始 Response: " + normalizePotentialMojibake(plain));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String randomNonce() {
|
||||||
|
return UUID.randomUUID().toString().replace("-", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static URI buildUri(String baseUrl, Map<String, Object> queryParams) {
|
||||||
|
if (queryParams == null || queryParams.isEmpty()) {
|
||||||
|
return URI.create(baseUrl);
|
||||||
|
}
|
||||||
|
StringBuilder builder = new StringBuilder(baseUrl);
|
||||||
|
builder.append(baseUrl.contains("?") ? '&' : '?');
|
||||||
|
boolean first = true;
|
||||||
|
for (Map.Entry<String, Object> entry : queryParams.entrySet()) {
|
||||||
|
if (!first) {
|
||||||
|
builder.append('&');
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
builder.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8));
|
||||||
|
builder.append('=');
|
||||||
|
builder.append(URLEncoder.encode(String.valueOf(entry.getValue()), StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
return URI.create(builder.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String generateSignature(Map<String, Object> queryParams, Map<String, Object> bodyParams) {
|
||||||
|
TreeMap<String, Object> sorted = new TreeMap<>();
|
||||||
|
if (queryParams != null) {
|
||||||
|
queryParams.forEach((key, value) -> sorted.put(key, normalizeValue(value)));
|
||||||
|
}
|
||||||
|
if (bodyParams != null) {
|
||||||
|
bodyParams.forEach((key, value) -> sorted.put(key, normalizeValue(value)));
|
||||||
|
}
|
||||||
|
sorted.put(ZT_APP_ID, APP_ID);
|
||||||
|
sorted.put(ZT_TIMESTAMP, TIMESTAMP);
|
||||||
|
StringBuilder canonical = new StringBuilder();
|
||||||
|
sorted.forEach((key, value) -> {
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (canonical.length() > 0) {
|
||||||
|
canonical.append('&');
|
||||||
|
}
|
||||||
|
canonical.append(key).append('=').append(value);
|
||||||
|
});
|
||||||
|
OUT.println("原始 签名串: " + canonical);
|
||||||
|
String md5Hex = md5Hex(canonical.toString());
|
||||||
|
OUT.println("原始签名: " + md5Hex);
|
||||||
|
return md5Hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object normalizeValue(Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (value instanceof Map || value instanceof Iterable) {
|
||||||
|
try {
|
||||||
|
return OBJECT_MAPPER.writeValueAsString(value);
|
||||||
|
} catch (JsonProcessingException ignored) {
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Object> parseBodyJson(String bodyJson) {
|
||||||
|
if (bodyJson == null || bodyJson.isBlank()) {
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return OBJECT_MAPPER.readValue(bodyJson, new TypeReference<Map<String, Object>>() { });
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new IllegalArgumentException("Failed to parse request body JSON", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String md5Hex(String input) {
|
||||||
|
try {
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("MD5");
|
||||||
|
byte[] bytes = digest.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||||
|
StringBuilder hex = new StringBuilder(bytes.length * 2);
|
||||||
|
for (byte b : bytes) {
|
||||||
|
String segment = Integer.toHexString(b & 0xFF);
|
||||||
|
if (segment.length() == 1) {
|
||||||
|
hex.append('0');
|
||||||
|
}
|
||||||
|
hex.append(segment);
|
||||||
|
}
|
||||||
|
return hex.toString();
|
||||||
|
} catch (NoSuchAlgorithmException ex) {
|
||||||
|
throw new IllegalStateException("MD5 algorithm not available", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String tryDecrypt(String cipherText) {
|
||||||
|
if (cipherText == null || cipherText.isBlank()) {
|
||||||
|
return cipherText;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// Databus 会在凭证开启加密时返回密文,这里做一次解密展示真实响应。
|
||||||
|
return CryptoSignatureUtils.decrypt(cipherText, APP_SECRET, ENCRYPTION_TYPE);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return "<unable to decrypt> " + ex.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解决控制台打印 乱码问题
|
||||||
|
private static String normalizePotentialMojibake(String value) {
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
long suspectCount = value.chars().filter(ch -> ch >= 0x80 && ch <= 0xFF).count();
|
||||||
|
long highCount = value.chars().filter(ch -> ch > 0xFF).count();
|
||||||
|
if (suspectCount > 0 && highCount == 0) {
|
||||||
|
try {
|
||||||
|
byte[] decoded = value.getBytes(StandardCharsets.ISO_8859_1);
|
||||||
|
String converted = new String(decoded, StandardCharsets.UTF_8);
|
||||||
|
if (converted.chars().anyMatch(ch -> ch > 0xFF)) {
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出流编码与当前控制台保持一致,避免中文字符再次出现编码差异。
|
||||||
|
*/
|
||||||
|
private static PrintStream buildConsolePrintStream() {
|
||||||
|
try {
|
||||||
|
String consoleEncoding = System.getProperty("sun.stdout.encoding");
|
||||||
|
if (consoleEncoding != null && !consoleEncoding.isBlank()) {
|
||||||
|
return new PrintStream(System.out, true, Charset.forName(consoleEncoding));
|
||||||
|
}
|
||||||
|
return new PrintStream(System.out, true, Charset.defaultCharset());
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return System.out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user