Commit 89df5b3cf619e98cf4a4fb98d5bd140f2b644eb3

Authored by YunaiV
1 parent 705a5ff6

临时提交,暂时不考虑 unionId,简化模型

yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/social/SocialTypeEnum.java
1 package cn.iocoder.yudao.module.system.enums.social; 1 package cn.iocoder.yudao.module.system.enums.social;
2 2
3 -import cn.hutool.core.collection.ListUtil;  
4 import cn.hutool.core.util.ArrayUtil; 3 import cn.hutool.core.util.ArrayUtil;
5 import cn.iocoder.yudao.framework.common.core.IntArrayValuable; 4 import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
6 import lombok.AllArgsConstructor; 5 import lombok.AllArgsConstructor;
7 import lombok.Getter; 6 import lombok.Getter;
8 7
9 import java.util.Arrays; 8 import java.util.Arrays;
10 -import java.util.List; 9 +import java.util.Collection;
  10 +import java.util.Set;
  11 +import java.util.stream.Collectors;
11 12
12 /** 13 /**
13 * 社交平台的类型枚举 14 * 社交平台的类型枚举
@@ -77,4 +78,11 @@ public enum SocialTypeEnum implements IntArrayValuable { @@ -77,4 +78,11 @@ public enum SocialTypeEnum implements IntArrayValuable {
77 return ArrayUtil.firstMatch(o -> o.getType().equals(type), values()); 78 return ArrayUtil.firstMatch(o -> o.getType().equals(type), values());
78 } 79 }
79 80
  81 + public static Set<Integer> getTypes(Collection<Integer> platforms) {
  82 + return Arrays.stream(values())
  83 + .filter(socialTypeEnum -> platforms.contains(socialTypeEnum.getPlatform()))
  84 + .map(SocialTypeEnum::getType)
  85 + .collect(Collectors.toSet());
  86 + }
  87 +
80 } 88 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserDO.java
@@ -43,13 +43,6 @@ public class SocialUserDO extends BaseDO { @@ -43,13 +43,6 @@ public class SocialUserDO extends BaseDO {
43 */ 43 */
44 private String token; 44 private String token;
45 /** 45 /**
46 - * 社交的全局编号  
47 - *  
48 - * 例如说,微信平台的 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html  
49 - * 如果没有 unionId 的平台,直接使用 openid 作为该字段的值  
50 - */  
51 - private String unionId;  
52 - /**  
53 * 原始 Token 数据,一般是 JSON 格式 46 * 原始 Token 数据,一般是 JSON 格式
54 */ 47 */
55 private String rawTokenInfo; 48 private String rawTokenInfo;
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserBindMapper.java
@@ -5,14 +5,21 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -5,14 +5,21 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
5 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO; 5 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
6 import org.apache.ibatis.annotations.Mapper; 6 import org.apache.ibatis.annotations.Mapper;
7 7
  8 +import java.util.List;
  9 +
