diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/FavorCouponsGetResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/FavorCouponsGetResult.java index f8f342de1c..6f1824f646 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/FavorCouponsGetResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/FavorCouponsGetResult.java @@ -1,5 +1,6 @@ package com.github.binarywang.wxpay.bean.marketing; +import com.github.binarywang.wxpay.bean.result.BaseWxPayV3Result; import com.google.gson.annotations.SerializedName; import lombok.Data; import lombok.NoArgsConstructor; @@ -13,7 +14,7 @@ */ @NoArgsConstructor @Data -public class FavorCouponsGetResult implements Serializable { +public class FavorCouponsGetResult extends BaseWxPayV3Result { private static final long serialVersionUID = 1L; diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/FavorStocksCreateResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/FavorStocksCreateResult.java index 74ac6fd205..381056b5a9 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/FavorStocksCreateResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/FavorStocksCreateResult.java @@ -1,5 +1,6 @@ package com.github.binarywang.wxpay.bean.marketing; +import com.github.binarywang.wxpay.bean.result.BaseWxPayV3Result; import com.google.gson.annotations.SerializedName; import lombok.Data; import lombok.NoArgsConstructor; @@ -13,7 +14,7 @@ */ @NoArgsConstructor @Data -public class FavorStocksCreateResult implements Serializable { +public class FavorStocksCreateResult extends BaseWxPayV3Result { private static final long serialVersionUID = 1L; diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/FavorStocksGetResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/FavorStocksGetResult.java index 294f273def..591afa7e51 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/FavorStocksGetResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/FavorStocksGetResult.java @@ -1,5 +1,6 @@ package com.github.binarywang.wxpay.bean.marketing; +import com.github.binarywang.wxpay.bean.result.BaseWxPayV3Result; import com.google.gson.annotations.SerializedName; import lombok.Data; import lombok.NoArgsConstructor; @@ -14,7 +15,7 @@ */ @NoArgsConstructor @Data -public class FavorStocksGetResult implements Serializable { +public class FavorStocksGetResult extends BaseWxPayV3Result { private static final long serialVersionUID = 1L; diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayV3Result.java new file mode 100644 index 0000000000..88724b939e --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayV3Result.java @@ -0,0 +1,42 @@ +package com.github.binarywang.wxpay.bean.result; + +import lombok.Data; + +import java.io.Serializable; + +/** + *
+ * 微信支付v3结果共用属性类. + * 用于保存原始JSON响应内容,便于访问API返回的新增字段. + *+ * + * @author Binary Wang + */ +@Data +public abstract class BaseWxPayV3Result implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 原始JSON字符串. + * 保存微信支付v3 API返回的原始JSON内容,便于访问未在Result类中定义的字段. + */ + private String rawJsonString; + + /** + * 获取原始JSON响应内容. + * + * @return 原始JSON字符串 + */ + public String getRawJsonString() { + return rawJsonString; + } + + /** + * 设置原始JSON响应内容. + * + * @param rawJsonString 原始JSON字符串 + */ + public void setRawJsonString(String rawJsonString) { + this.rawJsonString = rawJsonString; + } +} \ No newline at end of file diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MarketingFavorServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MarketingFavorServiceImpl.java index a10bbbb085..0f84d5f126 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MarketingFavorServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/MarketingFavorServiceImpl.java @@ -34,7 +34,9 @@ public FavorStocksCreateResult createFavorStocksV3(FavorStocksCreateRequest requ String url = String.format("%s/v3/marketing/favor/coupon-stocks", this.payService.getPayBaseUrl()); RsaCryptoUtil.encryptFields(request, this.payService.getConfig().getVerifier().getValidCertificate()); String result = this.payService.postV3WithWechatpaySerial(url, GSON.toJson(request)); - return GSON.fromJson(result, FavorStocksCreateResult.class); + FavorStocksCreateResult favorStocksCreateResult = GSON.fromJson(result, FavorStocksCreateResult.class); + favorStocksCreateResult.setRawJsonString(result); + return favorStocksCreateResult; } @Override @@ -75,7 +77,9 @@ public FavorStocksGetResult getFavorStocksV3(String stockId, String stockCreator String url = String.format("%s/v3/marketing/favor/stocks/%s", this.payService.getPayBaseUrl(), stockId); String query = String.format("?stock_creator_mchid=%s", stockCreatorMchid); String result = this.payService.getV3(url + query); - return GSON.fromJson(result, FavorStocksGetResult.class); + FavorStocksGetResult favorStocksGetResult = GSON.fromJson(result, FavorStocksGetResult.class); + favorStocksGetResult.setRawJsonString(result); + return favorStocksGetResult; } @Override @@ -83,7 +87,9 @@ public FavorCouponsGetResult getFavorCouponsV3(String couponId, String appid, St String url = String.format("%s/v3/marketing/favor/users/%s/coupons/%s", this.payService.getPayBaseUrl(), openid, couponId); String query = String.format("?appid=%s", appid); String result = this.payService.getV3(url + query); - return GSON.fromJson(result, FavorCouponsGetResult.class); + FavorCouponsGetResult favorCouponsGetResult = GSON.fromJson(result, FavorCouponsGetResult.class); + favorCouponsGetResult.setRawJsonString(result); + return favorCouponsGetResult; } @Override diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/marketing/FavorStocksGetResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/marketing/FavorStocksGetResultTest.java new file mode 100644 index 0000000000..ed39413833 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/marketing/FavorStocksGetResultTest.java @@ -0,0 +1,164 @@ +package com.github.binarywang.wxpay.bean.marketing; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayV3Result; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +/** + * 测试FavorStocksGetResult的原始JSON保存功能 + * + * @author Binary Wang + */ +public class FavorStocksGetResultTest { + + private static final Gson GSON = new GsonBuilder().create(); + + @Test + public void testRawJsonPreservation() { + // 模拟微信API返回的JSON(包含未在Result类中定义的字段) + String mockJson = "{\n" + + " \"stock_id\": \"9836588\",\n" + + " \"stock_creator_mchid\": \"1230000109\",\n" + + " \"stock_name\": \"微信支付代金券\",\n" + + " \"status\": \"running\",\n" + + " \"create_time\": \"2021-01-01T00:00:00.000+08:00\",\n" + + " \"description\": \"微信支付营销\",\n" + + " \"card_id\": \"pFS7Fjg9kqcMOBtl3bFn\",\n" + + " \"extra_field\": \"这是一个新增字段\"\n" + + "}"; + + // 模拟服务调用:反序列化JSON + FavorStocksGetResult result = GSON.fromJson(mockJson, FavorStocksGetResult.class); + + // 模拟服务调用:设置原始JSON + result.setRawJsonString(mockJson); + + // 验证基本字段正常解析 + assertEquals(result.getStockId(), "9836588"); + assertEquals(result.getStockCreatorMchId(), "1230000109"); + assertEquals(result.getStockName(), "微信支付代金券"); + assertEquals(result.getStatus(), "running"); + + // 验证原始JSON被保存 + assertNotNull(result.getRawJsonString()); + assertEquals(result.getRawJsonString(), mockJson); + + // 验证可以从原始JSON中获取未定义的字段 + assertTrue(result.getRawJsonString().contains("\"card_id\": \"pFS7Fjg9kqcMOBtl3bFn\"")); + assertTrue(result.getRawJsonString().contains("\"extra_field\": \"这是一个新增字段\"")); + } + + @Test + public void testBaseV3ResultInheritance() { + FavorStocksGetResult result = new FavorStocksGetResult(); + + // 验证继承关系 + assertTrue(result instanceof BaseWxPayV3Result); + + // 验证基类方法可用 + result.setRawJsonString("test json"); + assertEquals(result.getRawJsonString(), "test json"); + } + + @Test + public void testNullRawJson() { + FavorStocksGetResult result = new FavorStocksGetResult(); + + // 验证初始状态下rawJsonString为null + assertNull(result.getRawJsonString()); + + // 验证设置null不会出错 + result.setRawJsonString(null); + assertNull(result.getRawJsonString()); + } + + @Test + public void testRealWorldUsagePattern() { + // 实际使用场景的示例 + String realApiResponse = "{\n" + + " \"stock_id\": \"9836588\",\n" + + " \"stock_creator_mchid\": \"1230000109\",\n" + + " \"stock_name\": \"微信支付代金券\",\n" + + " \"status\": \"running\",\n" + + " \"create_time\": \"2021-01-01T00:00:00.000+08:00\",\n" + + " \"description\": \"微信支付营销\",\n" + + " \"card_id\": \"pFS7Fjg9kqcMOBtl3bFn\",\n" + + " \"future_field_1\": \"未来可能新增的字段1\",\n" + + " \"future_field_2\": \"未来可能新增的字段2\"\n" + + "}"; + + FavorStocksGetResult result = GSON.fromJson(realApiResponse, FavorStocksGetResult.class); + result.setRawJsonString(realApiResponse); + + // 1. 正常使用已定义的字段 + assertEquals(result.getStockId(), "9836588"); + assertEquals(result.getStockName(), "微信支付代金券"); + + // 2. 安全地获取可能存在的新字段 + String cardId = getStringFieldSafely(result, "card_id"); + assertEquals(cardId, "pFS7Fjg9kqcMOBtl3bFn"); + + // 3. 获取未来可能新增的字段 + String futureField1 = getStringFieldSafely(result, "future_field_1"); + assertEquals(futureField1, "未来可能新增的字段1"); + + String nonExistentField = getStringFieldSafely(result, "non_existent"); + assertNull(nonExistentField); + } + + @Test + public void testCardIdExtractionExample() { + // 测试具体的card_id字段提取(这是issue中提到的用例) + String apiResponseWithCardId = "{\n" + + " \"stock_id\": \"9836588\",\n" + + " \"stock_creator_mchid\": \"1230000109\",\n" + + " \"stock_name\": \"微信支付代金券\",\n" + + " \"status\": \"running\",\n" + + " \"card_id\": \"pFS7Fjg9kqcMOBtl3bFn\"\n" + + "}"; + + FavorStocksGetResult result = GSON.fromJson(apiResponseWithCardId, FavorStocksGetResult.class); + result.setRawJsonString(apiResponseWithCardId); + + // 验证可以获取card_id字段 + JsonElement jsonElement = JsonParser.parseString(result.getRawJsonString()); + assertTrue(jsonElement.getAsJsonObject().has("card_id")); + String cardId = jsonElement.getAsJsonObject().get("card_id").getAsString(); + assertEquals(cardId, "pFS7Fjg9kqcMOBtl3bFn"); + + // 展示实际用法 + String extractedCardId = extractCardId(result); + assertEquals(extractedCardId, "pFS7Fjg9kqcMOBtl3bFn"); + } + + /** + * 提取card_id的示例方法 + */ + private String extractCardId(FavorStocksGetResult result) { + return getStringFieldSafely(result, "card_id"); + } + + /** + * 安全地从结果中获取字符串字段的工具方法 + */ + private String getStringFieldSafely(BaseWxPayV3Result result, String fieldName) { + String rawJson = result.getRawJsonString(); + if (rawJson == null) return null; + + try { + JsonElement jsonElement = JsonParser.parseString(rawJson); + if (jsonElement.getAsJsonObject().has(fieldName)) { + JsonElement fieldElement = jsonElement.getAsJsonObject().get(fieldName); + return fieldElement.isJsonNull() ? null : fieldElement.getAsString(); + } + } catch (Exception e) { + // 解析失败时返回null + } + return null; + } +} \ No newline at end of file