1
0
mirror of synced 2026-10-13 18:58:41 +08:00

改进错误处理:避免异常被静默吞掉

- FileUtils.imageToBase64ByStream 读取失败时抛出 WxRuntimeException,不再 printStackTrace 后返回 null
- AesUtils.HMACSHA256 签名计算失败时抛出 WxRuntimeException,不再返回 null 导致签名为空
- RequestUtils.readData 读取回调请求体失败时抛出异常,不再静默返回不完整内容
- PartnerTransferServiceImpl.spDayEndBalance 传播查询异常,不再返回 null
- WxMpMemberCardServiceImpl.getActivatePluginParam 解析失败时抛出 WxErrorException
- DefaultApacheHttpClientBuilder 空闲连接监控线程被中断时恢复中断标志
- 支付回调转换器、临时二维码文件删除、payBaseUrl 解析失败改为记录日志

新增 FileUtilsTest、AesUtilsTest 回归测试

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Binary Wang
2026-07-31 04:01:57 +00:00
parent 6014c833bc
commit 0c7b6713a8
13 changed files with 78 additions and 39 deletions

View File

@@ -1,5 +1,6 @@
package me.chanjar.weixin.common.util.fs;
import me.chanjar.weixin.common.error.WxRuntimeException;
import org.apache.commons.io.IOUtils;
import java.io.File;
@@ -55,24 +56,16 @@ public class FileUtils {
*
* @param in 文件流
* @return base64编码
* @throws WxRuntimeException 读取文件流失败时抛出
*/
public static String imageToBase64ByStream(InputStream in) {
// 读取图片字节数组
try {
try (InputStream input = in) {
// 返回Base64编码过的字节数组字符串
return Base64.getEncoder().encodeToString(IOUtils.toByteArray(in));
return Base64.getEncoder().encodeToString(IOUtils.toByteArray(input));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
throw new WxRuntimeException(e);
}
return null;
}
}

View File

@@ -318,7 +318,9 @@ public class DefaultApacheHttpClientBuilder implements ApacheHttpClientBuilder {
TimeUnit.MILLISECONDS);
}
}
} catch (InterruptedException ignore) {
} catch (InterruptedException e) {
log.debug("空闲连接监控线程被中断,退出监控", e);
Thread.currentThread().interrupt();
}
}

View File

@@ -1,5 +1,6 @@
package me.chanjar.weixin.common.util.fs;
import me.chanjar.weixin.common.error.WxRuntimeException;
import org.apache.commons.io.IOUtils;
import org.testng.annotations.Test;
@@ -13,6 +14,7 @@ import java.util.Base64;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class FileUtilsTest {
@@ -59,4 +61,17 @@ public class FileUtilsTest {
String result = FileUtils.imageToBase64ByStream(chunked);
assertThat(result).isEqualTo(Base64.getEncoder().encodeToString(original));
}
@Test
public void testImageToBase64ByStreamThrowsOnIoError() {
InputStream failing = new InputStream() {
@Override
public int read() throws IOException {
throw new IOException("read failed");
}
};
assertThatThrownBy(() -> FileUtils.imageToBase64ByStream(failing))
.isInstanceOf(WxRuntimeException.class)
.hasCauseInstanceOf(IOException.class);
}
}

View File

@@ -7,6 +7,7 @@ import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor;
@@ -29,6 +30,7 @@ import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.Code.*;
* @author <a href="https://github.com/charmingoh">Charming</a>
* @since 2018-04-26 20:00
*/
@Slf4j
@RequiredArgsConstructor
public class WxMaCodeServiceImpl implements WxMaCodeService {
private final WxMaService service;
@@ -60,7 +62,8 @@ public class WxMaCodeServiceImpl implements WxMaCodeService {
try {
// 及时删除二维码文件,避免积压过多缓存文件
Files.delete(qrCodeFilePath);
} catch (Exception ignored) {
} catch (Exception e) {
log.warn("删除临时二维码文件失败,path={}", qrCodeFilePath, e);
}
}
}

View File

@@ -16,6 +16,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import me.chanjar.weixin.mp.api.WxMpMemberCardService;
@@ -253,9 +254,8 @@ public class WxMpMemberCardServiceImpl implements WxMpMemberCardService {
activatePluginParam.setBiz(resultMap.get("biz") + "==");
return activatePluginParam;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new WxErrorException(WxError.builder().errorMsg("解析激活插件参数失败:" + e.getMessage()).build(), e);
}
return null;
}

View File