8 @Mapper 10 @Mapper
9 public interface SocialUserBindMapper extends BaseMapperX<SocialUserBindDO> { 11 public interface SocialUserBindMapper extends BaseMapperX<SocialUserBindDO> {
10 12
11 - default void deleteByUserTypeAndUserIdAndPlatformAndUnionId(Integer userType, Long userId,  
12 - Integer platform, String unionId) { 13 + default void deleteByUserTypeAndUserIdAndUnionId(Integer userType, Long userId, Integer platform) {
13 delete(new LambdaQueryWrapperX<SocialUserBindDO>() 14 delete(new LambdaQueryWrapperX<SocialUserBindDO>()
14 .eq(SocialUserBindDO::getUserType, userType) 15 .eq(SocialUserBindDO::getUserType, userType)
15 .eq(SocialUserBindDO::getUserId, userId) 16 .eq(SocialUserBindDO::getUserId, userId)
  17 + .eq(SocialUserBindDO::getPlatform, platform));
  18 + }
  19 +
  20 + default void deleteByUserTypeAndPlatformAndUnionId(Integer userType, Integer platform, String unionId) {
  21 + delete(new LambdaQueryWrapperX<SocialUserBindDO>()
  22 + .eq(SocialUserBindDO::getUserType, userType)
16 .eq(SocialUserBindDO::getPlatform, platform) 23 .eq(SocialUserBindDO::getPlatform, platform)
17 .eq(SocialUserBindDO::getUnionId, unionId)); 24 .eq(SocialUserBindDO::getUnionId, unionId));
18 } 25 }
@@ -25,4 +32,10 @@ public interface SocialUserBindMapper extends BaseMapperX&lt;SocialUserBindDO&gt; { @@ -25,4 +32,10 @@ public interface SocialUserBindMapper extends BaseMapperX&lt;SocialUserBindDO&gt; {
25 .eq(SocialUserBindDO::getUnionId, unionId)); 32 .eq(SocialUserBindDO::getUnionId, unionId));
26 } 33 }
27 34
  35 + default List<SocialUserBindDO> selectListByUserIdAndUserType(Long userId, Integer userType) {
  36 + return selectList(new LambdaQueryWrapperX<SocialUserBindDO>()
  37 + .eq(SocialUserBindDO::getUserId, userId)
  38 + .eq(SocialUserBindDO::getUserType, userType));
  39 + }
  40 +
28 } 41 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserMapper.java
@@ -25,8 +25,10 @@ public interface SocialUserMapper extends BaseMapperX&lt;SocialUserDO&gt; { @@ -25,8 +25,10 @@ public interface SocialUserMapper extends BaseMapperX&lt;SocialUserDO&gt; {
25 .eq(SocialUserDO::getCode, openid)); 25 .eq(SocialUserDO::getCode, openid));
26 } 26 }
27 27
28 - default List<SocialUserDO> selectListByUserId(Integer userType, Long userId) {  
29 - return selectList(new QueryWrapper<SocialUserDO>().eq("user_type", userType).eq("user_id", userId)); 28 + default List<SocialUserDO> selectListByUnionIdAndType(Collection<String> unionIds, Collection<Integer> types) {
  29 + return selectList(new LambdaQueryWrapper<SocialUserDO>()
  30 + .in(SocialUserDO::getUnionId, unionIds)
  31 + .in(SocialUserDO::getType, types));
30 } 32 }
31 33
32 } 34 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java
1 package cn.iocoder.yudao.module.system.service.social; 1 package cn.iocoder.yudao.module.system.service.social;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
4 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
  6 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 import cn.iocoder.yudao.framework.common.util.http.HttpUtils; 7 import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
6 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; 8 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
7 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO; 9 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
@@ -21,7 +23,9 @@ import org.springframework.transaction.annotation.Transactional; @@ -21,7 +23,9 @@ import org.springframework.transaction.annotation.Transactional;
21 import org.springframework.validation.annotation.Validated; 23 import org.springframework.validation.annotation.Validated;
22 24
23 import javax.annotation.Resource; 25 import javax.annotation.Resource;
  26 +import java.util.Collections;
24 import java.util.List; 27 import java.util.List;
  28 +import java.util.Set;
25 29
26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 30 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
27 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; 31 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
@@ -64,20 +68,18 @@ public class SocialUserServiceImpl implements SocialUserService { @@ -64,20 +68,18 @@ public class SocialUserServiceImpl implements SocialUserService {
64 } 68 }
65 69
66 // 请求获取 70 // 请求获取
67 - AuthUser authUser = getAuthUser(type, buildAuthCallback(code, state));  
68 - if (authUser == null) {  
69 - throw exception(SOCIAL_USER_NOT_FOUND);  
70 - } 71 + AuthUser authUser = getAuthUser(type, code, state);
  72 + Assert.notNull(authUser, "三方用户不能为空");
