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 1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2  
3   -import cn.hutool.core.date.LocalDateTimeUtil;
4 3 import cn.hutool.core.util.RandomUtil;
5   -import cn.hutool.core.util.ReflectUtil;
6 4 import cn.iocoder.yudao.framework.common.exception.ServiceException;
7 5 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
8 6 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
9 7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
10 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 9 import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
14 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 11 import com.alipay.api.AlipayApiException;
18   -import com.alipay.api.DefaultAlipayClient;
19   -import com.alipay.api.domain.AlipayTradeRefundModel;
20 12 import com.alipay.api.request.AlipayTradePrecreateRequest;
21   -import com.alipay.api.request.AlipayTradeRefundRequest;
22 13 import com.alipay.api.response.AlipayTradePrecreateResponse;
23   -import com.alipay.api.response.AlipayTradeRefundResponse;
  14 +import org.junit.jupiter.api.BeforeEach;
24 15 import org.junit.jupiter.api.DisplayName;
25 16 import org.junit.jupiter.api.Test;
26 17 import org.mockito.ArgumentMatcher;
27 18 import org.mockito.InjectMocks;
28   -import org.mockito.Mock;
29 19  
30 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 22 import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.CLOSED;
35 23 import static cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum.WAITING;
36 24 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
37 25 import static org.junit.jupiter.api.Assertions.*;
38 26 import static org.mockito.ArgumentMatchers.argThat;
39 27 import static org.mockito.Mockito.when;
  28 +
40 29 /**
41 30 * {@link AlipayQrPayClient} 单元测试
42 31 *
43 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 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 44 @Test
... ... @@ -176,119 +152,4 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
176 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 52 * 会员此时的经验
53 53 */
54 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 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 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 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 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 141 @VisibleForTesting
142 142 void validateConfigValid(Long id, String name, Integer level, Integer experience) {
143 143 List<MemberLevelDO> list = levelMapper.selectList();
144   -
145 144 // 校验名称唯一
146 145 validateNameUnique(list, id, name);
147 146 // 校验等级唯一
... ... @@ -178,19 +177,14 @@ public class MemberLevelServiceImpl implements MemberLevelService {
178 177 return levelMapper.selectListByStatus(status);
179 178 }
180 179  
181   - @Transactional(rollbackFor = Exception.class)
182 180 @Override
  181 + @Transactional(rollbackFor = Exception.class)
183 182 public void updateUserLevel(MemberUserUpdateLevelReqVO updateReqVO) {
184 183 MemberUserDO user = memberUserMapper.selectById(updateReqVO.getId());
185 184 if (user == null) {
186 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 188 if (ObjUtil.equal(user.getLevelId(), updateReqVO.getLevelId())) {
195 189 return;
196 190 }
... ... @@ -218,36 +212,40 @@ public class MemberLevelServiceImpl implements MemberLevelService {
218 212 updateUserLevelIdAndExperience(user.getId(), updateReqVO.getLevelId(), totalExperience);
219 213 }
220 214  
221   -
222 215 // 记录会员经验变动
223 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 221 @Override
  222 + @Transactional(rollbackFor = Exception.class)
228 223 public void plusExperience(Long userId, Integer experience, MemberExperienceBizTypeEnum bizType, String bizId) {
229 224 if (experience == 0) {
230 225 return;
231 226 }
232 227  
233 228 MemberUserDO user = memberUserMapper.selectById(userId);
234   - if (user.getExperience() == null) {
235   - user.setExperience(0);
236   - }
237 229  
238 230 // 防止扣出负数
239 231 int userExperience = NumberUtil.max(user.getExperience() + experience, 0);
240   -
241 232 // 创建经验记录
242 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 249 private void updateUserLevelIdAndExperience(Long userId, Long levelId, Integer experience) {
252 250 memberUserMapper.updateById(new MemberUserDO()
253 251 .setId(userId)
... ... @@ -262,7 +260,7 @@ public class MemberLevelServiceImpl implements MemberLevelService {
262 260 * @param userExperience 会员当前的经验值
263 261 * @return 会员等级编号,null表示无变化
264 262 */
265   - private Long calcLevel(MemberUserDO user, int userExperience) {
  263 + private MemberLevelDO calculateNewLevel(MemberUserDO user, int userExperience) {
266 264 List<MemberLevelDO> list = getEnableLevelList();
267 265 if (CollUtil.isEmpty(list)) {
268 266 log.warn("计算会员等级失败:会员等级配置不存在");
... ... @@ -283,8 +281,6 @@ public class MemberLevelServiceImpl implements MemberLevelService {
283 281 return null;
284 282 }
285 283  
286   - // 保存等级变更记录
287   - memberLevelRecordService.createAutoUpgradeLog(user, matchLevel);
288   - return matchLevel.getId();
  284 + return matchLevel;
289 285 }
290 286 }
... ...