mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 10:18:42 +08:00 
			
		
		
		
	Merge remote-tracking branch 'origin/feature/mall_product' into member_dev
# Conflicts: # yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelServiceImpl.java
This commit is contained in:
		| @@ -0,0 +1,180 @@ | |||||||
|  | package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.date.LocalDateTimeUtil; | ||||||
|  | import cn.hutool.core.util.ReflectUtil; | ||||||
|  | import cn.iocoder.yudao.framework.common.exception.ServiceException; | ||||||
|  | import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; | ||||||
|  | import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; | ||||||
|  | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; | ||||||
|  | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; | ||||||
|  | import cn.iocoder.yudao.framework.pay.core.client.exception.PayException; | ||||||
|  | import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; | ||||||
|  | import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||||||
|  | import com.alipay.api.AlipayApiException; | ||||||
|  | import com.alipay.api.DefaultAlipayClient; | ||||||
|  | import com.alipay.api.DefaultSigner; | ||||||
|  | import com.alipay.api.domain.AlipayTradeRefundModel; | ||||||
|  | import com.alipay.api.request.AlipayTradeRefundRequest; | ||||||
|  | import com.alipay.api.response.AlipayTradeRefundResponse; | ||||||
|  | import org.junit.jupiter.api.DisplayName; | ||||||
|  | import org.junit.jupiter.api.Test; | ||||||
|  | import org.mockito.ArgumentMatcher; | ||||||
|  | import org.mockito.Mock; | ||||||
|  |  | ||||||
|  | import javax.validation.ConstraintViolationException; | ||||||
|  | import java.util.Date; | ||||||
|  |  | ||||||
|  | import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_PUBLIC_KEY; | ||||||
|  | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||||
|  | import static org.junit.jupiter.api.Assertions.*; | ||||||
|  | import static org.mockito.ArgumentMatchers.argThat; | ||||||
|  | import static org.mockito.Mockito.when; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @author jason | ||||||
|  |  */ | ||||||
|  | public abstract class AbstractAlipayClientTest extends BaseMockitoUnitTest { | ||||||
|  |  | ||||||
|  |     private final String privateKey = randomString(); | ||||||
|  |  | ||||||
|  |     protected AlipayPayClientConfig config = randomPojo(AlipayPayClientConfig.class, t -> { | ||||||
|  |         t.setServerUrl(randomURL()); | ||||||
|  |         t.setPrivateKey(privateKey); | ||||||
|  |         t.setMode(MODE_PUBLIC_KEY); | ||||||
|  |         t.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT); | ||||||
|  |         t.setAppCertContent(""); | ||||||
|  |         t.setAlipayPublicCertContent(""); | ||||||
|  |         t.setRootCertContent(""); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     @Mock | ||||||
|  |     protected DefaultAlipayClient defaultAlipayClient; | ||||||
|  |  | ||||||
|  |     private AbstractAlipayPayClient client; | ||||||
|  |  | ||||||
|  |     public void setClient(AbstractAlipayPayClient client) { | ||||||
|  |         this.client = client; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @DisplayName("支付宝 Client 初始化") | ||||||
|  |     public void test_do_init() { | ||||||
|  |         client.doInit(); | ||||||
|  |         DefaultAlipayClient realClient = (DefaultAlipayClient) ReflectUtil.getFieldValue(client, "client"); | ||||||
|  |         assertNotSame(defaultAlipayClient, realClient); | ||||||
|  |         assertInstanceOf(DefaultSigner.class, realClient.getSigner()); | ||||||
|  |         assertEquals(privateKey, ((DefaultSigner) realClient.getSigner()).getPrivateKey()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @DisplayName("支付宝 Client 统一退款成功") | ||||||
|  |     public void test_unified_refund_success() throws AlipayApiException { | ||||||
|  |         // 准备返回对象 | ||||||
|  |         String notifyUrl = randomURL(); | ||||||
|  |         Date refundTime = randomDate(); | ||||||
|  |         String outRefundNo = randomString(); | ||||||
|  |         String outTradeNo = randomString(); | ||||||
|  |         Integer refundAmount = randomInteger(); | ||||||
|  |         AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> { | ||||||
|  |             o.setSubCode(""); | ||||||
|  |             o.setGmtRefundPay(refundTime); | ||||||
|  |         }); | ||||||
|  |         // mock | ||||||
|  |         when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> { | ||||||
|  |             assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel()); | ||||||
|  |             AlipayTradeRefundModel bizModel = (AlipayTradeRefundModel) request.getBizModel(); | ||||||
|  |             assertEquals(outRefundNo, bizModel.getOutRequestNo()); | ||||||
|  |             assertEquals(outTradeNo, bizModel.getOutTradeNo()); | ||||||
|  |             assertEquals(String.valueOf(refundAmount / 100.0), bizModel.getRefundAmount()); | ||||||
|  |             return true; | ||||||
|  |         }))).thenReturn(response); | ||||||
|  |         // 准备请求参数 | ||||||
|  |         PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> { | ||||||
|  |             o.setOutRefundNo(outRefundNo); | ||||||
|  |             o.setOutTradeNo(outTradeNo); | ||||||
|  |             o.setNotifyUrl(notifyUrl); | ||||||
|  |             o.setRefundPrice(refundAmount); | ||||||
|  |         }); | ||||||
|  |         PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO); | ||||||
|  |         // 断言 | ||||||
|  |         assertEquals(PayRefundStatusRespEnum.SUCCESS.getStatus(), resp.getStatus()); | ||||||
|  |         assertNull(resp.getChannelRefundNo()); | ||||||
|  |         assertEquals(LocalDateTimeUtil.of(refundTime), resp.getSuccessTime()); | ||||||
|  |         assertEquals(outRefundNo, resp.getOutRefundNo()); | ||||||
|  |         assertSame(response, resp.getRawData()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @DisplayName("支付宝 Client 统一退款,渠道返回失败") | ||||||
|  |     public void test_unified_refund_channel_failed() throws AlipayApiException { | ||||||
|  |         // 准备返回对象 | ||||||
|  |         String notifyUrl = randomURL(); | ||||||
|  |         String subCode = randomString(); | ||||||
|  |         String subMsg = randomString(); | ||||||
|  |         AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> { | ||||||
|  |             o.setSubCode(subCode); | ||||||
|  |             o.setSubMsg(subMsg); | ||||||
|  |         }); | ||||||
|  |         // mock | ||||||
|  |         when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> { | ||||||
|  |             assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel()); | ||||||
|  |             return true; | ||||||
|  |         }))).thenReturn(response); | ||||||
|  |         // 准备请求参数 | ||||||
|  |         String outRefundNo = randomString(); | ||||||
|  |         String outTradeNo = randomString(); | ||||||
|  |         PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> { | ||||||
|  |             o.setOutRefundNo(outRefundNo); | ||||||
|  |             o.setOutTradeNo(outTradeNo); | ||||||
|  |             o.setNotifyUrl(notifyUrl); | ||||||
|  |         }); | ||||||
|  |         PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO); | ||||||
|  |         // 断言 | ||||||
|  |         assertEquals(PayRefundStatusRespEnum.FAILURE.getStatus(), resp.getStatus()); | ||||||
|  |         assertNull(resp.getChannelRefundNo()); | ||||||
|  |         assertEquals(subCode, resp.getChannelErrorCode()); | ||||||
|  |         assertEquals(subMsg, resp.getChannelErrorMsg()); | ||||||
|  |         assertNull(resp.getSuccessTime()); | ||||||
|  |         assertEquals(outRefundNo, resp.getOutRefundNo()); | ||||||
|  |         assertSame(response, resp.getRawData()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @DisplayName("支付宝 Client 统一退款,参数校验不通过") | ||||||
|  |     public void test_unified_refund_param_validate() { | ||||||
|  |         // 准备请求参数 | ||||||
|  |         String notifyUrl = randomURL(); | ||||||
|  |         PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> { | ||||||
|  |             o.setOutTradeNo(""); | ||||||
|  |             o.setNotifyUrl(notifyUrl); | ||||||
|  |         }); | ||||||
|  |         // 断言 | ||||||
|  |         assertThrows(ConstraintViolationException.class, () -> client.unifiedRefund(refundReqDTO)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @DisplayName("支付宝 Client 统一退款,抛出业务异常") | ||||||
|  |     public void test_unified_refund_throw_service_exception() throws AlipayApiException { | ||||||
|  |         // mock | ||||||
|  |         when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true))) | ||||||
|  |                 .thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR)); | ||||||
|  |         // 准备请求参数 | ||||||
|  |         String notifyUrl = randomURL(); | ||||||
|  |         PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> o.setNotifyUrl(notifyUrl)); | ||||||
|  |         // 断言 | ||||||
|  |         assertThrows(ServiceException.class, () -> client.unifiedRefund(refundReqDTO)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @DisplayName("支付宝 Client 统一退款,抛出系统异常") | ||||||
|  |     public void test_unified_refund_throw_pay_exception() throws AlipayApiException { | ||||||
|  |         // mock | ||||||
|  |         when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true))) | ||||||
|  |                 .thenThrow(new RuntimeException("系统异常")); | ||||||
|  |         // 准备请求参数 | ||||||
|  |         String notifyUrl = randomURL(); | ||||||
|  |         PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> o.setNotifyUrl(notifyUrl)); | ||||||
|  |         // 断言 | ||||||
|  |         assertThrows(PayException.class, () -> client.unifiedRefund(refundReqDTO)); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,68 +1,44 @@ | |||||||
| package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; | package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; | ||||||
|  |  | ||||||
| import cn.hutool.core.date.LocalDateTimeUtil; |  | ||||||
| import cn.hutool.core.util.RandomUtil; | import cn.hutool.core.util.RandomUtil; | ||||||
| import cn.hutool.core.util.ReflectUtil; |  | ||||||
| import cn.iocoder.yudao.framework.common.exception.ServiceException; | import cn.iocoder.yudao.framework.common.exception.ServiceException; | ||||||
| import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; | import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; | ||||||
| import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; | import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; | ||||||
| import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; | ||||||
| import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; | ||||||
| import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; |  | ||||||
| import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; |  | ||||||
| import cn.iocoder.yudao.framework.pay.core.client.exception.PayException; | import cn.iocoder.yudao.framework.pay.core.client.exception.PayException; | ||||||
| import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; | import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; | ||||||
| import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; |  | ||||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; |  | ||||||
| import com.alipay.api.AlipayApiException; | import com.alipay.api.AlipayApiException; | ||||||
| import com.alipay.api.DefaultAlipayClient; |  | ||||||
| import com.alipay.api.domain.AlipayTradeRefundModel; |  | ||||||
| import com.alipay.api.request.AlipayTradePrecreateRequest; | import com.alipay.api.request.AlipayTradePrecreateRequest; | ||||||
| import com.alipay.api.request.AlipayTradeRefundRequest; |  | ||||||
| import com.alipay.api.response.AlipayTradePrecreateResponse; | import com.alipay.api.response.AlipayTradePrecreateResponse; | ||||||
| import com.alipay.api.response.AlipayTradeRefundResponse; | import org.junit.jupiter.api.BeforeEach; | ||||||
| import org.junit.jupiter.api.DisplayName; | import org.junit.jupiter.api.DisplayName; | ||||||
| import org.junit.jupiter.api.Test; | import org.junit.jupiter.api.Test; | ||||||
| import org.mockito.ArgumentMatcher; | import org.mockito.ArgumentMatcher; | ||||||
| import org.mockito.InjectMocks; | import org.mockito.InjectMocks; | ||||||
| import org.mockito.Mock; |  | ||||||
|  |  | ||||||
| import javax.validation.ConstraintViolationException; | import javax.validation.ConstraintViolationException; | ||||||
| import java.util.Date; |  | ||||||
|  |  | ||||||
| import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_PUBLIC_KEY; |  | ||||||
| import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.CLOSED; | import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.CLOSED; | ||||||
| import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.WAITING; | import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.WAITING; | ||||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||||||
| import static org.junit.jupiter.api.Assertions.*; | import static org.junit.jupiter.api.Assertions.*; | ||||||
| import static org.mockito.ArgumentMatchers.argThat; | import static org.mockito.ArgumentMatchers.argThat; | ||||||
| import static org.mockito.Mockito.when; | import static org.mockito.Mockito.when; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * {@link AlipayQrPayClient} 单元测试 |  * {@link AlipayQrPayClient} 单元测试 | ||||||
|  * |  * | ||||||
|  * @author jason |  * @author jason | ||||||
|  */ |  */ | ||||||
| public class AlipayQrPayClientTest extends BaseMockitoUnitTest { | public class AlipayQrPayClientTest extends AbstractAlipayClientTest { | ||||||
|  |  | ||||||
|     private final AlipayPayClientConfig config = randomPojo(AlipayPayClientConfig.class, t -> { |  | ||||||
|         t.setServerUrl(randomURL()); |  | ||||||
|         t.setMode(MODE_PUBLIC_KEY); |  | ||||||
|         t.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT); |  | ||||||
|         t.setAppCertContent(""); |  | ||||||
|         t.setAlipayPublicCertContent(""); |  | ||||||
|         t.setRootCertContent(""); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     @InjectMocks |     @InjectMocks | ||||||
|     AlipayQrPayClient client = new AlipayQrPayClient(randomLongId(), config); |     private AlipayQrPayClient client = new AlipayQrPayClient(randomLongId(), config); | ||||||
|  |  | ||||||
|     @Mock |     @BeforeEach | ||||||
|     private DefaultAlipayClient defaultAlipayClient; |     public void setUp() { | ||||||
|  |         setClient(client); | ||||||
|     @Test |  | ||||||
|     public void test_do_init() { |  | ||||||
|         client.doInit(); |  | ||||||
|         assertNotSame(defaultAlipayClient, ReflectUtil.getFieldValue(client, "defaultAlipayClient")); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -176,119 +152,4 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest { | |||||||
|             o.setBody(RandomUtil.randomString(32)); |             o.setBody(RandomUtil.randomString(32)); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |  | ||||||
|     @DisplayName("支付包扫描退款成功") |  | ||||||
|     public void test_unified_refund_success() throws AlipayApiException { |  | ||||||
|         // 准备返回对象 |  | ||||||
|         String notifyUrl = randomURL(); |  | ||||||
|         Date refundTime = randomDate(); |  | ||||||
|         String outRefundNo = randomString(); |  | ||||||
|         String outTradeNo = randomString(); |  | ||||||
|         Integer refundAmount = randomInteger(); |  | ||||||
|         AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> { |  | ||||||
|             o.setSubCode(""); |  | ||||||
|             o.setGmtRefundPay(refundTime); |  | ||||||
|         }); |  | ||||||
|         // mock |  | ||||||
|         when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> { |  | ||||||
|             assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel()); |  | ||||||
|             AlipayTradeRefundModel bizModel = (AlipayTradeRefundModel) request.getBizModel(); |  | ||||||
|             assertEquals(outRefundNo, bizModel.getOutRequestNo()); |  | ||||||
|             assertEquals(outTradeNo, bizModel.getOutTradeNo()); |  | ||||||
|             assertEquals(String.valueOf(refundAmount / 100.0), bizModel.getRefundAmount()); |  | ||||||
|             return true; |  | ||||||
|         }))).thenReturn(response); |  | ||||||
|         // 准备请求参数 |  | ||||||
|         PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> { |  | ||||||
|             o.setOutRefundNo(outRefundNo); |  | ||||||
|             o.setOutTradeNo(outTradeNo); |  | ||||||
|             o.setNotifyUrl(notifyUrl); |  | ||||||
|             o.setRefundPrice(refundAmount); |  | ||||||
|         }); |  | ||||||
|         PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO); |  | ||||||
|         // 断言 |  | ||||||
|         assertEquals(PayRefundStatusRespEnum.SUCCESS.getStatus(), resp.getStatus()); |  | ||||||
|         assertNull(resp.getChannelRefundNo()); |  | ||||||
|         assertEquals(LocalDateTimeUtil.of(refundTime), resp.getSuccessTime()); |  | ||||||
|         assertEquals(outRefundNo, resp.getOutRefundNo()); |  | ||||||
|         assertSame(response, resp.getRawData()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Test |  | ||||||
|     @DisplayName("支付包扫描退款,渠道返回失败") |  | ||||||
|     public void test_unified_refund_channel_failed() throws AlipayApiException { |  | ||||||
|         // 准备返回对象 |  | ||||||
|         String notifyUrl = randomURL(); |  | ||||||
|         String subCode = randomString(); |  | ||||||
|         String subMsg = randomString(); |  | ||||||
|         AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> { |  | ||||||
|             o.setSubCode(subCode); |  | ||||||
|             o.setSubMsg(subMsg); |  | ||||||
|         }); |  | ||||||
|         // mock |  | ||||||
|         when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> { |  | ||||||
|             assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel()); |  | ||||||
|             return true; |  | ||||||
|         }))).thenReturn(response); |  | ||||||
|         // 准备请求参数 |  | ||||||
|         String outRefundNo = randomString(); |  | ||||||
|         String outTradeNo = randomString(); |  | ||||||
|         PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> { |  | ||||||
|             o.setOutRefundNo(outRefundNo); |  | ||||||
|             o.setOutTradeNo(outTradeNo); |  | ||||||
|             o.setNotifyUrl(notifyUrl); |  | ||||||
|         }); |  | ||||||
|         PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO); |  | ||||||
|         // 断言 |  | ||||||
|         assertEquals(PayRefundStatusRespEnum.FAILURE.getStatus(), resp.getStatus()); |  | ||||||
|         assertNull(resp.getChannelRefundNo()); |  | ||||||
|         assertEquals(subCode, resp.getChannelErrorCode()); |  | ||||||
|         assertEquals(subMsg, resp.getChannelErrorMsg()); |  | ||||||
|         assertNull(resp.getSuccessTime()); |  | ||||||
|         assertEquals(outRefundNo, resp.getOutRefundNo()); |  | ||||||
|         assertSame(response, resp.getRawData()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Test |  | ||||||
|     @DisplayName("支付包扫描退款,参数校验不通过") |  | ||||||
|     public void test_unified_refund_param_validate() { |  | ||||||
|         // 准备请求参数 |  | ||||||
|         String notifyUrl = randomURL(); |  | ||||||
|         PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> { |  | ||||||
|             o.setOutTradeNo(""); |  | ||||||
|             o.setNotifyUrl(notifyUrl); |  | ||||||
|         }); |  | ||||||
|         // 断言 |  | ||||||
|         assertThrows(ConstraintViolationException.class, () -> client.unifiedRefund(refundReqDTO)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Test |  | ||||||
|     @DisplayName("支付包扫描退款,抛出业务异常") |  | ||||||
|     public void test_unified_refund_throw_service_exception() throws AlipayApiException { |  | ||||||
|         // mock |  | ||||||
|         when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true))) |  | ||||||
|                 .thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR)); |  | ||||||
|         // 准备请求参数 |  | ||||||
|         String notifyUrl = randomURL(); |  | ||||||
|         PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> { |  | ||||||
|             o.setNotifyUrl(notifyUrl); |  | ||||||
|         }); |  | ||||||
|         // 断言 |  | ||||||
|         assertThrows(ServiceException.class, () -> client.unifiedRefund(refundReqDTO)); |  | ||||||
|     } |  | ||||||
|     @Test |  | ||||||
|     @DisplayName("支付包扫描退款,抛出系统异常") |  | ||||||
|     public void test_unified_refund_throw_pay_exception() throws AlipayApiException { |  | ||||||
|         // mock |  | ||||||
|         when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true))) |  | ||||||
|                 .thenThrow(new RuntimeException("系统异常")); |  | ||||||
|         // 准备请求参数 |  | ||||||
|         String notifyUrl = randomURL(); |  | ||||||
|         PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> { |  | ||||||
|             o.setNotifyUrl(notifyUrl); |  | ||||||
|         }); |  | ||||||
|         // 断言 |  | ||||||
|         assertThrows(PayException.class, () -> client.unifiedRefund(refundReqDTO)); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -52,6 +52,7 @@ public class MemberLevelRecordDO extends BaseDO { | |||||||
|      * 会员此时的经验 |      * 会员此时的经验 | ||||||
|      */ |      */ | ||||||
|     private Integer userExperience; |     private Integer userExperience; | ||||||
|  |     // TODO @疯狂:是不是 remark 和 description 可以合并成 description 就够了 | ||||||
|     /** |     /** | ||||||
|      * 备注 |      * 备注 | ||||||
|      */ |      */ | ||||||
|   | |||||||
| @@ -39,6 +39,16 @@ public interface MemberExperienceRecordService { | |||||||
|      */ |      */ | ||||||
|     PageResult<MemberExperienceRecordDO> getExperienceLogPage(MemberExperienceRecordPageReqVO pageReqVO); |     PageResult<MemberExperienceRecordDO> getExperienceLogPage(MemberExperienceRecordPageReqVO pageReqVO); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获得会员经验记录列表, 用于 Excel 导出 | ||||||
|  |      * | ||||||
|  |      * @param exportReqVO 查询条件 | ||||||
|  |      * @return 会员经验记录列表 | ||||||
|  |      */ | ||||||
|  |     List<MemberExperienceLogDO> getExperienceLogList(MemberExperienceLogExportReqVO exportReqVO); | ||||||
|  |  | ||||||
|  |     // TODO @疯狂:类似 MemberLevelLogService 的方法,这里也需要提供一个通用的方法,用于创建经验变动记录 | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 创建 手动调整 经验变动记录 |      * 创建 手动调整 经验变动记录 | ||||||
|      * |      * | ||||||
|   | |||||||
| @@ -47,6 +47,16 @@ public interface MemberLevelRecordService { | |||||||
|      */ |      */ | ||||||
|     PageResult<MemberLevelRecordDO> getLevelLogPage(MemberLevelRecordPageReqVO pageReqVO); |     PageResult<MemberLevelRecordDO> getLevelLogPage(MemberLevelRecordPageReqVO pageReqVO); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获得会员等级记录列表, 用于 Excel 导出 | ||||||
|  |      * | ||||||
|  |      * @param exportReqVO 查询条件 | ||||||
|  |      * @return 会员等级记录列表 | ||||||
|  |      */ | ||||||
|  |     List<MemberLevelLogDO> getLevelLogList(MemberLevelLogExportReqVO exportReqVO); | ||||||
|  |  | ||||||
|  |     // TODO @疯狂:把 createCancelLog、createAdjustLog、createAutoUpgradeLog 几个日志合并成一个通用的日志方法;整体的内容,交给 MemberLevelService 去做;以及对应的 level 变化的通知; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 创建记录: 取消等级 |      * 创建记录: 取消等级 | ||||||
|      * |      * | ||||||
|   | |||||||
| @@ -141,7 +141,6 @@ public class MemberLevelServiceImpl implements MemberLevelService { | |||||||
|     @VisibleForTesting |     @VisibleForTesting | ||||||
|     void validateConfigValid(Long id, String name, Integer level, Integer experience) { |     void validateConfigValid(Long id, String name, Integer level, Integer experience) { | ||||||
|         List<MemberLevelDO> list = levelMapper.selectList(); |         List<MemberLevelDO> list = levelMapper.selectList(); | ||||||
|  |  | ||||||
|         // 校验名称唯一 |         // 校验名称唯一 | ||||||
|         validateNameUnique(list, id, name); |         validateNameUnique(list, id, name); | ||||||
|         // 校验等级唯一 |         // 校验等级唯一 | ||||||
| @@ -178,19 +177,14 @@ public class MemberLevelServiceImpl implements MemberLevelService { | |||||||
|         return levelMapper.selectListByStatus(status); |         return levelMapper.selectListByStatus(status); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Transactional(rollbackFor = Exception.class) |  | ||||||
|     @Override |     @Override | ||||||
|  |     @Transactional(rollbackFor = Exception.class) | ||||||
|     public void updateUserLevel(MemberUserUpdateLevelReqVO updateReqVO) { |     public void updateUserLevel(MemberUserUpdateLevelReqVO updateReqVO) { | ||||||
|         MemberUserDO user = memberUserMapper.selectById(updateReqVO.getId()); |         MemberUserDO user = memberUserMapper.selectById(updateReqVO.getId()); | ||||||
|         if (user == null) { |         if (user == null) { | ||||||
|             throw exception(USER_NOT_EXISTS); |             throw exception(USER_NOT_EXISTS); | ||||||
|         } |         } | ||||||
|  |         // 等级未发生变化 | ||||||
|         // 未调整的情况1 |  | ||||||
|         if (user.getLevelId() == null && updateReqVO.getLevelId() == null) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         // 未调整的情况2 |  | ||||||
|         if (ObjUtil.equal(user.getLevelId(), updateReqVO.getLevelId())) { |         if (ObjUtil.equal(user.getLevelId(), updateReqVO.getLevelId())) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| @@ -218,36 +212,40 @@ public class MemberLevelServiceImpl implements MemberLevelService { | |||||||
|             updateUserLevelIdAndExperience(user.getId(), updateReqVO.getLevelId(), totalExperience); |             updateUserLevelIdAndExperience(user.getId(), updateReqVO.getLevelId(), totalExperience); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|         // 记录会员经验变动 |         // 记录会员经验变动 | ||||||
|         memberExperienceRecordService.createAdjustLog(user.getId(), experience, totalExperience); |         memberExperienceRecordService.createAdjustLog(user.getId(), experience, totalExperience); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Transactional(rollbackFor = Exception.class) |     // TODO @疯狂:方法名,建议改成 increase 或者 add 经验,和项目更统一一些 | ||||||
|  |     // TODO @疯狂:bizType 改成具体数值,主要是枚举在 api 不好传递,rpc 情况下 | ||||||
|     @Override |     @Override | ||||||
|  |     @Transactional(rollbackFor = Exception.class) | ||||||
|     public void plusExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId) { |     public void plusExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId) { | ||||||
|         if (experience == 0) { |         if (experience == 0) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         MemberUserDO user = memberUserMapper.selectById(userId); |         MemberUserDO user = memberUserMapper.selectById(userId); | ||||||
|         if (user.getExperience() == null) { |  | ||||||
|             user.setExperience(0); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // 防止扣出负数 |         // 防止扣出负数 | ||||||
|         int userExperience = NumberUtil.max(user.getExperience() + experience, 0); |         int userExperience = NumberUtil.max(user.getExperience() + experience, 0); | ||||||
|  |  | ||||||
|         // 创建经验记录 |         // 创建经验记录 | ||||||
|         memberExperienceRecordService.createBizLog(userId, experience, userExperience, bizType, bizId); |         memberExperienceRecordService.createBizLog(userId, experience, userExperience, bizType, bizId); | ||||||
|  |  | ||||||
|         // 计算会员等级 |         // 计算会员等级 | ||||||
|         Long levelId = calcLevel(user, userExperience); |         MemberLevelDO newLevel = calculateNewLevel(user, userExperience); | ||||||
|  |         Long newLevelId = null; | ||||||
|  |         if (newLevel != null) { | ||||||
|  |             newLevelId = newLevel.getId(); | ||||||
|  |             // 保存等级变更记录 | ||||||
|  |             memberLevelRecordService.createAutoUpgradeLog(user, newLevel); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         // 更新会员表上的等级编号、经验值 |         // 更新会员表上的等级编号、经验值 | ||||||
|         updateUserLevelIdAndExperience(user.getId(), levelId, userExperience); |         updateUserLevelIdAndExperience(user.getId(), newLevelId, userExperience); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // TODO @疯狂:让 memberUserService 那开个方法;每个模块,不直接操作对方的 mapper; | ||||||
|     private void updateUserLevelIdAndExperience(Long userId, Long levelId, Integer experience) { |     private void updateUserLevelIdAndExperience(Long userId, Long levelId, Integer experience) { | ||||||
|         memberUserMapper.updateById(new MemberUserDO() |         memberUserMapper.updateById(new MemberUserDO() | ||||||
|                 .setId(userId) |                 .setId(userId) | ||||||
| @@ -262,7 +260,7 @@ public class MemberLevelServiceImpl implements MemberLevelService { | |||||||
|      * @param userExperience 会员当前的经验值 |      * @param userExperience 会员当前的经验值 | ||||||
|      * @return 会员等级编号,null表示无变化 |      * @return 会员等级编号,null表示无变化 | ||||||
|      */ |      */ | ||||||
|     private Long calcLevel(MemberUserDO user, int userExperience) { |     private MemberLevelDO calculateNewLevel(MemberUserDO user, int userExperience) { | ||||||
|         List<MemberLevelDO> list = getEnableLevelList(); |         List<MemberLevelDO> list = getEnableLevelList(); | ||||||
|         if (CollUtil.isEmpty(list)) { |         if (CollUtil.isEmpty(list)) { | ||||||
|             log.warn("计算会员等级失败:会员等级配置不存在"); |             log.warn("计算会员等级失败:会员等级配置不存在"); | ||||||
| @@ -283,8 +281,6 @@ public class MemberLevelServiceImpl implements MemberLevelService { | |||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 保存等级变更记录 |         return matchLevel; | ||||||
|         memberLevelRecordService.createAutoUpgradeLog(user, matchLevel); |  | ||||||
|         return matchLevel.getId(); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 owen
					owen