71 73
72 // 保存到 DB 中 74 // 保存到 DB 中
73 socialUser = socialUserMapper.selectByTypeAndOpenid(type, authUser.getUuid()); 75 socialUser = socialUserMapper.selectByTypeAndOpenid(type, authUser.getUuid());
74 if (socialUser == null) { 76 if (socialUser == null) {
75 socialUser = new SocialUserDO(); 77 socialUser = new SocialUserDO();
76 } 78 }
77 - socialUser.setOpenid(authUser.getUuid()).setToken(authUser.getToken().getAccessToken()).setRawTokenInfo((toJsonString(authUser.getToken()))) 79 + socialUser.setType(type).setCode(code).setState(state) // 需要保存 code + state 字段,保证后续可查询
  80 + .setOpenid(authUser.getUuid()).setToken(authUser.getToken().getAccessToken()).setRawTokenInfo((toJsonString(authUser.getToken())))
78 .setUnionId(StrUtil.blankToDefault(authUser.getToken().getUnionId(), authUser.getUuid())) // unionId 识别多个用户 81 .setUnionId(StrUtil.blankToDefault(authUser.getToken().getUnionId(), authUser.getUuid())) // unionId 识别多个用户
79 - .setNickname(authUser.getNickname()).setAvatar(authUser.getAvatar()).setRawUserInfo(toJsonString(authUser.getRawUserInfo()))  
80 - .setCode(code).setState(state); // 需要保存 code + state 字段,保证后续可查询 82 + .setNickname(authUser.getNickname()).setAvatar(authUser.getAvatar()).setRawUserInfo(toJsonString(authUser.getRawUserInfo()));
81 if (socialUser.getId() == null) { 83 if (socialUser.getId() == null) {
82 socialUserMapper.insert(socialUser); 84 socialUserMapper.insert(socialUser);
83 } else { 85 } else {
@@ -88,7 +90,15 @@ public class SocialUserServiceImpl implements SocialUserService { @@ -88,7 +90,15 @@ public class SocialUserServiceImpl implements SocialUserService {
88 90
89 @Override 91 @Override
90 public List<SocialUserDO> getSocialUserList(Long userId, Integer userType) { 92 public List<SocialUserDO> getSocialUserList(Long userId, Integer userType) {
91 - return socialUserMapper.selectListByUserId(userType, userId); 93 + // 获得绑定
  94 + List<SocialUserBindDO> socialUserBinds = socialUserBindMapper.selectListByUserIdAndUserType(userId, userType);
  95 + if (CollUtil.isEmpty(socialUserBinds)) {
  96 + return Collections.emptyList();
  97 + }
  98 + // 获得社交用户
  99 + Set<Integer> platforms = CollectionUtils.convertSet(socialUserBinds, SocialUserBindDO::getPlatform);
  100 + return socialUserMapper.selectListByUnionIdAndType(CollectionUtils.convertSet(socialUserBinds, SocialUserBindDO::getUnionId),
  101 + SocialTypeEnum.getTypes(platforms));
92 } 102 }
93 103
94 @Override 104 @Override
@@ -98,13 +108,19 @@ public class SocialUserServiceImpl implements SocialUserService { @@ -98,13 +108,19 @@ public class SocialUserServiceImpl implements SocialUserService {
98 SocialUserDO socialUser = authSocialUser(reqDTO.getType(), reqDTO.getCode(), reqDTO.getState()); 108 SocialUserDO socialUser = authSocialUser(reqDTO.getType(), reqDTO.getCode(), reqDTO.getState());
99 Assert.notNull(socialUser, "社交用户不能为空"); 109 Assert.notNull(socialUser, "社交用户不能为空");
100 110
  111 + // 如果 unionId 之前被绑定过,需要进行解绑
  112 + Integer platform = SocialTypeEnum.valueOfType(socialUser.getType()).getPlatform();
  113 + socialUserBindMapper.deleteByUserTypeAndPlatformAndUnionId(reqDTO.getUserType(), platform,
  114 + socialUser.getUnionId());
  115 +
101 // 如果 userId 之前绑定过该 type 的其它账号,需要进行解绑 116 // 如果 userId 之前绑定过该 type 的其它账号,需要进行解绑
102 - socialUserBindMapper.deleteByUserTypeAndUserIdAndPlatformAndUnionId(reqDTO.getUserType(), reqDTO.getUserId(),  
103 - SocialTypeEnum.valueOfType(socialUser.getType()).getPlatform(), socialUser.getUnionId()); 117 + socialUserBindMapper.deleteByUserTypeAndUserIdAndUnionId(reqDTO.getUserType(), reqDTO.getUserId(),
  118 + socialUser.getUnionId());
104 119
105 // 绑定当前登录的社交用户 120 // 绑定当前登录的社交用户
106 - SocialUserBindDO socialUserBind = SocialUserBindDO.builder().userId(reqDTO.getUserId()).userType(reqDTO.getUserType())  
107 - .unionId(socialUser.getUnionId()).build(); 121 + SocialUserBindDO socialUserBind = SocialUserBindDO.builder()
  122 + .userId(reqDTO.getUserId()).userType(reqDTO.getUserType())
  123 + .platform(platform).unionId(socialUser.getUnionId()).build();
108 socialUserBindMapper.insert(socialUserBind); 124 socialUserBindMapper.insert(socialUserBind);
109 } 125 }
110 126
@@ -140,22 +156,20 @@ public class SocialUserServiceImpl implements SocialUserService { @@ -140,22 +156,20 @@ public class SocialUserServiceImpl implements SocialUserService {
140 * 请求社交平台,获得授权的用户 156 * 请求社交平台,获得授权的用户
141 * 157 *
142 * @param type 社交平台的类型 158 * @param type 社交平台的类型
143 - * @param authCallback 授权回调 159 + * @param code 授权码
  160 + * @param state 授权 state
144 * @return 授权的用户 161 * @return 授权的用户
145 */ 162 */
146 - private AuthUser getAuthUser(Integer type, AuthCallback authCallback) { 163 + private AuthUser getAuthUser(Integer type, String code, String state) {
147 AuthRequest authRequest = authRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource()); 164 AuthRequest authRequest = authRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource());
  165 + AuthCallback authCallback = AuthCallback.builder().code(code).state(state).build();
148 AuthResponse<?> authResponse = authRequest.login(authCallback); 166 AuthResponse<?> authResponse = authRequest.login(authCallback);
149 - log.info("[getAuthUser0][请求社交平台 type({}) request({}) response({})]", type, toJsonString(authCallback),  
150 - toJsonString(authResponse)); 167 + log.info("[getAuthUser][请求社交平台 type({}) request({}) response({})]", type,
  168 + toJsonString(authCallback), toJsonString(authResponse));
151 if (!authResponse.ok()) { 169 if (!authResponse.ok()) {
152 throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg()); 170 throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg());
153 } 171 }
154 return (AuthUser) authResponse.getData(); 172 return (AuthUser) authResponse.getData();
155 } 173 }
156 174
157 - private static AuthCallback buildAuthCallback(String code, String state) {  
158 - return AuthCallback.builder().code(code).state(state).build();  
159 - }  
160 -  
161 } 175 }
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceTest.java
1 package cn.iocoder.yudao.module.system.service.social; 1 package cn.iocoder.yudao.module.system.service.social;
2 2
  3 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  4 +import cn.iocoder.yudao.framework.test.core.util.AssertUtils;
  5 +import cn.iocoder.yudao.framework.test.core.util.RandomUtils;
  6 +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
  9 +import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper;
