package com.ruoyi.user.service.impl;
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.when;
|
|
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.mockito.ArgumentCaptor;
|
import org.mockito.InjectMocks;
|
import org.mockito.Mock;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
import com.ruoyi.common.core.constant.UserConstants;
|
import com.ruoyi.common.core.exception.ServiceException;
|
import com.ruoyi.user.config.UniversalCodeProperties;
|
import com.ruoyi.user.domain.AccountUser;
|
import com.ruoyi.user.domain.vo.AccountUserInfoVo;
|
import com.ruoyi.user.domain.vo.EarningSummaryVo;
|
import com.ruoyi.user.enums.MemberLevelEnum;
|
import com.ruoyi.user.mapper.AccountEarningRecordMapper;
|
import com.ruoyi.user.mapper.AccountUserMapper;
|
|
/**
|
* 会员服务单元测试
|
*
|
* 覆盖会员注册(通用推荐码放行、无效/停用推荐码拒绝、正常绑定推荐人)
|
* 与个人中心会员信息查询(正常返回、入参/会员缺失校验、收益与等级兜底)。
|
*
|
* @author hugray
|
*/
|
@ExtendWith(MockitoExtension.class)
|
class AccountUserServiceImplTest
|
{
|
private static final String PHONE = "13800000000";
|
|
private static final String UNIVERSAL_CODE = "HUGRAY88";
|
|
@Mock
|
private AccountUserMapper accountUserMapper;
|
|
@Mock
|
private AccountEarningRecordMapper accountEarningRecordMapper;
|
|
@Mock
|
private UniversalCodeProperties universalCodeProperties;
|
|
@InjectMocks
|
private AccountUserServiceImpl accountUserService;
|
|
@Test
|
@DisplayName("命中通用推荐码应注册成功且不绑定推荐人")
|
void registerMember_shouldNotBindReferrer_whenUniversalCodeHit()
|
{
|
when(universalCodeProperties.getUniversalReferralCode()).thenReturn(UNIVERSAL_CODE);
|
when(accountUserMapper.selectAccountUserByPhone(PHONE)).thenReturn(null);
|
when(accountUserMapper.selectAccountUserByReferralCode(anyString())).thenReturn(null);
|
when(accountUserMapper.insertAccountUser(any(AccountUser.class))).thenReturn(1);
|
|
accountUserService.registerMember(PHONE, UNIVERSAL_CODE);
|
|
ArgumentCaptor<AccountUser> captor = ArgumentCaptor.forClass(AccountUser.class);
|
verify(accountUserMapper).insertAccountUser(captor.capture());
|
AccountUser saved = captor.getValue();
|
assertNull(saved.getReferrerId());
|
assertNotNull(saved.getMyReferralCode());
|
// 姓名注册未采集,落库留空(不再以手机号填充)
|
assertNull(saved.getNickname());
|
// 通用推荐码注册直接发放 L4 会员等级
|
assertEquals(MemberLevelEnum.L4.getCode(), saved.getSystemLevel());
|
// 通用码不参与推荐人定位
|
verify(accountUserMapper, never()).selectAccountUserByReferralCode(UNIVERSAL_CODE);
|
}
|
|
@Test
|
@DisplayName("未配置通用推荐码且推荐码无效时应拒绝注册")
|
void registerMember_shouldThrow_whenReferralCodeInvalid()
|
{
|
when(universalCodeProperties.getUniversalReferralCode()).thenReturn(null);
|
when(accountUserMapper.selectAccountUserByPhone(PHONE)).thenReturn(null);
|
when(accountUserMapper.selectAccountUserByReferralCode("BADCODE")).thenReturn(null);
|
|
ServiceException ex = assertThrows(ServiceException.class,
|
() -> accountUserService.registerMember(PHONE, "BADCODE"));
|
assertEquals("推荐人码无效", ex.getMessage());
|
}
|
|
@Test
|
@DisplayName("推荐人账号停用时应拒绝注册")
|
void registerMember_shouldThrow_whenReferrerDisabled()
|
{
|
AccountUser referrer = new AccountUser();
|
referrer.setUserId(100L);
|
referrer.setStatus(UserConstants.USER_DISABLE);
|
when(universalCodeProperties.getUniversalReferralCode()).thenReturn(null);
|
when(accountUserMapper.selectAccountUserByPhone(PHONE)).thenReturn(null);
|
when(accountUserMapper.selectAccountUserByReferralCode("VALID123")).thenReturn(referrer);
|
|
ServiceException ex = assertThrows(ServiceException.class,
|
() -> accountUserService.registerMember(PHONE, "VALID123"));
|
assertEquals("推荐人账号异常", ex.getMessage());
|
}
|
|
@Test
|
@DisplayName("推荐码有效时应绑定推荐人")
|
void registerMember_shouldBindReferrer_whenReferralCodeValid()
|
{
|
AccountUser referrer = new AccountUser();
|
referrer.setUserId(100L);
|
referrer.setStatus(UserConstants.NORMAL);
|
when(universalCodeProperties.getUniversalReferralCode()).thenReturn(null);
|
when(accountUserMapper.selectAccountUserByPhone(PHONE)).thenReturn(null);
|
// 仅 "VALID123" 命中推荐人;其余(本人推荐码查重)返回 null,避免影响唯一码生成
|
when(accountUserMapper.selectAccountUserByReferralCode(anyString()))
|
.thenAnswer(invocation -> "VALID123".equals(invocation.getArgument(0)) ? referrer : null);
|
when(accountUserMapper.insertAccountUser(any(AccountUser.class))).thenReturn(1);
|
|
accountUserService.registerMember(PHONE, "VALID123");
|
|
ArgumentCaptor<AccountUser> captor = ArgumentCaptor.forClass(AccountUser.class);
|
verify(accountUserMapper).insertAccountUser(captor.capture());
|
assertEquals(100L, captor.getValue().getReferrerId());
|
// 非通用码注册不设等级,走库默认值 C(普通用户)
|
assertNull(captor.getValue().getSystemLevel());
|
}
|
|
@Test
|
@DisplayName("会员存在时应返回昵称/头像/等级与收益汇总")
|
void getAccountUserInfo_shouldReturnProfile_whenMemberExists()
|
{
|
AccountUser member = new AccountUser();
|
member.setUserId(66L);
|
member.setNickname("辉煌G891");
|
member.setPhone("13355555555");
|
member.setAvatar("https://cdn.example.com/a.png");
|
member.setSystemLevel(MemberLevelEnum.L3.getCode());
|
member.setAuthStatus("2");
|
EarningSummaryVo summary = new EarningSummaryVo(1000L, 40000L, 12800000L);
|
when(accountUserMapper.selectAccountUserById(66L)).thenReturn(member);
|
when(accountEarningRecordMapper.selectEarningSummary(66L)).thenReturn(summary);
|
|
AccountUserInfoVo vo = accountUserService.getAccountUserInfo(66L);
|
|
assertEquals(66L, vo.getUserId());
|
assertEquals("辉煌G891", vo.getNickname());
|
assertEquals("133****5555", vo.getPhone());
|
assertEquals("https://cdn.example.com/a.png", vo.getAvatar());
|
assertEquals(MemberLevelEnum.L3.getCode(), vo.getMemberLevel());
|
// 认证状态原样下发,供个人中心「实名认证」行徽标回显
|
assertEquals("2", vo.getAuthStatus());
|
assertEquals(1000L, vo.getEarnings().getToday());
|
assertEquals(40000L, vo.getEarnings().getMonth());
|
assertEquals(12800000L, vo.getEarnings().getTotal());
|
}
|
|
@Test
|
@DisplayName("会员ID为空时应抛出获取会员信息失败")
|
void getAccountUserInfo_shouldThrow_whenUserIdNull()
|
{
|
ServiceException ex = assertThrows(ServiceException.class,
|
() -> accountUserService.getAccountUserInfo(null));
|
assertEquals("获取会员信息失败", ex.getMessage());
|
}
|
|
@Test
|
@DisplayName("会员不存在时应抛出获取会员信息失败")
|
void getAccountUserInfo_shouldThrow_whenMemberNotFound()
|
{
|
when(accountUserMapper.selectAccountUserById(66L)).thenReturn(null);
|
|
ServiceException ex = assertThrows(ServiceException.class,
|
() -> accountUserService.getAccountUserInfo(66L));
|
assertEquals("获取会员信息失败", ex.getMessage());
|
}
|
|
@Test
|
@DisplayName("无收益流水时应兜底返回全零汇总")
|
void getAccountUserInfo_shouldFallbackZeroEarnings_whenNoRecord()
|
{
|
AccountUser member = new AccountUser();
|
member.setUserId(66L);
|
member.setSystemLevel(MemberLevelEnum.L1.getCode());
|
when(accountUserMapper.selectAccountUserById(66L)).thenReturn(member);
|
when(accountEarningRecordMapper.selectEarningSummary(66L)).thenReturn(null);
|
|
AccountUserInfoVo vo = accountUserService.getAccountUserInfo(66L);
|
|
assertNotNull(vo.getEarnings());
|
assertEquals(0L, vo.getEarnings().getToday());
|
assertEquals(0L, vo.getEarnings().getMonth());
|
assertEquals(0L, vo.getEarnings().getTotal());
|
}
|
|
@Test
|
@DisplayName("等级为空时应兜底为 C")
|
void getAccountUserInfo_shouldFallbackC_whenSystemLevelBlank()
|
{
|
assertFallbackLevel(null, MemberLevelEnum.C.getCode());
|
assertFallbackLevel("", MemberLevelEnum.C.getCode());
|
}
|
|
@Test
|
@DisplayName("等级为非法值时应兜底为 C")
|
void getAccountUserInfo_shouldFallbackC_whenSystemLevelIllegal()
|
{
|
assertFallbackLevel("L9", MemberLevelEnum.C.getCode());
|
}
|
|
@Test
|
@DisplayName("认证状态为空时应兜底为未认证(0)")
|
void getAccountUserInfo_shouldFallbackNoneAuthStatus_whenBlank()
|
{
|
AccountUser member = new AccountUser();
|
member.setUserId(66L);
|
member.setSystemLevel(MemberLevelEnum.C.getCode());
|
when(accountUserMapper.selectAccountUserById(66L)).thenReturn(member);
|
when(accountEarningRecordMapper.selectEarningSummary(66L)).thenReturn(EarningSummaryVo.zero());
|
|
AccountUserInfoVo vo = accountUserService.getAccountUserInfo(66L);
|
|
assertEquals("0", vo.getAuthStatus());
|
}
|
|
/**
|
* 校验不同等级取值下的等级兜底结果
|
*
|
* @param rawLevel 库中原始等级值
|
* @param expectedLevel 期望下发的等级
|
*/
|
private void assertFallbackLevel(String rawLevel, String expectedLevel)
|
{
|
AccountUser member = new AccountUser();
|
member.setUserId(66L);
|
member.setSystemLevel(rawLevel);
|
when(accountUserMapper.selectAccountUserById(66L)).thenReturn(member);
|
when(accountEarningRecordMapper.selectEarningSummary(66L)).thenReturn(EarningSummaryVo.zero());
|
|
AccountUserInfoVo vo = accountUserService.getAccountUserInfo(66L);
|
|
assertEquals(expectedLevel, vo.getMemberLevel());
|
}
|
|
@Test
|
@DisplayName("更新头像时应只写入 avatar 列")
|
void updateAvatar_shouldUpdateAvatar_whenValid()
|
{
|
when(accountUserMapper.updateUserAvatar(any(AccountUser.class))).thenReturn(1);
|
|
accountUserService.updateAvatar(66L, "https://cdn.example.com/new.png");
|
|
ArgumentCaptor<AccountUser> captor = ArgumentCaptor.forClass(AccountUser.class);
|
verify(accountUserMapper).updateUserAvatar(captor.capture());
|
assertEquals(66L, captor.getValue().getUserId());
|
assertEquals("https://cdn.example.com/new.png", captor.getValue().getAvatar());
|
}
|
|
@Test
|
@DisplayName("头像地址为空时应抛出提示且不落库")
|
void updateAvatar_shouldThrow_whenAvatarBlank()
|
{
|
ServiceException ex = assertThrows(ServiceException.class,
|
() -> accountUserService.updateAvatar(66L, ""));
|
|
assertEquals("头像地址不能为空", ex.getMessage());
|
verify(accountUserMapper, never()).updateUserAvatar(any(AccountUser.class));
|
}
|
|
@Test
|
@DisplayName("头像地址超长时应抛出提示且不落库")
|
void updateAvatar_shouldThrow_whenAvatarTooLong()
|
{
|
String longUrl = "a".repeat(256);
|
|
ServiceException ex = assertThrows(ServiceException.class,
|
() -> accountUserService.updateAvatar(66L, longUrl));
|
|
assertEquals("头像地址过长", ex.getMessage());
|
verify(accountUserMapper, never()).updateUserAvatar(any(AccountUser.class));
|
}
|
|
@Test
|
@DisplayName("会员不存在(影响行数为 0)时应抛出更新失败")
|
void updateAvatar_shouldThrow_whenMemberNotFound()
|
{
|
when(accountUserMapper.updateUserAvatar(any(AccountUser.class))).thenReturn(0);
|
|
ServiceException ex = assertThrows(ServiceException.class,
|
() -> accountUserService.updateAvatar(66L, "https://cdn.example.com/new.png"));
|
|
assertEquals("更新头像失败", ex.getMessage());
|
}
|
|
@Test
|
@DisplayName("会员ID为空时应抛出更新失败")
|
void updateAvatar_shouldThrow_whenUserIdNull()
|
{
|
ServiceException ex = assertThrows(ServiceException.class,
|
() -> accountUserService.updateAvatar(null, "https://cdn.example.com/new.png"));
|
|
assertEquals("更新头像失败", ex.getMessage());
|
}
|
}
|