@@ -4,6 +4,7 @@ import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.v3.auth.*;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.net.URI;
@@ -17,6 +18,7 @@ import java.security.PublicKey;
*
* @author holy
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
class VerifierBuilder {
/**
@@ -127,8 +129,8 @@ class VerifierBuilder {
if (StringUtils.isNotBlank(rawPath) && !"/".equals(rawPath)) {
signUriStripPrefix = rawPath;
}
} catch (URISyntaxException ignored) {
// ignore
} catch (URISyntaxException e) {
log.warn("payBaseUrl不是合法的URI,将不使用签名前缀剪裁,payBaseUrl={}", payBaseUrl, e);
}
}
return new AutoUpdateCertificatesVerifier(

View File

@@ -12,6 +12,7 @@ import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.beans.PropertyDescriptor;
@@ -26,6 +27,7 @@ import java.util.Map;
*
* @author aimilin
*/
@Slf4j
public class WxPayOrderNotifyResultConverter extends AbstractReflectionConverter {
/**
@@ -116,7 +118,8 @@ public class WxPayOrderNotifyResultConverter extends AbstractReflectionConverter
PropertyDescriptor pd = new PropertyDescriptor((String) field.getName(), obj.getClass());
pd.getWriteMethod().invoke(obj, val);
}
} catch (Exception ignored) {
} catch (Exception e) {
log.warn("支付回调报文字段赋值失败,field={}", field.getName(), e);
}
}

View File

@@ -195,7 +195,7 @@ public interface PartnerTransferService {
* @param accountType 服务商账户类型
* @param date 查询日期 2020-09-11
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
* @throws me.chanjar.weixin.common.error.WxRuntimeException 查询失败时抛出,具体原因参见 cause
*/
FundBalanceResult spDayEndBalance(SpAccountTypeEnum accountType, String date);
}

View File

@@ -7,6 +7,7 @@ import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.PartnerTransferService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil;
import me.chanjar.weixin.common.error.WxRuntimeException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import jodd.util.StringUtil;
@@ -315,16 +316,15 @@ public class PartnerTransferServiceImpl implements PartnerTransferService {
* @param accountType 服务商账户类型
* @param date 查询日期 2020-09-11
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
* @throws WxRuntimeException 查询失败时抛出,具体原因参见 cause
*/
@Override
public FundBalanceResult spDayEndBalance(SpAccountTypeEnum accountType, String date) {
try {
return this.payService.getEcommerceService().spDayEndBalance(accountType, date);
} catch (Exception e) {
e.printStackTrace();
} catch (WxPayException e) {
throw new WxRuntimeException(e);
}
return null;
}

View File

@@ -1,5 +1,7 @@
package com.github.binarywang.wxpay.util;
import me.chanjar.weixin.common.error.WxRuntimeException;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
@@ -19,12 +21,11 @@ public class RequestUtils {
*
* @param request HTTP请求对象
* @return 字符串
* @throws WxRuntimeException 读取请求体失败时抛出
*/
public static String readData(HttpServletRequest request) {
BufferedReader br = null;
StringBuilder result = new StringBuilder();
try {
br = request.getReader();
try (BufferedReader br = request.getReader()) {
for (String line; (line = br.readLine()) != null; ) {
if (result.length() > 0) {
result.append("\n");
@@ -32,15 +33,7 @@ public class RequestUtils {
result.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
throw new WxRuntimeException("读取请求体失败", e);
}
return result.toString();

View File

@@ -3,8 +3,10 @@ package com.github.binarywang.wxpay.v3.auth;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.Base64;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxRuntimeException;
@Slf4j
public class PublicCertificateVerifier implements Verifier{
private final PublicKey publicKey;
@@ -32,6 +34,7 @@ public class PublicCertificateVerifier implements Verifier{
}
} catch (Exception e) {
// 证书验证失败,继续尝试公钥验证
log.debug("平台证书验签失败,降级为公钥验签,serialNumber={}", serialNumber, e);
}
}
// 使用公钥验证(兜底方案,适用于公钥转账等场景)

View File

@@ -1,5 +1,6 @@
package com.github.binarywang.wxpay.v3.util;
import me.chanjar.weixin.common.error.WxRuntimeException;
import org.apache.commons.lang3.StringUtils;
import javax.crypto.Cipher;
@@ -123,9 +124,8 @@ public class AesUtils {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
} catch (Exception e) {
e.printStackTrace();
return null;
} catch (GeneralSecurityException | IllegalArgumentException e) {
throw new WxRuntimeException("HMAC-SHA256签名计算失败", e);
}
}

View File

@@ -0,0 +1,25 @@
package com.github.binarywang.wxpay.v3.util;
import me.chanjar.weixin.common.error.WxRuntimeException;
import org.testng.annotations.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* 测试 {@link AesUtils}.
*/
public class AesUtilsTest {
@Test
public void testHmacSha256() {
assertThat(AesUtils.HMACSHA256("data", "key")).isNotEmpty();
}
@Test
public void testHmacSha256ThrowsOnInvalidKey() {
// 空密钥无法构造 SecretKeySpec,此前该场景会被静默吞掉并返回 null
assertThatThrownBy(() -> AesUtils.HMACSHA256("data", ""))
.isInstanceOf(WxRuntimeException.class);
}
}