3 import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper; 10 import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper;
4 import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; 11 import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest;
  12 +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
5 import com.baomidou.mybatisplus.core.toolkit.PluginUtils; 13 import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
6 import com.xkcoding.justauth.AuthRequestFactory; 14 import com.xkcoding.justauth.AuthRequestFactory;
  15 +import me.zhyd.oauth.enums.AuthResponseStatus;
  16 +import me.zhyd.oauth.model.AuthCallback;
  17 +import me.zhyd.oauth.model.AuthResponse;
  18 +import me.zhyd.oauth.model.AuthUser;
7 import me.zhyd.oauth.request.AuthRequest; 19 import me.zhyd.oauth.request.AuthRequest;
8 import me.zhyd.oauth.utils.AuthStateUtils; 20 import me.zhyd.oauth.utils.AuthStateUtils;
9 import org.junit.jupiter.api.Test; 21 import org.junit.jupiter.api.Test;
  22 +import org.junit.jupiter.api.function.Executable;
10 import org.mockito.MockedStatic; 23 import org.mockito.MockedStatic;
11 import org.springframework.boot.test.mock.mockito.MockBean; 24 import org.springframework.boot.test.mock.mockito.MockBean;
12 import org.springframework.context.annotation.Import; 25 import org.springframework.context.annotation.Import;
13 26
14 import javax.annotation.Resource; 27 import javax.annotation.Resource;
15 28
16 -import static cn.hutool.core.util.RandomUtil.randomEle;  
17 -import static cn.hutool.core.util.RandomUtil.randomString; 29 +import java.util.List;
  30 +
  31 +import static cn.hutool.core.util.RandomUtil.*;
  32 +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
  33 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  34 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
