Commit b7f54a3061fa835c00f99f61d4d19e40d14d8f3b

Authored by owen
2 parents ca51056c 551a9684

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
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayClientTest.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
  2 +
  3 +import cn.hutool.core.date.LocalDateTimeUtil;
  4 +import cn.hutool.core.util.ReflectUtil;
  5 +import cn.iocoder.yudao.framework.common.exception.ServiceException;
  6 +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
  7 +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
  8 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
  9 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
  10 +import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
  11 +import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
  12 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  13 +import com.alipay.api.AlipayApiException;
  14 +import com.alipay.api.DefaultAlipayClient;
  15 +import com.alipay.api.DefaultSigner;
  16 +import com.alipay.api.domain.AlipayTradeRefundModel;
  17 +import com.alipay.api.request.AlipayTradeRefundRequest;
  18 +import com.alipay.api.response.AlipayTradeRefundResponse;
  19 +import org.junit.jupiter.api.DisplayName;
  20 +import org.junit.jupiter.api.Test;
  21 +import org.mockito.ArgumentMatcher;
  22 +import org.mockito.Mock;
  23 +
  24 +import javax.validation.ConstraintViolationException;
  25 +import java.util.Date;
  26 +
  27 +import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_PUBLIC_KEY;
  28 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  29 +import static org.junit.jupiter.api.Assertions.*;
  30 +import static org.mockito.ArgumentMatchers.argThat;
  31 +import static org.mockito.Mockito.when;
  32 +
  33 +/**
  34 + * @author jason
  35 + */
  36 +public abstract class AbstractAlipayClientTest extends BaseMockitoUnitTest {
  37 +
  38 + private final String privateKey = randomString();
  39 +
  40 + protected AlipayPayClientConfig config = randomPojo(AlipayPayClientConfig.class, t -> {
  41 + t.setServerUrl(randomURL());
  42 + t.setPrivateKey(privateKey);
  43 + t.setMode(MODE_PUBLIC_KEY);
  44 + t.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
  45 + t.setAppCertContent("");
  46 + t.setAlipayPublicCertContent("");
  47 + t.setRootCertContent("");
  48 + });
  49 +
  50 + @Mock
  51 + protected DefaultAlipayClient defaultAlipayClient;
  52 +
  53 + private AbstractAlipayPayClient client;
  54 +
  55 + public void setClient(AbstractAlipayPayClient client) {
  56 + this.client = client;
  57 + }
  58 +
  59 + @Test
  60 + @DisplayName("支付宝 Client 初始化")
  61 + public void test_do_init() {
  62 + client.doInit();
  63 + DefaultAlipayClient realClient = (DefaultAlipayClient) ReflectUtil.getFieldValue(client, "client");
  64 + assertNotSame(defaultAlipayClient, realClient);
  65 + assertInstanceOf(DefaultSigner.class, realClient.getSigner());
  66 + assertEquals(privateKey, ((DefaultSigner) realClient.getSigner()).getPrivateKey());
  67 + }
  68 +
  69 + @Test
  70 + @DisplayName("支付宝 Client 统一退款成功")
  71 + public void test_unified_refund_success() throws AlipayApiException {
  72 + // 准备返回对象
  73 + String notifyUrl = randomURL();
  74 + Date refundTime = randomDate();
  75 + String outRefundNo = randomString();
  76 + String outTradeNo = randomString();
  77 + Integer refundAmount = randomInteger();
  78 + AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> {
  79 + o.setSubCode("");
  80 + o.setGmtRefundPay(refundTime);
  81 + });
  82 + // mock
  83 + when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> {
  84 + assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel());
  85 + AlipayTradeRefundModel bizModel = (AlipayTradeRefundModel) request.getBizModel();
  86 + assertEquals(outRefundNo, bizModel.getOutRequestNo());
  87 + assertEquals(outTradeNo, bizModel.getOutTradeNo());
  88 + assertEquals(String.valueOf(refundAmount / 100.0), bizModel.getRefundAmount());
  89 + return true;
  90 + }))).thenReturn(response);
  91 + // 准备请求参数
  92 + PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
  93 + o.setOutRefundNo(outRefundNo);
  94 + o.setOutTradeNo(outTradeNo);
  95 + o.setNotifyUrl(notifyUrl);
  96 + o.setRefundPrice(refundAmount);
  97 + });
  98 + PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO);
  99 + // 断言
  100 + assertEquals(PayRefundStatusRespEnum.SUCCESS.getStatus(), resp.getStatus());
  101 + assertNull(resp.getChannelRefundNo());
  102 + assertEquals(LocalDateTimeUtil.of(refundTime), resp.getSuccessTime());
  103 + assertEquals(outRefundNo, resp.getOutRefundNo());
  104 + assertSame(response, resp.getRawData());
  105 + }
  106 +
  107 + @Test
  108 + @DisplayName("支付宝 Client 统一退款,渠道返回失败")
  109 + public void test_unified_refund_channel_failed() throws AlipayApiException {
  110 + // 准备返回对象
  111 + String notifyUrl = randomURL();
  112 + String subCode = randomString();
  113 + String subMsg = randomString();
  114 + AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> {
  115 + o.setSubCode(subCode);
  116 + o.setSubMsg(subMsg);
  117 + });
  118 + // mock
  119 + when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> {
  120 + assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel());
  121 + return true;
  122 + }))).thenReturn(response);
  123 + // 准备请求参数
  124 + String outRefundNo = randomString();
  125 + String outTradeNo = randomString();
  126 + PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
  127 + o.setOutRefundNo(outRefundNo);
  128 + o.setOutTradeNo(outTradeNo);
  129 + o.setNotifyUrl(notifyUrl);
  130 + });
  131 + PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO);
  132 + // 断言
  133 + assertEquals(PayRefundStatusRespEnum.FAILURE.getStatus(), resp.getStatus());
  134 + assertNull(resp.getChannelRefundNo());
  135 + assertEquals(subCode, resp.getChannelErrorCode());
  136 + assertEquals(subMsg, resp.getChannelErrorMsg());
  137 + assertNull(resp.getSuccessTime());
  138 + assertEquals(outRefundNo, resp.getOutRefundNo());
  139 + assertSame(response, resp.getRawData());
  140 + }
  141 +
  142 + @Test
  143 + @DisplayName("支付宝 Client 统一退款,参数校验不通过")
  144 + public void test_unified_refund_param_validate() {
  145 + // 准备请求参数
  146 + String notifyUrl = randomURL();
  147 + PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
  148 + o.setOutTradeNo("");
  149 + o.setNotifyUrl(notifyUrl);
  150 + });
  151 + // 断言
  152 + assertThrows(ConstraintViolationException.class, () -> client.unifiedRefund(refundReqDTO));
  153 + }
  154 +
  155 + @Test
  156 + @DisplayName("支付宝 Client 统一退款,抛出业务异常")
  157 + public void test_unified_refund_throw_service_exception() throws AlipayApiException {
  158 + // mock
  159 + when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true)))
  160 + .thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR));
  161 + // 准备请求参数
  162 + String notifyUrl = randomURL();
  163 + PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> o.setNotifyUrl(notifyUrl));
  164 + // 断言
  165 + assertThrows(ServiceException.class, () -> client.unifiedRefund(refundReqDTO));
  166 + }
  167 +
  168 + @Test
  169 + @DisplayName("支付宝 Client 统一退款,抛出系统异常")
  170 + public void test_unified_refund_throw_pay_exception() throws AlipayApiException {
  171 + // mock
  172 + when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true)))
  173 + .thenThrow(new RuntimeException("系统异常"));
  174 + // 准备请求参数
  175 + String notifyUrl = randomURL();
  176 + PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> o.setNotifyUrl(notifyUrl));
  177 + // 断言
  178 + assertThrows(PayException.class, () -> client.unifiedRefund(refundReqDTO));
  179 + }
  180 +}
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java
1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; 1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2
3 -import cn.hutool.core.date.LocalDateTimeUtil;  
4 import cn.hutool.core.util.RandomUtil; 3 import cn.hutool.core.util.RandomUtil;
5 -import cn.hutool.core.util.ReflectUtil;  
6 import cn.iocoder.yudao.framework.common.exception.ServiceException; 4 import cn.iocoder.yudao.framework.common.exception.ServiceException;
7 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; 5 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
8 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; 6 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
9 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
10 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 8 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
11 -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;  
12 -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;  
13 import cn.iocoder.yudao.framework.pay.core.client.exception.PayException; 9 import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
14 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 10 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
15 -import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;  
16 -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;  
17 import com.alipay.api.AlipayApiException; 11 import com.alipay.api.AlipayApiException;
18 -import com.alipay.api.DefaultAlipayClient;  
19 -import com.alipay.api.domain.AlipayTradeRefundModel;  
20 import com.alipay.api.request.AlipayTradePrecreateRequest; 12 import com.alipay.api.request.AlipayTradePrecreateRequest;
21 -import com.alipay.api.request.AlipayTradeRefundRequest;  
22 import com.alipay.api.response.AlipayTradePrecreateResponse; 13 import com.alipay.api.response.AlipayTradePrecreateResponse;
23 -import com.alipay.api.response.AlipayTradeRefundResponse; 14 +import org.junit.jupiter.api.BeforeEach;
24 import org.junit.jupiter.api.DisplayName; 15 import org.junit.jupiter.api.DisplayName;
25 import org.junit.jupiter.api.Test; 16 import org.junit.jupiter.api.Test;
26 import org.mockito.ArgumentMatcher; 17 import org.mockito.ArgumentMatcher;
27 import org.mockito.InjectMocks; 18 import org.mockito.InjectMocks;
28 -import org.mockito.Mock;  
29 19
30 import javax.validation.ConstraintViolationException; 20 import javax.validation.ConstraintViolationException;
31 -import java.util.Date;  
32 21
33 -import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_PUBLIC_KEY;  
34 import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.CLOSED; 22 import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.CLOSED;
35 import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.WAITING; 23 import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.WAITING;
36 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; 24 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
37 import static org.junit.jupiter.api.Assertions.*; 25 import static org.junit.jupiter.api.Assertions.*;
38 import static org.mockito.ArgumentMatchers.argThat; 26 import static org.mockito.ArgumentMatchers.argThat;
39 import static org.mockito.Mockito.when; 27 import static org.mockito.Mockito.when;
  28 +
