From fc5d43e590ac5453f1e1f96fcf84f558f383ca40 Mon Sep 17 00:00:00 2001
From: baoshiwei <baoshiwei@shlanbao.cn>
Date: 星期五, 21 三月 2025 14:18:21 +0800
Subject: [PATCH] feat(social): 添加 Keycloak单点登录功能

---
 eims/ruoyi-admin/src/main/resources/application-dev.yml                                                                          |   10 +
 eims-ui/apps/web-antd/src/views/_core/authentication/login.vue                                                                   |    6 
 eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/keycloak/AuthKeycloakSource.java                   |   36 ++++++
 eims/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysSocialService.java                                  |    1 
 eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/keycloak/AuthKeycloakRequest.java                  |  144 ++++++++++++++++++++++++
 eims/ruoyi-admin/src/main/resources/application-prod.yml                                                                         |   10 +
 eims-ui/apps/web-antd/src/views/_core/social-callback/index.vue                                                                  |    2 
 eims/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java                                              |   44 +++++++
 eims-ui/apps/web-antd/src/views/_core/oauth-common.ts                                                                            |   10 +
 eims/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java                                                      |   29 ++++
 eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java |    3 
 eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java                             |    2 
 eims/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java                          |    7 +
 13 files changed, 300 insertions(+), 4 deletions(-)

diff --git a/eims-ui/apps/web-antd/src/views/_core/authentication/login.vue b/eims-ui/apps/web-antd/src/views/_core/authentication/login.vue
index d68624c..4652260 100644
--- a/eims-ui/apps/web-antd/src/views/_core/authentication/login.vue
+++ b/eims-ui/apps/web-antd/src/views/_core/authentication/login.vue
@@ -8,7 +8,7 @@
 
 import { omit } from 'lodash-es';
 
-import { tenantList, type TenantResp } from '#/api';
+import { authBinding, tenantList, type TenantResp } from '#/api';
 import { captchaImage, type CaptchaResponse } from '#/api/core/captcha';
 import { useAuthStore } from '#/store';
 