18 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; 35 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  36 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_AUTH_FAILURE;
  37 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND;
19 import static org.junit.jupiter.api.Assertions.assertEquals; 38 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.mockito.Mockito.*; 39 import static org.mockito.Mockito.*;
21 40
@@ -27,6 +46,8 @@ public class SocialUserServiceTest extends BaseDbAndRedisUnitTest { @@ -27,6 +46,8 @@ public class SocialUserServiceTest extends BaseDbAndRedisUnitTest {
27 46
28 @Resource 47 @Resource
29 private SocialUserMapper socialUserMapper; 48 private SocialUserMapper socialUserMapper;
  49 + @Resource
  50 + private SocialUserBindMapper socialUserBindMapper;
30 51
31 @MockBean 52 @MockBean
32 private AuthRequestFactory authRequestFactory; 53 private AuthRequestFactory authRequestFactory;
@@ -35,7 +56,7 @@ public class SocialUserServiceTest extends BaseDbAndRedisUnitTest { @@ -35,7 +56,7 @@ public class SocialUserServiceTest extends BaseDbAndRedisUnitTest {
35 public void testGetAuthorizeUrl() { 56 public void testGetAuthorizeUrl() {
36 try (MockedStatic<AuthStateUtils> authStateUtilsMock = mockStatic(AuthStateUtils.class)) { 57 try (MockedStatic<AuthStateUtils> authStateUtilsMock = mockStatic(AuthStateUtils.class)) {
37 // 准备参数 58 // 准备参数
38 - Integer type = 31; 59 + Integer type = SocialTypeEnum.WECHAT_MP.getType();
39 String redirectUri = "sss"; 60 String redirectUri = "sss";
40 // mock 获得对应的 AuthRequest 实现 61 // mock 获得对应的 AuthRequest 实现
41 AuthRequest authRequest = mock(AuthRequest.class); 62 AuthRequest authRequest = mock(AuthRequest.class);
@@ -51,25 +72,138 @@ public class SocialUserServiceTest extends BaseDbAndRedisUnitTest { @@ -51,25 +72,138 @@ public class SocialUserServiceTest extends BaseDbAndRedisUnitTest {
51 } 72 }
52 } 73 }
53 74
54 -// /**  
55 -// * 情况一,创建 SocialUserDO 的情况  
56 -// */  
57 -// @Test  
58 -// public void testBindSocialUser_create() {  
59 -// // mock 数据  
60 -// // 准备参数  
61 -// Long userId = randomLongId();  
62 -// Integer type = randomEle(SocialTypeEnum.values()).getType();  
63 -// AuthUser authUser = randomPojo(AuthUser.class);  
64 -// // mock 方法  
65 -//  
66 -// // 调用  
67 -// socialService.bindSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, authUser);  
68 -// // 断言  
69 -// List<SocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId);  
70 -// assertEquals(1, socialUsers.size());  
71 -// assertBindSocialUser(socialUsers.get(0), authUser, userId, type);  
72 -// } 75 + @Test
  76 + public void testAuthSocialUser_exists() {
  77 + // 准备参数
  78 + Integer type = SocialTypeEnum.GITEE.getType();
  79 + String code = "tudou";
  80 + String state = "yuanma";
  81 + // mock 方法
  82 + SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state);
  83 + socialUserMapper.insert(socialUser);
  84 +
  85 + // 调用
  86 + SocialUserDO result = socialUserService.authSocialUser(type, code, state);
  87 + // 断言
  88 + assertPojoEquals(socialUser, result);
  89 + }
  90 +
  91 + @Test
  92 + public void testAuthSocialUser_authFailure() {
  93 + // 准备参数
  94 + Integer type = SocialTypeEnum.GITEE.getType();
  95 + // mock 方法
  96 + AuthRequest authRequest = mock(AuthRequest.class);
  97 + when(authRequestFactory.get(anyString())).thenReturn(authRequest);
  98 + AuthResponse<?> authResponse = new AuthResponse<>(0, "模拟失败", null);
  99 + when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse);
  100 +
  101 + // 调用并断言
  102 + assertServiceException(
  103 + () -> socialUserService.authSocialUser(type, randomString(10), randomString(10)),
  104 + SOCIAL_USER_AUTH_FAILURE, "模拟失败");
  105 + }
  106 +
  107 + @Test
  108 + public void testAuthSocialUser_insert() {
  109 + // 准备参数
  110 + Integer type = SocialTypeEnum.GITEE.getType();
  111 + String code = "tudou";
  112 + String state = "yuanma";
  113 + // mock 方法
  114 + AuthRequest authRequest = mock(AuthRequest.class);
  115 + when(authRequestFactory.get(eq(SocialTypeEnum.GITEE.getSource()))).thenReturn(authRequest);
  116 + AuthUser authUser = randomPojo(AuthUser.class);
  117 + AuthResponse<AuthUser> authResponse = new AuthResponse<>(AuthResponseStatus.SUCCESS.getCode(), null, authUser);
  118 + when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse);
  119 +
  120 + // 调用
  121 + SocialUserDO result = socialUserService.authSocialUser(type, code, state);
  122 + // 断言
  123 + assertBindSocialUser(type, result, authResponse.getData());
  124 + assertEquals(code, result.getCode());
  125 + assertEquals(state, result.getState());
  126 + }
  127 +
  128 + @Test
  129 + public void testAuthSocialUser_update() {
  130 + // 准备参数
  131 + Integer type = SocialTypeEnum.GITEE.getType();
  132 + String code = "tudou";
  133 + String state = "yuanma";
  134 + // mock 数据
  135 + socialUserMapper.insert(randomPojo(SocialUserDO.class).setType(type).setOpenid("test_openid"));
  136 + // mock 方法
  137 + AuthRequest authRequest = mock(AuthRequest.class);
  138 + when(authRequestFactory.get(eq(SocialTypeEnum.GITEE.getSource()))).thenReturn(authRequest);
  139 + AuthUser authUser = randomPojo(AuthUser.class);
  140 + authUser.getToken().setOpenId("test_openid");
  141 + AuthResponse<AuthUser> authResponse = new AuthResponse<>(AuthResponseStatus.SUCCESS.getCode(), null, authUser);
  142 + when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse);
  143 +
  144 + // 调用
  145 + SocialUserDO result = socialUserService.authSocialUser(type, code, state);
  146 + // 断言
  147 + assertBindSocialUser(type, result, authResponse.getData());
  148 + assertEquals(code, result.getCode());
  149 + assertEquals(state, result.getState());
  150 + }
  151 +
  152 + private void assertBindSocialUser(Integer type, SocialUserDO socialUser, AuthUser authUser) {
  153 + assertEquals(authUser.getToken().getAccessToken(), socialUser.getToken());
  154 + assertEquals(toJsonString(authUser.getToken()), socialUser.getRawTokenInfo());
  155 + assertEquals(authUser.getNickname(), socialUser.getNickname());
  156 + assertEquals(authUser.getAvatar(), socialUser.getAvatar());
  157 + assertEquals(toJsonString(authUser.getRawUserInfo()), socialUser.getRawUserInfo());
  158 + assertEquals(type, socialUser.getType());
  159 + assertEquals(authUser.getUuid(), socialUser.getOpenid());
  160 + assertEquals(authUser.getToken().getUnionId(), socialUser.getUnionId());
  161 + }
  162 +
  163 + @Test
  164 + public void testGetSocialUserList() {
  165 + Long userId = 1L;
  166 + Integer userType = UserTypeEnum.ADMIN.getValue();
  167 + // mock 获得绑定
  168 + socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class) // 可被查询到
  169 + .setUserId(userId).setUserType(userType).setPlatform(SocialTypeEnum.GITEE.getPlatform())
  170 + .setUnionId("test_unionId"));
  171 + socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class) // 不可被查询到
  172 + .setUserId(2L).setUserType(userType).setPlatform(SocialTypeEnum.DINGTALK.getPlatform()));
  173 + // mock 获得社交用户
  174 + SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(SocialTypeEnum.GITEE.getType())
  175 + .setUnionId("test_unionId");
  176 + socialUserMapper.insert(socialUser); // 可被查到
  177 + socialUserMapper.insert(randomPojo(SocialUserDO.class)); // 不可被查到
  178 +
  179 + // 调用
  180 + List<SocialUserDO> result = socialUserService.getSocialUserList(userId, userType);
  181 + // 断言
  182 + assertEquals(1, result.size());
  183 + assertPojoEquals(socialUser, result.get(0));
  184 + }
  185 +
  186 + @Test
  187 + public void testBindSocialUser_create() {
  188 + // 准备参数
  189 + SocialUserBindReqDTO reqDTO = new SocialUserBindReqDTO()
  190 + .setUserId(1L).setUserType(UserTypeEnum.ADMIN.getValue())
  191 + .setType(SocialTypeEnum.GITEE.getType()).setCode("test_code").setState("test_state");
  192 + // mock 数据:获得社交用户
  193 + SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(reqDTO.getType())
  194 + .setCode(reqDTO.getCode()).setState(reqDTO.getState()).setUnionId("test_unionId");
  195 + socialUserMapper.insert(socialUser);
  196 + // mock 数据:解绑其它账号
  197 + socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class).setUserId(1L).setUserType(UserTypeEnum.ADMIN.getValue())
  198 + .setPlatform(SocialTypeEnum.GITEE.getPlatform()).setUnionId("test_delete_unionId"));
  199 +
  200 + // 调用
  201 + socialUserService.bindSocialUser(reqDTO);
  202 + // 断言
  203 + List<SocialUserBindDO> socialUserBinds = socialUserBindMapper.selectList();
  204 + assertEquals(1, socialUserBinds.size());
  205 +
  206 + }