40 /** 29 /**
41 * {@link AlipayQrPayClient} 单元测试 30 * {@link AlipayQrPayClient} 单元测试
42 * 31 *
43 * @author jason 32 * @author jason
44 */ 33 */
45 -public class AlipayQrPayClientTest extends BaseMockitoUnitTest {  
46 -  
47 - private final AlipayPayClientConfig config = randomPojo(AlipayPayClientConfig.class, t -> {  
48 - t.setServerUrl(randomURL());  
49 - t.setMode(MODE_PUBLIC_KEY);  
50 - t.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);  
51 - t.setAppCertContent("");  
52 - t.setAlipayPublicCertContent("");  
53 - t.setRootCertContent("");  
54 - }); 34 +public class AlipayQrPayClientTest extends AbstractAlipayClientTest {
55 35
56 @InjectMocks 36 @InjectMocks
57 - AlipayQrPayClient client = new AlipayQrPayClient(randomLongId(), config);  
58 -  
59 - @Mock  
60 - private DefaultAlipayClient defaultAlipayClient; 37 + private AlipayQrPayClient client = new AlipayQrPayClient(randomLongId(), config);
61 38
62 - @Test  
63 - public void test_do_init() {  
64 - client.doInit();  
65 - assertNotSame(defaultAlipayClient, ReflectUtil.getFieldValue(client, "defaultAlipayClient")); 39 + @BeforeEach
  40 + public void setUp() {
  41 + setClient(client);
66 } 42 }
67 43
68 @Test 44 @Test
@@ -176,119 +152,4 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest { @@ -176,119 +152,4 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
176 o.setBody(RandomUtil.randomString(32)); 152 o.setBody(RandomUtil.randomString(32));
177 }); 153 });
178 } 154 }
179 -  
180 - @Test  
181 - @DisplayName("支付包扫描退款成功")  
182 - public void test_unified_refund_success() throws AlipayApiException {  
183 - // 准备返回对象  
184 - String notifyUrl = randomURL();  
185 - Date refundTime = randomDate();  
186 - String outRefundNo = randomString();  
187 - String outTradeNo = randomString();  
188 - Integer refundAmount = randomInteger();  
189 - AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> {  
190 - o.setSubCode("");  
191 - o.setGmtRefundPay(refundTime);  
192 - });  
193 - // mock  
194 - when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> {  
195 - assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel());  
196 - AlipayTradeRefundModel bizModel = (AlipayTradeRefundModel) request.getBizModel();  
197 - assertEquals(outRefundNo, bizModel.getOutRequestNo());  
198 - assertEquals(outTradeNo, bizModel.getOutTradeNo());  
199 - assertEquals(String.valueOf(refundAmount / 100.0), bizModel.getRefundAmount());  
200 - return true;  
201 - }))).thenReturn(response);  
202 - // 准备请求参数  
203 - PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {  
204 - o.setOutRefundNo(outRefundNo);  
205 - o.setOutTradeNo(outTradeNo);  
206 - o.setNotifyUrl(notifyUrl);  
207 - o.setRefundPrice(refundAmount);  
208 - });  
209 - PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO);  
210 - // 断言  
211 - assertEquals(PayRefundStatusRespEnum.SUCCESS.getStatus(), resp.getStatus());  
212 - assertNull(resp.getChannelRefundNo());  
213 - assertEquals(LocalDateTimeUtil.of(refundTime), resp.getSuccessTime());  
214 - assertEquals(outRefundNo, resp.getOutRefundNo());  
215 - assertSame(response, resp.getRawData());  
216 - }  
217 -  
218 - @Test  
219 - @DisplayName("支付包扫描退款,渠道返回失败")  
220 - public void test_unified_refund_channel_failed() throws AlipayApiException {  
221 - // 准备返回对象  
222 - String notifyUrl = randomURL();  
223 - String subCode = randomString();  
224 - String subMsg = randomString();  
225 - AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> {  
226 - o.setSubCode(subCode);  
227 - o.setSubMsg(subMsg);  
228 - });  
229 - // mock  
230 - when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> {  
231 - assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel());  
232 - return true;  
233 - }))).thenReturn(response);  
234 - // 准备请求参数  
235 - String outRefundNo = randomString();  
236 - String outTradeNo = randomString();  
237 - PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {  
238 - o.setOutRefundNo(outRefundNo);  
239 - o.setOutTradeNo(outTradeNo);  
240 - o.setNotifyUrl(notifyUrl);  
241 - });  
242 - PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO);  
243 - // 断言  
244 - assertEquals(PayRefundStatusRespEnum.FAILURE.getStatus(), resp.getStatus());  
245 - assertNull(resp.getChannelRefundNo());  
246 - assertEquals(subCode, resp.getChannelErrorCode());  
247 - assertEquals(subMsg, resp.getChannelErrorMsg());  
248 - assertNull(resp.getSuccessTime());  
249 - assertEquals(outRefundNo, resp.getOutRefundNo());  
250 - assertSame(response, resp.getRawData());  
251 - }  
252 -  
253 - @Test  
254 - @DisplayName("支付包扫描退款,参数校验不通过")  
255 - public void test_unified_refund_param_validate() {  
256 - // 准备请求参数  
257 - String notifyUrl = randomURL();  
258 - PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {  
259 - o.setOutTradeNo("");  
260 - o.setNotifyUrl(notifyUrl);  
261 - });  
262 - // 断言  
263 - assertThrows(ConstraintViolationException.class, () -> client.unifiedRefund(refundReqDTO));  
264 - }  
265 -  
266 - @Test  
267 - @DisplayName("支付包扫描退款,抛出业务异常")  
268 - public void test_unified_refund_throw_service_exception() throws AlipayApiException {  
269 - // mock  
270 - when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true)))  
271 - .thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR));  
272 - // 准备请求参数  
273 - String notifyUrl = randomURL();  
274 - PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {  
275 - o.setNotifyUrl(notifyUrl);  
276 - });  
277 - // 断言  
278 - assertThrows(ServiceException.class, () -> client.unifiedRefund(refundReqDTO));  
279 - }  
280 - @Test  
281 - @DisplayName("支付包扫描退款,抛出系统异常")  
282 - public void test_unified_refund_throw_pay_exception() throws AlipayApiException {  
283 - // mock  
284 - when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true)))  
285 - .thenThrow(new RuntimeException("系统异常"));  
286 - // 准备请求参数  
287 - String notifyUrl = randomURL();  
288 - PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {  
289 - o.setNotifyUrl(notifyUrl);  
290 - });  
291 - // 断言  
292 - assertThrows(PayException.class, () -> client.unifiedRefund(refundReqDTO));  
293 - }  
294 } 155 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/level/MemberLevelRecordDO.java
@@ -52,6 +52,7 @@ public class MemberLevelRecordDO extends BaseDO { @@ -52,6 +52,7 @@ public class MemberLevelRecordDO extends BaseDO {
52 * 会员此时的经验 52 * 会员此时的经验
53 */ 53 */
54 private Integer userExperience; 54 private Integer userExperience;
  55 + // TODO @疯狂:是不是 remark 和 description 可以合并成 description 就够了
55 /** 56 /**
56 * 备注 57 * 备注
57 */ 58 */
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberExperienceRecordService.java
@@ -40,6 +40,16 @@ public interface MemberExperienceRecordService { @@ -40,6 +40,16 @@ public interface MemberExperienceRecordService {
40 PageResult<MemberExperienceRecordDO> getExperienceLogPage(MemberExperienceRecordPageReqVO pageReqVO); 40 PageResult<MemberExperienceRecordDO> getExperienceLogPage(MemberExperienceRecordPageReqVO pageReqVO);
41 41
42 /** 42 /**
  43 + * 获得会员经验记录列表, 用于 Excel 导出
  44 + *
  45 + * @param exportReqVO 查询条件
  46 + * @return 会员经验记录列表
  47 + */
  48 + List<MemberExperienceLogDO> getExperienceLogList(MemberExperienceLogExportReqVO exportReqVO);
  49 +
  50 + // TODO @疯狂:类似 MemberLevelLogService 的方法,这里也需要提供一个通用的方法,用于创建经验变动记录
  51 +
  52 + /**
43 * 创建 手动调整 经验变动记录 53 * 创建 手动调整 经验变动记录
44 * 54 *
45 * @param userId 会员编号 55 * @param userId 会员编号
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelRecordService.java
@@ -48,6 +48,16 @@ public interface MemberLevelRecordService { @@ -48,6 +48,16 @@ public interface MemberLevelRecordService {
48 PageResult<MemberLevelRecordDO> getLevelLogPage(MemberLevelRecordPageReqVO pageReqVO); 48 PageResult<MemberLevelRecordDO> getLevelLogPage(MemberLevelRecordPageReqVO pageReqVO);
49 49
50 /** 50 /**
  51 + * 获得会员等级记录列表, 用于 Excel 导出
  52 + *
  53 + * @param exportReqVO 查询条件
  54 + * @return 会员等级记录列表
  55 + */
  56 + List<MemberLevelLogDO> getLevelLogList(MemberLevelLogExportReqVO exportReqVO);
  57 +
  58 + // TODO @疯狂:把 createCancelLog、createAdjustLog、createAutoUpgradeLog 几个日志合并成一个通用的日志方法;整体的内容,交给 MemberLevelService 去做;以及对应的 level 变化的通知;
  59 +
  60 + /**
51 * 创建记录: 取消等级 61 * 创建记录: 取消等级
52 * 62 *
53 * @param userId 会员编号 63 * @param userId 会员编号
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelServiceImpl.java
@@ -141,7 +141,6 @@ public class MemberLevelServiceImpl implements MemberLevelService { @@ -141,7 +141,6 @@ public class MemberLevelServiceImpl implements MemberLevelService {
141 @VisibleForTesting 141 @VisibleForTesting
142 void validateConfigValid(Long id, String name, Integer level, Integer experience) { 142 void validateConfigValid(Long id, String name, Integer level, Integer experience) {
143 List<MemberLevelDO> list = levelMapper.selectList(); 143 List<MemberLevelDO> list = levelMapper.selectList();
144 -  
145 // 校验名称唯一 144 // 校验名称唯一
146 validateNameUnique(list, id, name); 145 validateNameUnique(list, id, name);
147 // 校验等级唯一 146 // 校验等级唯一
@@ -178,19 +177,14 @@ public class MemberLevelServiceImpl implements MemberLevelService { @@ -178,19 +177,14 @@ public class MemberLevelServiceImpl implements MemberLevelService {
178 return levelMapper.selectListByStatus(status); 177 return levelMapper.selectListByStatus(status);
179 } 178 }
180 179
181 - @Transactional(rollbackFor = Exception.class)  
182 @Override 180 @Override
  181 + @Transactional(rollbackFor = Exception.class)
183 public void updateUserLevel(MemberUserUpdateLevelReqVO updateReqVO) { 182 public void updateUserLevel(MemberUserUpdateLevelReqVO updateReqVO) {
184 MemberUserDO user = memberUserMapper.selectById(updateReqVO.getId()); 183 MemberUserDO user = memberUserMapper.selectById(updateReqVO.getId());
185 if (user == null) { 184 if (user == null) {
186 throw exception(USER_NOT_EXISTS); 185 throw exception(USER_NOT_EXISTS);
187 } 186 }
188 -  
189 - // 未调整的情况1  
190 - if (user.getLevelId() == null && updateReqVO.getLevelId() == null) {  
191 - return;  
192 - }  
193 - // 未调整的情况2 187 + // 等级未发生变化
194 if (ObjUtil.equal(user.getLevelId(), updateReqVO.getLevelId())) { 188 if (ObjUtil.equal(user.getLevelId(), updateReqVO.getLevelId())) {
195 return; 189 return;
196 } 190 }
@@ -218,36 +212,40 @@ public class MemberLevelServiceImpl implements MemberLevelService { @@ -218,36 +212,40 @@ public class MemberLevelServiceImpl implements MemberLevelService {
218 updateUserLevelIdAndExperience(user.getId(), updateReqVO.getLevelId(), totalExperience); 212 updateUserLevelIdAndExperience(user.getId(), updateReqVO.getLevelId(), totalExperience);
219 } 213 }
220 214
221 -  
222 // 记录会员经验变动 215 // 记录会员经验变动
223 memberExperienceRecordService.createAdjustLog(user.getId(), experience, totalExperience); 216 memberExperienceRecordService.createAdjustLog(user.getId(), experience, totalExperience);
224 } 217 }
225 218
226 - @Transactional(rollbackFor = Exception.class) 219 + // TODO @疯狂:方法名,建议改成 increase 或者 add 经验,和项目更统一一些
  220 + // TODO @疯狂:bizType 改成具体数值,主要是枚举在 api 不好传递,rpc 情况下
227 @Override 221 @Override
  222 + @Transactional(rollbackFor = Exception.class)
228 public void plusExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId) { 223 public void plusExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId) {
229 if (experience == 0) { 224 if (experience == 0) {
230 return; 225 return;
231 } 226 }
232 227
233 MemberUserDO user = memberUserMapper.selectById(userId); 228 MemberUserDO user = memberUserMapper.selectById(userId);
234 - if (user.getExperience() == null) {  
235 - user.setExperience(0);  
236 - }  
237 229
238 // 防止扣出负数 230 // 防止扣出负数
239 int userExperience = NumberUtil.max(user.getExperience() + experience, 0); 231 int userExperience = NumberUtil.max(user.getExperience() + experience, 0);
240 -  
241 // 创建经验记录 232 // 创建经验记录
242 memberExperienceRecordService.createBizLog(userId, experience, userExperience, bizType, bizId); 233 memberExperienceRecordService.createBizLog(userId, experience, userExperience, bizType, bizId);
243 234
244 // 计算会员等级 235 // 计算会员等级
245 - Long levelId = calcLevel(user, userExperience); 236 + MemberLevelDO newLevel = calculateNewLevel(user, userExperience);
  237 + Long newLevelId = null;
  238 + if (newLevel != null) {
  239 + newLevelId = newLevel.getId();
  240 + // 保存等级变更记录
  241 + memberLevelRecordService.createAutoUpgradeLog(user, newLevel);
  242 + }
246 243
247 // 更新会员表上的等级编号、经验值 244 // 更新会员表上的等级编号、经验值
248 - updateUserLevelIdAndExperience(user.getId(), levelId, userExperience); 245 + updateUserLevelIdAndExperience(user.getId(), newLevelId, userExperience);
249 } 246 }
250 247
  248 + // TODO @疯狂:让 memberUserService 那开个方法;每个模块,不直接操作对方的 mapper;
251 private void updateUserLevelIdAndExperience(Long userId, Long levelId, Integer experience) { 249 private void updateUserLevelIdAndExperience(Long userId, Long levelId, Integer experience) {
252 memberUserMapper.updateById(new MemberUserDO() 250 memberUserMapper.updateById(new MemberUserDO()
253 .setId(userId) 251 .setId(userId)
@@ -262,7 +260,7 @@ public class MemberLevelServiceImpl implements MemberLevelService { @@ -262,7 +260,7 @@ public class MemberLevelServiceImpl implements MemberLevelService {
262 * @param userExperience 会员当前的经验值 260 * @param userExperience 会员当前的经验值
263 * @return 会员等级编号,null表示无变化 261 * @return 会员等级编号,null表示无变化
264 */ 262 */
265 - private Long calcLevel(MemberUserDO user, int userExperience) { 263 + private MemberLevelDO calculateNewLevel(MemberUserDO user, int userExperience) {
266 List<MemberLevelDO> list = getEnableLevelList(); 264 List<MemberLevelDO> list = getEnableLevelList();
267 if (CollUtil.isEmpty(list)) { 265 if (CollUtil.isEmpty(list)) {
268 log.warn("计算会员等级失败:会员等级配置不存在"); 266 log.warn("计算会员等级失败:会员等级配置不存在");
@@ -283,8 +281,6 @@ public class MemberLevelServiceImpl implements MemberLevelService { @@ -283,8 +281,6 @@ public class MemberLevelServiceImpl implements MemberLevelService {
283 return null; 281 return null;
284 } 282 }
285 283
286 - // 保存等级变更记录  
287 - memberLevelRecordService.createAutoUpgradeLog(user, matchLevel);  
288 - return matchLevel.getId(); 284 + return matchLevel;
289 } 285 }
290 } 286 }