@@ -50,7 +50,11 @@
 }
 
 onMounted(async () => {
+  // 鍚姩鍗曠偣鐧诲綍娉ㄩ噴鎺変笅杈硅繖涓�琛岋紝鍚﹀垯鏀惧紑
   await Promise.all([loadCaptcha(), loadTenant()]);
+  // 鍚姩鍗曠偣鐧诲綍鏀惧紑涓嬭竟涓よ娉ㄩ噴锛屽惁鍒欐敞閲婃帀
+  // const href = await authBinding('keycloak', '000000');
+  // window.location.href = href;
 });
 
 const formSchema = computed((): VbenFormSchema[] => {
diff --git a/eims-ui/apps/web-antd/src/views/_core/oauth-common.ts b/eims-ui/apps/web-antd/src/views/_core/oauth-common.ts
index 6627442..dc7d50f 100644
--- a/eims-ui/apps/web-antd/src/views/_core/oauth-common.ts
+++ b/eims-ui/apps/web-antd/src/views/_core/oauth-common.ts
@@ -59,6 +59,7 @@
  * action涓嶄负绌虹殑浼氬湪鐧诲綍椤垫樉绀�
  */
 export const accountBindList: BindItem[] = [
+
   {
     avatar: TaobaoIcon,
     color: '#ff4000',
@@ -101,4 +102,13 @@
     source: 'github',
     title: 'GITHUB',
   },
+  {
+    action: () => handleAuthBinding('keycloak'),
+    avatar: TaobaoIcon,
+    color: '#ff4000',
+    description: 'keycloak鐧诲綍',
+    key: '6',
+    source: 'keycloak',
+    title: 'keycloak',
+  },
 ];
diff --git a/eims-ui/apps/web-antd/src/views/_core/social-callback/index.vue b/eims-ui/apps/web-antd/src/views/_core/social-callback/index.vue
index ad7135f..b343283 100644
--- a/eims-ui/apps/web-antd/src/views/_core/social-callback/index.vue
+++ b/eims-ui/apps/web-antd/src/views/_core/social-callback/index.vue
@@ -18,7 +18,7 @@
 const state = route.query.state as string;
 const stateJson = JSON.parse(atob(state));
 // 鏉ユ簮
-const source = route.query.source as string;
+const source = 'keycloak';
 // 绉熸埛ID
 const defaultTenantId = '000000';
 const tenantId = (stateJson.tenantId as string) ?? defaultTenantId;
diff --git a/eims/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java b/eims/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java
index c7ad917..d99a2fd 100644
--- a/eims/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java
+++ b/eims/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java
@@ -6,6 +6,8 @@
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Opt;
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
 import com.baomidou.lock.annotation.Lock4j;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -48,6 +50,12 @@
 @Slf4j
 @Service
 public class SysLoginService {
+
+    @Value("${justauth.type.keycloak.server-url}")
+    private String keycloakServerUrl;
+
+    @Value("${justauth.type.keycloak.realm}")
+    private String keycloakRealm;
 
     @Value("${user.password.maxRetryCount}")
     private Integer maxRetryCount;
@@ -116,6 +124,27 @@
                 TenantHelper.clearDynamic();
             }
             recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"));
+
+            // 鏂板Keycloak鐧诲嚭閫昏緫
+
+            Long userId = loginUser.getUserId();
+
+            SysSocialVo social = sysSocialService.selectByUserId(userId);
+            if (social == null) {
+                return;
+            }
+
+            String logoutUrl = keycloakServerUrl + "/realms/" + keycloakRealm + "/protocol/openid-connect/logout";
+            HttpRequest request = HttpRequest.get(logoutUrl)
+                .form("refresh_token", social.getRefreshToken())
+                .form("id_token_hint", social.getIdToken());
+
+
+            HttpResponse response = request.execute();
+            if (response.isOk()) {
+                System.out.println("1234");
+            }
+
         } catch (NotLoginException ignored) {
         } finally {
             try {
diff --git a/eims/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java b/eims/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java
index 8463026..ef2caf8 100644
--- a/eims/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java
+++ b/eims/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java
@@ -7,6 +7,7 @@
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.HttpUtil;
 import cn.hutool.http.Method;
+import com.aizuda.snailjob.common.core.constant.SystemConstants;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import me.zhyd.oauth.model.AuthResponse;
@@ -23,6 +24,8 @@
 import org.dromara.common.social.config.properties.SocialProperties;
 import org.dromara.common.social.utils.SocialUtils;
 import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.system.domain.SysUser;
+import org.dromara.system.domain.bo.SysSocialBo;
 import org.dromara.system.domain.vo.SysClientVo;
 import org.dromara.system.domain.vo.SysSocialVo;
 import org.dromara.system.domain.vo.SysUserVo;
@@ -77,6 +80,47 @@
                     .formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
                     .executeAsync();
         }
+        if ("KEYCLOAK".equals(authUserData.getSource())) {
+            // 鏂板KEYCLOAK鐢ㄦ埛鑷姩鍒涘缓閫昏緫
+            String authId = authUserData.getSource() + authUserData.getUuid();
+            List<SysSocialVo> list = sysSocialService.selectByAuthId(authId);
+            if (CollUtil.isEmpty(list)) {
+                // 鑷姩鍒涘缓鏂扮敤鎴�
+                SysUser newUser = new SysUser();
+                newUser.setUserName(authUserData.getUsername());
+                newUser.setEmail(authUserData.getEmail());
+                newUser.setNickName(authUserData.getNickname());
+                newUser.setPassword("Initial123@"); // 鍒濆瀵嗙爜闇�绗﹀悎瀹夊叏绛栫暐
+                newUser.setStatus("0");
+
+                userMapper.insert(newUser); // 鍋囪瀛樺湪鎻掑叆鏂规硶
+
+                // 鍒涘缓绀句氦缁戝畾璁板綍
+                SysSocialBo newSocial = new SysSocialBo();
+                newSocial.setUserId(newUser.getUserId());
+                newSocial.setUserName(newUser.getUserName());
+                newSocial.setAuthId(authId);
+                newSocial.setSource(authUserData.getSource());
+                newSocial.setTenantId(newUser.getTenantId());
+                newSocial.setOpenId(authUserData.getUuid());
+                newSocial.setAccessToken(authUserData.getToken().getAccessToken());
+                newSocial.setRefreshToken(authUserData.getToken().getRefreshToken());
+                newSocial.setIdToken(authUserData.getToken().getIdToken());
+                sysSocialService.insertByBo(newSocial); // 闇�纭繚鏈嶅姟鏈夋柊澧炴柟娉�
+
+                // 閲嶆柊鏌ヨ纭繚鏁版嵁鍙敤
+                list = sysSocialService.selectByAuthId(authId);
+            } else {
+                // 鏇存柊绀句氦缁戝畾璁板綍
+                SysSocialBo socialBo = new SysSocialBo();
+                socialBo.setId(list.get(0).getId());
+                socialBo.setAccessToken(authUserData.getToken().getAccessToken());
+                socialBo.setRefreshToken(authUserData.getToken().getRefreshToken());
+                socialBo.setIdToken(authUserData.getToken().getIdToken());
+                sysSocialService.updateByBo(socialBo);
+
+            }
+        }
 
         List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
         if (CollUtil.isEmpty(list)) {
diff --git a/eims/ruoyi-admin/src/main/resources/application-dev.yml b/eims/ruoyi-admin/src/main/resources/application-dev.yml
index a2da72f..cd1faed 100644
--- a/eims/ruoyi-admin/src/main/resources/application-dev.yml
+++ b/eims/ruoyi-admin/src/main/resources/application-dev.yml
@@ -187,8 +187,16 @@
 --- # 涓夋柟鎺堟潈
 justauth:
   # 鍓嶇澶栫綉璁块棶鍦板潃
-  address: http://localhost:80
+  address: http://192.168.12.236:80
   type:
+    keycloak:
+      # keycloak 鏈嶅姟鍣ㄥ湴鍧�
+      server-url: https://lanbaosystem.shlanbao.cn:8443
+      realm: lanbao
+      client-id: DataCapture
+      client-secret: kplisa4lJHEIM6knqefVbxln85QbA5NX
+      redirect-uri: ${justauth.address}/social-callback
+      scopes: [ openid, email, phone, profile ]
     maxkey:
       # maxkey 鏈嶅姟鍣ㄥ湴鍧�
       # 娉ㄦ剰 濡備笅鍧囬厤缃潎涓嶉渶瑕佷慨鏀� maxkey 宸茬粡鍐呯疆濂戒簡鏁版嵁
diff --git a/eims/ruoyi-admin/src/main/resources/application-prod.yml b/eims/ruoyi-admin/src/main/resources/application-prod.yml
index 2823bba..ee68d30 100644
--- a/eims/ruoyi-admin/src/main/resources/application-prod.yml
+++ b/eims/ruoyi-admin/src/main/resources/application-prod.yml
@@ -189,8 +189,16 @@
 --- # 涓夋柟鎺堟潈
 justauth:
   # 鍓嶇澶栫綉璁块棶鍦板潃
-  address: http://localhost:80
+  address: http://192.168.0.24:80
   type:
+    keycloak:
+      # keycloak 鏈嶅姟鍣ㄥ湴鍧�
+      server-url: https://lanbaosystem.shlanbao.cn:8443
+      realm: lanbao
+      client-id: DataCapture
+      client-secret: kplisa4lJHEIM6knqefVbxln85QbA5NX
+      redirect-uri: ${justauth.address}/social-callback
+      scopes: [ openid, email, phone, profile ]
     maxkey:
       # maxkey 鏈嶅姟鍣ㄥ湴鍧�
       # 娉ㄦ剰 濡備笅鍧囬厤缃潎涓嶉渶瑕佷慨鏀� maxkey 宸茬粡鍐呯疆濂戒簡鏁版嵁
diff --git a/eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java b/eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java
index 5f49d9c..ec0131a 100644
--- a/eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java
+++ b/eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java
@@ -72,4 +72,7 @@
      */
     private List<String> scopes;
 
+    private String realm;
+
+
 }
diff --git a/eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/keycloak/AuthKeycloakRequest.java b/eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/keycloak/AuthKeycloakRequest.java
new file mode 100644
index 0000000..f3da8a1
--- /dev/null
+++ b/eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/keycloak/AuthKeycloakRequest.java
@@ -0,0 +1,144 @@
+package org.dromara.common.social.keycloak;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import com.xkcoding.http.support.HttpHeader;
+import me.zhyd.oauth.cache.AuthStateCache;
+import me.zhyd.oauth.config.AuthConfig;
+import me.zhyd.oauth.exception.AuthException;
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthToken;
+import me.zhyd.oauth.model.AuthUser;
+import me.zhyd.oauth.request.AuthDefaultRequest;
+import me.zhyd.oauth.utils.HttpUtils;
+import me.zhyd.oauth.utils.UrlBuilder;
+import org.dromara.common.core.domain.model.PasswordLoginBody;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.json.utils.JsonUtils;
+
+/**
+ * Keycloak OAuth2 璁よ瘉璇锋眰
+ */
+public class AuthKeycloakRequest extends AuthDefaultRequest {
+
+    public static final String SERVER_URL = SpringUtils.getProperty("justauth.type.keycloak.server-url");
+    public static final String REALM = SpringUtils.getProperty("justauth.type.keycloak.realm");
+
+    public AuthKeycloakRequest(AuthConfig config) {
+        super(config, AuthKeycloakSource.KEYCLOAK);
+    }
+
+    public AuthKeycloakRequest(AuthConfig config, AuthStateCache authStateCache) {
+        super(config, AuthKeycloakSource.KEYCLOAK, authStateCache);
+    }
+
+
+    public  AuthToken getAccessToken(PasswordLoginBody loginBody) {
+
+        HttpRequest request = HttpRequest.post(SERVER_URL + "/realms/" + REALM + "/protocol/openid-connect/token")
+            .form("grant_type", "password")
+            .form("client_id", config.getClientId())
+            .form("client_secret", config.getClientSecret())
+            .form("username", loginBody.getUsername())
+            .form("password", loginBody.getPassword())
+            .form("scope", "openid");
+
+        HttpResponse response = request.execute();
+
+        Dict object = JsonUtils.parseMap(response.body());
+
+        if (object.containsKey("error")) {
+            throw new AuthException(object.getStr("error_description"));
+        }
+        if (object.containsKey("message")) {
+            throw new AuthException(object.getStr("message"));
+        }
+        return AuthToken.builder()
+            .accessToken(object.getStr("access_token"))
+            .refreshToken(object.getStr("refresh_token"))
+            .idToken(object.getStr("id_token"))
+            .tokenType(object.getStr("token_type"))
+            .expireIn(object.getInt("expires_in"))
+            .build();
+    }
+
+    @Override
+    public AuthToken getAccessToken(AuthCallback authCallback) {
+        String body = doPostAuthorizationCode(authCallback.getCode());
+        Dict object = JsonUtils.parseMap(body);
+
+        if (object.containsKey("error")) {
+            throw new AuthException(object.getStr("error_description"));
+        }
+        if (object.containsKey("message")) {
+            throw new AuthException(object.getStr("message"));
+        }
+
+        return AuthToken.builder()
+            .accessToken(object.getStr("access_token"))
+            .refreshToken(object.getStr("refresh_token"))
+            .idToken(object.getStr("id_token"))
+            .tokenType(object.getStr("token_type"))
+            .expireIn(object.getInt("expires_in"))
+            .build();
+    }
+
+    @Override
+    public AuthUser getUserInfo(AuthToken authToken) {
+        String body = doGetUserInfo(authToken);
+        Dict object = JsonUtils.parseMap(body);
+
+        if (object.containsKey("error")) {
+            throw new AuthException(object.getStr("error_description"));
+        }
+        if (object.containsKey("message")) {
+            throw new AuthException(object.getStr("message"));
+        }
+
+        return AuthUser.builder()
+            .uuid(object.getStr("sub"))
+            .username(object.getStr("preferred_username"))
+            .nickname(object.getStr("name"))
+            .email(object.getStr("email"))
+            .token(authToken)
+            .source(this.source.toString())
+            .build();
+    }
+
+    @Override
+    protected String doPostAuthorizationCode(String code) {
+        HttpRequest request = HttpRequest.post(source.accessToken())
+            .header("Authorization", "Basic " + Base64.encode("%s:%s".formatted(config.getClientId(), config.getClientSecret())))
+            .form("grant_type", "authorization_code")
+            .form("code", code)
+            .form("redirect_uri", config.getRedirectUri());
+        HttpResponse response = request.execute();
+        return response.body();
+    }
+
+    @Override
+    protected String doGetUserInfo(AuthToken authToken) {
+        try {
+            return new HttpUtils(config.getHttpConfig()).get(source.userInfo(), null, new HttpHeader()
+                .add("Content-Type", "application/json")
+                .add("Authorization", "Bearer " + authToken.getAccessToken()), false).getBody();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    public String authorize(String state) {
+        return UrlBuilder.fromBaseUrl(super.authorize(state))
+            .queryParam("scope", StrUtil.join("%20", config.getScopes()))
+            .build();
+    }
+
+
+
+
+}
diff --git a/eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/keycloak/AuthKeycloakSource.java b/eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/keycloak/AuthKeycloakSource.java
new file mode 100644
index 0000000..e232c34
--- /dev/null
+++ b/eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/keycloak/AuthKeycloakSource.java
@@ -0,0 +1,36 @@
+package org.dromara.common.social.keycloak;
+
+import me.zhyd.oauth.config.AuthSource;
+import me.zhyd.oauth.request.AuthDefaultRequest;
+
+public enum AuthKeycloakSource implements AuthSource {
+    KEYCLOAK {
+        /**
+         * 鎺堟潈鐨刟pi
+         */
+        @Override
+        public String authorize() {
+            return String.format("%s/realms/%s/protocol/openid-connect/auth", AuthKeycloakRequest.SERVER_URL, AuthKeycloakRequest.REALM);
+        }
+
+        @Override
+        public String accessToken() {
+            return String.format("%s/realms/%s/protocol/openid-connect/token", AuthKeycloakRequest.SERVER_URL, AuthKeycloakRequest.REALM);
+        }
+
+        @Override
+        public String userInfo() {
+            return String.format("%s/realms/%s/protocol/openid-connect/userinfo", AuthKeycloakRequest.SERVER_URL, AuthKeycloakRequest.REALM);
+        }
+
+
+        public String logout() {
+            return String.format("%s/realms/%s/protocol/openid-connect/logout", AuthKeycloakRequest.SERVER_URL, AuthKeycloakRequest.REALM);
+        }
+
+        @Override
+        public Class<? extends AuthDefaultRequest> getTargetClass() {
+            return AuthKeycloakRequest.class;
+        }
+    }
+}
diff --git a/eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java b/eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java
index 9191fca..3c334f9 100644
--- a/eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java
+++ b/eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java
@@ -10,6 +10,7 @@
 import org.dromara.common.core.utils.SpringUtils;
 import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
 import org.dromara.common.social.config.properties.SocialProperties;
+import org.dromara.common.social.keycloak.AuthKeycloakRequest;
 import org.dromara.common.social.maxkey.AuthMaxKeyRequest;
 import org.dromara.common.social.topiam.AuthTopIamRequest;
 
@@ -66,6 +67,7 @@
             case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE);
             case "maxkey" -> new AuthMaxKeyRequest(builder.build(), STATE_CACHE);
             case "topiam" -> new AuthTopIamRequest(builder.build(), STATE_CACHE);
+            case "keycloak" -> new AuthKeycloakRequest(builder.build(), STATE_CACHE);
             default -> throw new AuthException("鏈幏鍙栧埌鏈夋晥鐨凙uth閰嶇疆");
         };
     }
diff --git a/eims/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysSocialService.java b/eims/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysSocialService.java
index cc7016e..e88961b 100644
--- a/eims/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysSocialService.java
+++ b/eims/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysSocialService.java
@@ -49,5 +49,6 @@
      */
     List<SysSocialVo> selectByAuthId(String authId);
 
+    SysSocialVo selectByUserId(Long userId);
 
 }
diff --git a/eims/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java b/eims/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java
index 9c54cbc..f13ba15 100644
--- a/eims/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java
+++ b/eims/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java
@@ -109,4 +109,11 @@
         return baseMapper.selectVoList(new LambdaQueryWrapper<SysSocial>().eq(SysSocial::getAuthId, authId));
     }
 
+    @Override
+    public SysSocialVo selectByUserId(Long userId) {
+        SysSocialVo socialVo = baseMapper.selectVoOne(new LambdaQueryWrapper<SysSocial>().eq(SysSocial::getUserId, userId));
+        return socialVo;
+    }
+
+
 }

--
Gitblit v1.9.3