73 // 207 //
74 // /** 208 // /**
75 // * 情况二,更新 SocialUserDO 的情况 209 // * 情况二,更新 SocialUserDO 的情况
@@ -119,20 +253,7 @@ public class SocialUserServiceTest extends BaseDbAndRedisUnitTest { @@ -119,20 +253,7 @@ public class SocialUserServiceTest extends BaseDbAndRedisUnitTest {
119 // List<SocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId); 253 // List<SocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId);
120 // assertEquals(1, socialUsers.size()); 254 // assertEquals(1, socialUsers.size());
121 // } 255 // }
122 -//  
123 -// private void assertBindSocialUser(SocialUserDO socialUser, AuthUser authUser, Long userId,  
124 -// Integer type) {  
125 -// assertEquals(authUser.getToken().getAccessToken(), socialUser.getToken());  
126 -// assertEquals(toJsonString(authUser.getToken()), socialUser.getRawTokenInfo());  
127 -// assertEquals(authUser.getNickname(), socialUser.getNickname());  
128 -// assertEquals(authUser.getAvatar(), socialUser.getAvatar());  
129 -// assertEquals(toJsonString(authUser.getRawUserInfo()), socialUser.getRawUserInfo());  
130 -// assertEquals(userId, socialUser.getUserId());  
131 -// assertEquals(UserTypeEnum.ADMIN.getValue(), socialUser.getUserType());  
132 -// assertEquals(type, socialUser.getType());  
133 -// assertEquals(authUser.getUuid(), socialUser.getOpenid());  
134 -// assertEquals(socialService.getAuthUserUnionId(authUser), socialUser.getUnionId());  
135 -// } 256 +
136 // 257 //
137 // /** 258 // /**
138 // * 情况一,如果新老的 unionId 是一致的,无需解绑 259 // * 情况一,如果新老的 unionId 是一致的,无需解绑
yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql
@@ -15,6 +15,7 @@ DELETE FROM &quot;system_sms_template&quot;; @@ -15,6 +15,7 @@ DELETE FROM &quot;system_sms_template&quot;;
15 DELETE FROM "system_sms_log"; 15 DELETE FROM "system_sms_log";
16 DELETE FROM "system_error_code"; 16 DELETE FROM "system_error_code";
17 DELETE FROM "system_social_user"; 17 DELETE FROM "system_social_user";
  18 +DELETE FROM "system_social_user_bind";
18 DELETE FROM "system_tenant"; 19 DELETE FROM "system_tenant";
19 DELETE FROM "system_tenant_package"; 20 DELETE FROM "system_tenant_package";
20 DELETE FROM "system_sensitive_word"; 21 DELETE FROM "system_sensitive_word";
yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql
@@ -378,16 +378,16 @@ CREATE TABLE IF NOT EXISTS &quot;system_error_code&quot; ( @@ -378,16 +378,16 @@ CREATE TABLE IF NOT EXISTS &quot;system_error_code&quot; (
378 378
379 CREATE TABLE IF NOT EXISTS "system_social_user" ( 379 CREATE TABLE IF NOT EXISTS "system_social_user" (
380 "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY, 380 "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
381 - "user_id" bigint NOT NULL,  
382 - "user_type" tinyint NOT NULL DEFAULT '0',  
383 "type" tinyint NOT NULL, 381 "type" tinyint NOT NULL,
384 - "openid" varchar(32) NOT NULL, 382 + "openid" varchar(64) NOT NULL,
385 "token" varchar(256) DEFAULT NULL, 383 "token" varchar(256) DEFAULT NULL,
386 - "union_id" varchar(32) NOT NULL, 384 + "union_id" varchar(64) NOT NULL,
387 "raw_token_info" varchar(1024) NOT NULL, 385 "raw_token_info" varchar(1024) NOT NULL,
388 "nickname" varchar(32) NOT NULL, 386 "nickname" varchar(32) NOT NULL,
389 "avatar" varchar(255) DEFAULT NULL, 387 "avatar" varchar(255) DEFAULT NULL,
390 "raw_user_info" varchar(1024) NOT NULL, 388 "raw_user_info" varchar(1024) NOT NULL,
  389 + "code" varchar(64) NOT NULL,
  390 + "state" varchar(64),
391 "creator" varchar(64) DEFAULT '', 391 "creator" varchar(64) DEFAULT '',
392 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 392 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
393 "updater" varchar(64) DEFAULT '', 393 "updater" varchar(64) DEFAULT '',
@@ -396,6 +396,20 @@ CREATE TABLE IF NOT EXISTS &quot;system_social_user&quot; ( @@ -396,6 +396,20 @@ CREATE TABLE IF NOT EXISTS &quot;system_social_user&quot; (
396 PRIMARY KEY ("id") 396 PRIMARY KEY ("id")
397 ) COMMENT '社交用户'; 397 ) COMMENT '社交用户';
398 398
  399 +CREATE TABLE IF NOT EXISTS "system_social_user_bind" (
  400 + "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  401 + "user_id" bigint NOT NULL,
  402 + "user_type" tinyint NOT NULL DEFAULT '0',
  403 + "platform" tinyint NOT NULL,
  404 + "union_id" varchar(64) NOT NULL,
  405 + "creator" varchar(64) DEFAULT '',
  406 + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  407 + "updater" varchar(64) DEFAULT '',
  408 + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  409 + "deleted" bit NOT NULL DEFAULT FALSE,
  410 + PRIMARY KEY ("id")
  411 +) COMMENT '社交用户的绑定';
  412 +
399 CREATE TABLE IF NOT EXISTS "system_tenant" ( 413 CREATE TABLE IF NOT EXISTS "system_tenant" (
400 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, 414 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
401 "name" varchar(63) NOT NULL, 415 "name" varchar(63) NOT NULL,