From 7de1396e315896dbc72a9d54e44f77434ea90f18 Mon Sep 17 00:00:00 2001
From: wlzboy <66905212@qq.com>
Date: 星期日, 14 十二月 2025 23:47:34 +0800
Subject: [PATCH] feat:增加企业微信自动登录

---
 app/pages/index.vue                                                                            |    2 
 ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml                                |    6 
 app/api/login.js                                                                               |   12 
 app/pages.json                                                                                 |    5 
 ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/QyWechatLoginService.java        |   89 +++
 app/pages/qylogin.vue                                                                          |  323 +++++++++++++
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/QyWechatLoginController.java         |  210 ++++++++
 ruoyi-framework/src/main/java/com/ruoyi/framework/security/QyWechatAuthenticationProvider.java |   75 +++
 ruoyi-admin/src/main/resources/application.yml                                                 |    2 
 app/App.vue                                                                                    |   71 ++
 企业微信免登功能使用说明.md                                                                                |  155 ++++++
 ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java      |   11 
 app/permission.js                                                                              |    3 
 ruoyi-framework/src/main/java/com/ruoyi/framework/security/QyWechatAuthenticationToken.java    |   85 +++
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java               |   12 
 app/pages/login.vue                                                                            |   36 +
 ruoyi-ui/src/views/system/qywechat/autologin.vue                                               |  108 ++++
 ruoyi-system/src/main/java/com/ruoyi/system/service/IQyWechatAccessTokenService.java           |   17 
 ruoyi-ui/src/router/modules/qywechat.js                                                        |   25 +
 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java                       |    8 
 ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java                   |   11 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java                          |    8 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/QyWechatAccessTokenServiceImpl.java   |  122 ++++
 ruoyi-ui/src/router/index.js                                                                   |    2 
 24 files changed, 1,366 insertions(+), 32 deletions(-)

diff --git a/app/App.vue b/app/App.vue
index 86cbf17..15eb1ac 100644
--- a/app/App.vue
+++ b/app/App.vue
@@ -12,9 +12,9 @@
         lastToken: null // 鐢ㄤ簬妫�娴� token 鍙樺寲
       }
     },
-    onLaunch: function() {
+    onLaunch: function(options) {
       this.lastToken = getToken()
-      this.initApp()
+      this.initApp(options)
       
       // 妫�鏌ュ苟娓呯悊瀛樺偍绌洪棿
       this.checkStorage()
@@ -72,13 +72,11 @@
     },
     methods: {
       // 鍒濆鍖栧簲鐢�
-      initApp() {
+      initApp(options) {
         // 鍒濆鍖栧簲鐢ㄩ厤缃�
         this.initConfig()
-        // 妫�鏌ョ敤鎴风櫥褰曠姸鎬�
-        //#ifdef H5
-        this.checkLogin()
-        //#endif
+        // 妫�鏌ョ敤鎴风櫥褰曠姸鎬佸苟鑷姩璺宠浆鍒板悎閫傜殑鐧诲綍椤甸潰
+        this.checkLoginAndRedirect(options)
         
         // 娉ㄦ剰锛氫笉鍦ㄥ簲鐢ㄥ惎鍔ㄦ椂鑷姩鍚姩杞
         // 鍙湁鍦ㄧ敤鎴蜂富鍔ㄧ櫥褰曟垚鍔熷悗鎵嶅惎鍔紙閫氳繃 user-login 浜嬩欢瑙﹀彂锛�
@@ -86,9 +84,62 @@
       initConfig() {
         this.globalData.config = config
       },
-      checkLogin() {
+      // 妫�鏌ョ櫥褰曠姸鎬佸苟鑷姩璺宠浆鍒板悎閫傜殑鐧诲綍椤甸潰
+      checkLoginAndRedirect(options) {
         if (!getToken()) {
-          this.$tab.reLaunch('/pages/login') 
+          // 妫�鏌ヨ繍琛岀幆澧冨苟璺宠浆鍒板搴旂殑鐧诲綍椤甸潰
+          // #ifdef MP-WEIXIN
+          // 鍦ㄥ井淇″皬绋嬪簭鐜涓�
+          try {
+            // 鑾峰彇绯荤粺淇℃伅
+            const systemInfo = uni.getSystemInfoSync()
+            console.log('绯荤粺淇℃伅:', systemInfo)
+            
+            // 妫�鏌nvironment瀛楁鏄惁涓簑xwork
+            if (systemInfo.environment === 'wxwork') {
+              console.log('妫�娴嬪埌浼佷笟寰俊鐜锛岃烦杞埌浼佷笟寰俊鍏嶇櫥椤甸潰')
+              // 鏋勯�犲甫鍙傛暟鐨刄RL
+              let url = '/pages/qylogin'
+              if (options && options.query) {
+                const queryParams = Object.keys(options.query).map(key => `${key}=${encodeURIComponent(options.query[key])}`).join('&')
+                if (queryParams) {
+                  url += '?' + queryParams
+                }
+              }
+              this.$tab.reLaunch(url)
+              return
+            } else {
+              console.log('妫�娴嬪埌鏅�氬井淇$幆澧冿紝璺宠浆鍒板井淇$櫥褰曢〉闈�')
+              // 鏋勯�犲甫鍙傛暟鐨刄RL
+              let url = '/pages/login'
+              if (options && options.query) {
+                const queryParams = Object.keys(options.query).map(key => `${key}=${encodeURIComponent(options.query[key])}`).join('&')
+                if (queryParams) {
+                  url += '?' + queryParams
+                }
+              }
+              this.$tab.reLaunch(url)
+              return
+            }
+          } catch (e) {
+            console.error('鑾峰彇绯荤粺淇℃伅澶辫触:', e)
+            // 榛樿璺宠浆鍒版櫘閫氱櫥褰曢〉闈�
+            this.$tab.reLaunch('/pages/login')
+          }
+          // #endif
+          
+          // #ifndef MP-WEIXIN
+          // 闈炲井淇″皬绋嬪簭鐜锛岃烦杞埌鏅�氱櫥褰曢〉闈�
+          console.log('闈炲井淇″皬绋嬪簭鐜锛岃烦杞埌鏅�氱櫥褰曢〉闈�')
+          let url = '/pages/login'
+          if (options && options.query) {
+            const queryParams = Object.keys(options.query).map(key => `${key}=${encodeURIComponent(options.query[key])}`).join('&')
+            if (queryParams) {
+              url += '?' + queryParams
+            }
+          }
+          this.$tab.reLaunch(url)
+          // #endif
         }
       },
       
@@ -187,4 +238,4 @@
 
 <style lang="scss">
   @import '@/static/scss/index.scss'
-</style>
+</style>
\ No newline at end of file
diff --git a/app/api/login.js b/app/api/login.js
index baa7af2..558cd15 100644
--- a/app/api/login.js
+++ b/app/api/login.js
@@ -101,3 +101,15 @@
     }
   })
 }
+
+// 浼佷笟寰俊鍏嶇櫥
+export function qyWechatAutoLogin(code) {
+  return request({
+    url: '/system/qywechat/autoLogin',
+    headers: {
+      isToken: false
+    },
+    method: 'post',
+    data: { code }
+  })
+}
\ No newline at end of file
diff --git a/app/pages.json b/app/pages.json
index bf309f7..bb9fff8 100644
--- a/app/pages.json
+++ b/app/pages.json
@@ -5,6 +5,11 @@
       "navigationBarTitleText": "鐧诲綍"
     }
   }, {
+    "path": "pages/qylogin",
+    "style": {
+      "navigationBarTitleText": "浼佷笟寰俊鍏嶇櫥"
+    }
+  }, {
     "path": "pages/login/wechat",
     "style": {
       "navigationBarTitleText": "寰俊鐧诲綍"
diff --git a/app/pages/index.vue b/app/pages/index.vue
index e458aba..1260e73 100644
--- a/app/pages/index.vue
+++ b/app/pages/index.vue
@@ -276,7 +276,7 @@
     }
 
     // 妫�鏌ヨ闃呯姸鎬侊紙鍏堟鏌ユ湰鍦帮紝鍚庨潰浼氭鏌ュ井淇″畼鏂圭姸鎬侊級
-    this.hasSubscribed = subscribeManager.checkLocalSubscribeStatus();
+    this.hasSubscribed = true;//subscribeManager.checkLocalSubscribeStatus();
 
     // 鑷姩璁㈤槄锛堝鏋滄湭璁㈤槄鍒欐樉绀虹‘璁ゅ脊绐楋級
     this.autoSubscribeOnLaunch();
diff --git a/app/pages/login.vue b/app/pages/login.vue
index d6bf9cf..700706b 100644
--- a/app/pages/login.vue
+++ b/app/pages/login.vue
@@ -85,7 +85,13 @@
         isWechat: false, // 鏄惁涓哄井淇″皬绋嬪簭鐜
         wechatOpenId: '', // 寰俊OpenID
         wechatUnionId: '', // 寰俊UnionID
+        // 椤甸潰鍙傛暟
+        pageOptions: {}
       }
+    },
+    onLoad(options) {
+      // 淇濆瓨椤甸潰鍙傛暟
+      this.pageOptions = options || {}
     },
     created() {
       this.getCode()
@@ -153,7 +159,15 @@
         this.$store.dispatch('GetInfo').then(res => {
           // 瑙﹀彂鐧诲綍鎴愬姛浜嬩欢锛屽惎鍔ㄦ秷鎭疆璇�
           uni.$emit('user-login')
-          this.$tab.reLaunch('/pages/index')
+          // 妫�鏌ユ槸鍚︽湁redirect鍙傛暟鎸囧畾璺宠浆椤甸潰
+          if (this.pageOptions.redirect) {
+            // 瑙g爜redirect鍙傛暟
+            const redirectUrl = decodeURIComponent(this.pageOptions.redirect)
+            this.$tab.reLaunch(redirectUrl)
+          } else {
+            // 榛樿璺宠浆鍒伴椤�
+            this.$tab.reLaunch('/pages/index')
+          }
         })
       },
       
@@ -237,7 +251,15 @@
                   const { setToken } = require('@/utils/auth')
                   setToken(token)
                   
-                  this.loginSuccess()
+                  // 妫�鏌ユ槸鍚︽湁redirect鍙傛暟鎸囧畾璺宠浆椤甸潰
+                  if (this.pageOptions.redirect) {
+                    // 瑙g爜redirect鍙傛暟
+                    const redirectUrl = decodeURIComponent(this.pageOptions.redirect)
+                    this.$tab.reLaunch(redirectUrl)
+                  } else {
+                    // 榛樿璺宠浆鍒伴椤�
+                    this.$tab.reLaunch('/pages/index')
+                  }
                 } else {
                   this.$modal.msgError(response.msg || '鐧诲綍澶辫触')
                 }
@@ -275,7 +297,15 @@
             const { setToken } = require('@/utils/auth')
             setToken(token)
             
-            this.loginSuccess()
+            // 妫�鏌ユ槸鍚︽湁redirect鍙傛暟鎸囧畾璺宠浆椤甸潰
+            if (this.pageOptions.redirect) {
+              // 瑙g爜redirect鍙傛暟
+              const redirectUrl = decodeURIComponent(this.pageOptions.redirect)
+              this.$tab.reLaunch(redirectUrl)
+            } else {
+              // 榛樿璺宠浆鍒伴椤�
+              this.$tab.reLaunch('/pages/index')
+            }
           } else {
             // OpenID鏈粦瀹氭垨楠岃瘉澶辫触锛岄渶瑕佽幏鍙栨墜鏈哄彿缁戝畾
             console.log('璇penID灏氭湭缁戝畾鎴栭獙璇佸け璐ワ紝闇�瑕佽幏鍙栨墜鏈哄彿')
diff --git a/app/pages/qylogin.vue b/app/pages/qylogin.vue
new file mode 100644
index 0000000..80ff189
--- /dev/null
+++ b/app/pages/qylogin.vue
@@ -0,0 +1,323 @@
+<template>
+  <view class="container">
+    <view class="loading-content" v-if="loading">
+      <image class="loading-icon" src="/static/images/loading.gif"></image>
+      <text class="loading-text">姝e湪浼佷笟寰俊鍏嶇櫥涓�...</text>
+    </view>
+
+    <view class="error-content" v-else-if="error">
+      <image class="error-icon" src="/static/images/error.png"></image>
+      <text class="error-text">{{ errorMessage }}</text>
+      <button class="retry-btn" @click="retryLogin">閲嶆柊灏濊瘯</button>
+    </view>
+
+    <view class="success-content" v-else>
+      <image class="success-icon" src="/static/images/success.png"></image>
+      <text class="success-text">浼佷笟寰俊鍏嶇櫥鎴愬姛</text>
+    </view>
+  </view>
+</template>
+
+<script>
+import { qyWechatAutoLogin } from "@/api/login";
+
+export default {
+  data() {
+    return {
+      loading: true,
+      error: false,
+      errorMessage: "",
+      // 淇濆瓨椤甸潰鍙傛暟
+      pageOptions: {},
+    };
+  },
+
+  onLoad(options) {
+    // 淇濆瓨椤甸潰鍙傛暟
+    this.pageOptions = options || {};
+    // 椤甸潰鍔犺浇鏃舵墽琛屽厤鐧绘祦绋�
+    this.qyWechatAutoLogin();
+  },
+
+  methods: {
+    async getLoginCode() {
+      //杩欓噷瑕佽皟鐢╳x.qy.login鑾峰彇code,鏄惁鏈夐泦鎴愬埌uni-app涓憿
+      return new Promise((resolve, reject) => {
+        wx.qy.login({
+          success: (res) => {
+            if (res.code) {
+              console.log("浼佷笟寰俊灏忕▼搴� ---> code锛�", res.code);
+              resolve(res.code); // 杩斿洖 code 缁欏悗绔�
+            } else {
+              reject(new Error("鑾峰彇 code 澶辫触锛�" + res.errMsg));
+            }
+          },
+          fail: (err) => {
+            console.error("wx.qy.login 璋冪敤澶辫触锛�", err);
+            // 甯歌澶辫触鍘熷洜锛氶潪浼佷笟寰俊瀹㈡埛绔墦寮�銆佸皬绋嬪簭鏈叧鑱斾紒涓氬井淇$瓑
+            reject(new Error("浼佷笟寰俊鐧诲綍鎺ュ彛璋冪敤澶辫触锛�" + err.errMsg));
+          },
+        });
+      });
+    },
+    /**
+     * 浼佷笟寰俊鍏嶇櫥娴佺▼
+     */
+    async qyWechatAutoLogin() {
+      try {
+        this.loading = true;
+        this.error = false;
+
+        // #ifdef MP-WEIXIN
+        // 鍦ㄥ井淇″皬绋嬪簭鐜涓紝閫氳繃浼佷笟寰俊鍏嶇櫥
+        console.log("浼佷笟寰俊灏忕▼搴忕幆澧冨厤鐧�");
+
+        // 鑾峰彇URL鍙傛暟涓殑code
+        const code = await this.getLoginCode();
+
+        if (!code) {
+          // 濡傛灉娌℃湁code锛屽皾璇曢�氳繃浼佷笟寰俊API鑾峰彇
+          this.handleWxWorkLogin();
+          return;
+        }
+
+        // 璋冪敤鍚庣鎺ュ彛杩涜鍏嶇櫥
+        const response = await qyWechatAutoLogin(code);
+
+        if (response.code === 200) {
+          // 鍏嶇櫥鎴愬姛锛屼繚瀛榯oken
+          const token = response.data.token;
+          
+          this.$store.commit('SET_TOKEN', token)
+           // 蹇呴』璋冪敤setToken淇濆瓨鍒版湰鍦板瓨鍌�
+          const { setToken } = require('@/utils/auth')
+          setToken(token)
+          // 鑾峰彇鐢ㄦ埛淇℃伅
+          await this.$store.dispatch("GetInfo");
+
+          // 璺宠浆鍒伴椤垫垨鍏朵粬鎸囧畾椤甸潰
+          this.redirectAfterLogin();
+        } else {
+          throw new Error(response.msg || "鍏嶇櫥澶辫触");
+        }
+        // #endif
+
+        // #ifdef H5
+        // 妫�鏌ユ槸鍚﹀湪浼佷笟寰俊鐜涓�
+        if (!this.isWxWorkEnvironment()) {
+          throw new Error("璇峰湪浼佷笟寰俊瀹㈡埛绔腑鎵撳紑");
+        }
+
+        // 鑾峰彇URL鍙傛暟涓殑code
+        const codeH5 = this.getUrlParam("code");
+
+        if (!codeH5) {
+          // 濡傛灉娌℃湁code锛屽垯璺宠浆鍒颁紒涓氬井淇℃巿鏉冮〉闈�
+          this.redirectToWxWorkAuth();
+          return;
+        }
+
+        // 璋冪敤鍚庣鎺ュ彛杩涜鍏嶇櫥
+        const responseH5 = await qyWechatAutoLogin(codeH5);
+
+        if (responseH5.code === 200) {
+          // 鍏嶇櫥鎴愬姛锛屼繚瀛榯oken
+          const token = responseH5.data.token;
+          this.$store.commit("SET_TOKEN", token);
+          uni.setStorageSync("token", token);
+
+          // 鑾峰彇鐢ㄦ埛淇℃伅
+          await this.$store.dispatch("GetInfo");
+
+          // 璺宠浆鍒伴椤垫垨鍏朵粬鎸囧畾椤甸潰
+          this.redirectAfterLogin();
+        } else {
+          throw new Error(responseH5.msg || "鍏嶇櫥澶辫触");
+        }
+        // #endif
+
+        // #ifndef MP-WEIXIN || H5
+        throw new Error("璇ュ姛鑳戒粎鏀寔鍦ㄤ紒涓氬井淇′腑浣跨敤");
+        // #endif
+      } catch (err) {
+        console.error("浼佷笟寰俊鍏嶇櫥澶辫触:", err);
+        this.loading = false;
+        this.error = true;
+        this.errorMessage = err.message || "鍏嶇櫥澶辫触锛岃绋嶅悗閲嶈瘯";
+      }
+    },
+
+    /**
+     * 澶勭悊浼佷笟寰俊鐧诲綍
+     */
+    handleWxWorkLogin() {
+      // #ifdef MP-WEIXIN
+      // 鍦ㄤ紒涓氬井淇″皬绋嬪簭涓紝鍙互鐩存帴璋冪敤浼佷笟寰俊鐧诲綍API
+      uni.login({
+        provider: "weixin",
+        success: (loginRes) => {
+          console.log("浼佷笟寰俊鐧诲綍鎴愬姛", loginRes);
+          // 璋冪敤鍚庣鎺ュ彛杩涜鍏嶇櫥
+          qyWechatAutoLogin(loginRes.code)
+            .then((response) => {
+              if (response.code === 200) {
+                // 鍏嶇櫥鎴愬姛锛屼繚瀛榯oken
+                const token = response.data.token;
+                this.$store.commit("SET_TOKEN", token);
+                uni.setStorageSync("token", token);
+
+                // 鑾峰彇鐢ㄦ埛淇℃伅
+                this.$store.dispatch("GetInfo").then(() => {
+                  // 璺宠浆鍒伴椤垫垨鍏朵粬鎸囧畾椤甸潰
+                  this.redirectAfterLogin();
+                });
+              } else {
+                throw new Error(response.msg || "鍏嶇櫥澶辫触");
+              }
+            })
+            .catch((error) => {
+              console.error("鍏嶇櫥澶辫触:", error);
+              this.loading = false;
+              this.error = true;
+              this.errorMessage = error.message || "鍏嶇櫥澶辫触锛岃绋嶅悗閲嶈瘯";
+            });
+        },
+        fail: (err) => {
+          console.error("浼佷笟寰俊鐧诲綍澶辫触:", err);
+          this.loading = false;
+          this.error = true;
+          this.errorMessage = "浼佷笟寰俊鐧诲綍澶辫触锛岃绋嶅悗閲嶈瘯";
+        },
+      });
+      // #endif
+    },
+
+    /**
+     * 妫�鏌ユ槸鍚﹀湪浼佷笟寰俊鐜
+     */
+    isWxWorkEnvironment() {
+      const userAgent = navigator.userAgent.toLowerCase();
+      return userAgent.includes("wxwork");
+    },
+
+    /**
+     * 鑾峰彇URL鍙傛暟
+     */
+    getUrlParam(name) {
+      // #ifdef H5
+      const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
+      const r = window.location.search.substr(1).match(reg);
+      if (r != null) return decodeURIComponent(r[2]);
+      // #endif
+
+      // #ifdef MP-WEIXIN
+      // 鍦ㄥ皬绋嬪簭涓彲浠ラ�氳繃鍏朵粬鏂瑰紡鑾峰彇鍙傛暟
+      // 杩欓噷绠�鍖栧鐞嗭紝瀹為檯椤圭洰涓彲浠ユ牴鎹渶瑕佽皟鏁�
+      // #endif
+
+      return null;
+    },
+
+    /**
+     * 璺宠浆鍒颁紒涓氬井淇℃巿鏉冮〉闈�
+     */
+    redirectToWxWorkAuth() {
+      // 浠庡叏灞�閰嶇疆涓幏鍙栦紒涓氬井淇¢厤缃�
+      const config = getApp().globalData.config;
+      const corpId = config.qyWechatCorpId || "your_corp_id"; // 浼佷笟ID
+      const agentId = config.qyWechatAgentId || "your_agent_id"; // 搴旂敤ID
+      const redirectUri = encodeURIComponent(window.location.href);
+      const state = Date.now(); // 闃查噸鏀炬敾鍑�
+
+      const authUrl = `https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=${corpId}&agentid=${agentId}&redirect_uri=${redirectUri}&state=${state}`;
+
+      window.location.href = authUrl;
+    },
+
+    /**
+     * 鐧诲綍鎴愬姛鍚庣殑璺宠浆澶勭悊
+     */
+    redirectAfterLogin() {
+      // 妫�鏌ユ槸鍚︽湁redirect鍙傛暟鎸囧畾璺宠浆椤甸潰
+      if (this.pageOptions.redirect) {
+        // 瑙g爜redirect鍙傛暟
+        const redirectUrl = decodeURIComponent(this.pageOptions.redirect);
+        this.$tab.reLaunch(redirectUrl);
+      } else {
+        // 榛樿璺宠浆鍒伴椤�
+        this.$tab.reLaunch("/pages/index");
+      }
+    },
+
+    /**
+     * 閲嶆柊灏濊瘯鐧诲綍
+     */
+    retryLogin() {
+      this.qyWechatAutoLogin();
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.container {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  min-height: 100vh;
+  padding: 40rpx;
+  background-color: #f8f8f8;
+}
+
+.loading-content,
+.error-content,
+.success-content {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  text-align: center;
+}
+
+.loading-icon,
+.error-icon,
+.success-icon {
+  width: 120rpx;
+  height: 120rpx;
+  margin-bottom: 30rpx;
+}
+
+.loading-text,
+.error-text,
+.success-text {
+  font-size: 32rpx;
+  color: #333;
+  margin-bottom: 40rpx;
+}
+
+.error-text,
+.success-text {
+  font-weight: bold;
+}
+
+.error-text {
+  color: #ff0000;
+}
+
+.success-text {
+  color: #00cc00;
+}
+
+.retry-btn {
+  width: 80%;
+  height: 80rpx;
+  background-color: #007aff;
+  color: #fff;
+  border-radius: 10rpx;
+  font-size: 32rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+</style>
\ No newline at end of file
diff --git a/app/permission.js b/app/permission.js
index 43daf70..fc40fe1 100644
--- a/app/permission.js
+++ b/app/permission.js
@@ -6,6 +6,7 @@
 // 椤甸潰鐧藉悕鍗�
 const whiteList = [
   '/pages/login', 
+  '/pages/qylogin',  // 浼佷笟寰俊鍏嶇櫥椤甸潰锛堝尶鍚嶈闂級
   '/pages/register', 
   '/pages/common/webview/index',
   '/pages/mine/privacy-policy/index',  // 闅愮鏀跨瓥锛堝尶鍚嶈闂級
@@ -40,4 +41,4 @@
       console.log(err)
     }
   })
-})
+})
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/QyWechatLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/QyWechatLoginController.java
new file mode 100644
index 0000000..7cd5e5a
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/QyWechatLoginController.java
@@ -0,0 +1,210 @@
+package com.ruoyi.web.controller.system;
+
+import com.ruoyi.common.annotation.Anonymous;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.framework.web.service.QyWechatLoginService;
+import com.ruoyi.system.service.IQyWechatAccessTokenService;
+import com.ruoyi.system.service.IQyWechatService;
+import com.ruoyi.system.service.ISysConfigService;
+import com.ruoyi.system.service.ISysUserService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+
+/**
+ * 浼佷笟寰俊鍏嶇櫥鎺у埗鍣�
+ * 
+ * @author ruoyi
+ * @date 2025-12-14
+ */
+@RestController
+@RequestMapping("/system/qywechat")
+public class QyWechatLoginController extends BaseController {
+    
+    private static final Logger log = LoggerFactory.getLogger(QyWechatLoginController.class);
+    
+    @Autowired
+    private IQyWechatAccessTokenService qyWechatAccessTokenService;
+    
+    @Autowired
+    private IQyWechatService qyWechatService;
+    
+    @Autowired
+    private ISysConfigService configService;
+    
+    @Autowired
+    private ISysUserService userService;
+    
+    @Autowired
+    private QyWechatLoginService qyWechatLoginService;
+    
+    /**
+     * 浼佷笟寰俊鍏嶇櫥鎺ュ彛
+     * 
+     * @param params 浼佷笟寰俊鎺堟潈code
+     * @return 鐧诲綍缁撴灉
+     */
+    @Anonymous
+    @PostMapping("/autoLogin")
+    public AjaxResult autoLogin(@RequestBody Map<String, String> params) {
+        try {
+            String code = params.get("code");
+            
+            if (code == null || code.isEmpty()) {
+                return AjaxResult.error("缂哄皯鎺堟潈code鍙傛暟");
+            }
+            
+            // 鑾峰彇浼佷笟寰俊閰嶇疆
+            String corpId = configService.selectConfigByKey("qy_wechat.corp_id");
+            // 鑾峰彇浼佷笟寰俊灏忕▼搴忕殑secret锛堢敤浜庤幏鍙朅ccessToken锛�
+            String miniProgramSecret = configService.selectConfigByKey("qy_wechat.miniprogram_secret");
+            
+            if (corpId == null || miniProgramSecret == null) {
+                return AjaxResult.error("浼佷笟寰俊閰嶇疆涓嶅畬鏁达紝璇锋鏌orp_id鍜宮iniprogram_secret閰嶇疆");
+            }
+            
+            // 鑾峰彇AccessToken锛堜娇鐢ㄥ皬绋嬪簭鐨剆ecret锛�
+            String accessToken = qyWechatAccessTokenService.getQyMiniAccessToken(corpId, miniProgramSecret);
+            if (accessToken == null) {
+                return AjaxResult.error("鑾峰彇浼佷笟寰俊AccessToken澶辫触");
+            }
+            
+            // 閫氳繃code鑾峰彇鐢ㄦ埛淇℃伅
+            Map<String, Object> userInfo = getUserInfoByCode(accessToken, code);
+            if (!((Boolean) userInfo.get("success"))) {
+                return AjaxResult.error((String) userInfo.get("message"));
+            }
+            
+            // 鑾峰彇鐢ㄦ埛ID
+            String userId = (String) userInfo.get("userid");
+            
+            // 鏍规嵁浼佷笟寰俊鐢ㄦ埛ID鏌ユ壘绯荤粺鐢ㄦ埛
+            SysUser sysUser = userService.selectUserByQyWechatUserId(userId);
+            if (sysUser == null) {
+                return AjaxResult.error("璇ヤ紒涓氬井淇¤处鍙锋湭缁戝畾绯荤粺鐢ㄦ埛");
+            }
+            
+            // 妫�鏌ョ敤鎴风姸鎬�
+            if ("1".equals(sysUser.getStatus())) {
+                return AjaxResult.error("鐢ㄦ埛宸茶鍋滅敤锛岃鑱旂郴绠$悊鍛�");
+            }
+            
+            if ("1".equals(sysUser.getDelFlag())) {
+                return AjaxResult.error("鐢ㄦ埛宸茶鍒犻櫎锛岃鑱旂郴绠$悊鍛�");
+            }
+            
+            // 浣跨敤QyWechatLoginService鐢熸垚token
+            String token = qyWechatLoginService.loginByQyUserId(userId, corpId);
+            
+            // 鏋勯�犺繑鍥炵粨鏋�
+            Map<String, Object> result = new HashMap<>();
+            result.put("token", token);
+            result.put("user", sysUser);
+            
+            return AjaxResult.success("鐧诲綍鎴愬姛", result);
+        } catch (Exception e) {
+            log.error("浼佷笟寰俊鍏嶇櫥寮傚父", e);
+            return AjaxResult.error("鐧诲綍寮傚父锛�" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 鏍规嵁code鑾峰彇鐢ㄦ埛淇℃伅
+     * 
+     * @param accessToken AccessToken
+     * @param code 鎺堟潈code
+     * @return 鐢ㄦ埛淇℃伅
+     */
+    private Map<String, Object> getUserInfoByCode(String accessToken, String code) {
+        try {
+            Map<String, Object> result = new HashMap<>();
+            
+            // 鏋勯�犺姹俇RL - 浣跨敤浼佷笟寰俊灏忕▼搴忎笓鐢ㄦ帴鍙�
+            String url = "https://qyapi.weixin.qq.com/cgi-bin/miniprogram/jscode2session?access_token=" + accessToken + "&js_code=" + code + "&grant_type=authorization_code";
+            
+            // 鍙戦�丠TTP GET璇锋眰
+            String response = sendHttpGetRequest(url);
+            if (response == null || response.isEmpty()) {
+                result.put("success", false);
+                result.put("message", "鑾峰彇鐢ㄦ埛淇℃伅澶辫触锛屽搷搴斾负绌�");
+                return result;
+            }
+            
+            // 浣跨敤FastJSON瑙f瀽鍝嶅簲
+            JSONObject jsonResponse = JSON.parseObject(response);
+            
+            // 妫�鏌ユ槸鍚︽湁閿欒
+            Integer errcode = jsonResponse.getInteger("errcode");
+            if (errcode != null && errcode != 0) {
+                String errmsg = jsonResponse.getString("errmsg");
+                result.put("success", false);
+                result.put("message", "鑾峰彇鐢ㄦ埛淇℃伅澶辫触锛岄敊璇爜锛�" + errcode + "锛岄敊璇俊鎭細" + errmsg);
+                return result;
+            }
+            
+            // 妫�鏌ユ槸鍚﹀寘鍚玼serid瀛楁
+            String userId = jsonResponse.getString("userid");
+            if (userId == null || userId.isEmpty()) {
+                result.put("success", false);
+                result.put("message", "鑾峰彇鐢ㄦ埛淇℃伅澶辫触锛屾湭鎵惧埌鐢ㄦ埛ID");
+                return result;
+            }
+            
+            result.put("success", true);
+            result.put("userid", userId);
+            result.put("corpid", jsonResponse.getString("corpid"));
+            result.put("session_key", jsonResponse.getString("session_key"));
+            
+            return result;
+        } catch (Exception e) {
+            log.error("鑾峰彇鐢ㄦ埛淇℃伅寮傚父", e);
+            Map<String, Object> result = new HashMap<>();
+            result.put("success", false);
+            result.put("message", "鑾峰彇鐢ㄦ埛淇℃伅寮傚父锛�" + e.getMessage());
+            return result;
+        }
+    }
+    
+    /**
+     * 鍙戦�丠TTP GET璇锋眰
+     * 
+     * @param url 璇锋眰URL
+     * @return 鍝嶅簲鍐呭
+     */
+    private String sendHttpGetRequest(String url) {
+        try {
+            java.net.HttpURLConnection conn = (java.net.HttpURLConnection) new java.net.URL(url).openConnection();
+            conn.setRequestMethod("GET");
+            conn.setConnectTimeout(5000);
+            conn.setReadTimeout(5000);
+            
+            int responseCode = conn.getResponseCode();
+            if (responseCode == 200) {
+                java.io.BufferedReader reader = new java.io.BufferedReader(
+                    new java.io.InputStreamReader(conn.getInputStream(), "UTF-8"));
+                StringBuilder response = new StringBuilder();
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    response.append(line);
+                }
+                reader.close();
+                return response.toString();
+            } else {
+                log.error("HTTP璇锋眰澶辫触锛屽搷搴旂爜: {}", responseCode);
+                return null;
+            }
+        } catch (Exception e) {
+            log.error("鍙戦�丠TTP璇锋眰澶辫触", e);
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 4219d2c..105bc73 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -58,7 +58,7 @@
     basename: i18n/messages
   profiles:
     # 鐜 dev|test|prod
-    active: prod
+    active: dev
   # 鏂囦欢涓婁紶
   servlet:
     multipart:
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
index d9e5371..bfa6810 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -21,6 +21,7 @@
 import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
 import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
 import com.ruoyi.framework.security.WechatAuthenticationProvider;
+import com.ruoyi.framework.security.QyWechatAuthenticationProvider;
 import com.ruoyi.common.annotation.Anonymous;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.web.method.HandlerMethod;
@@ -85,6 +86,12 @@
     private WechatAuthenticationProvider wechatAuthenticationProvider;
 
     /**
+     * 浼佷笟寰俊璁よ瘉鎻愪緵鑰�
+     */
+    @Autowired
+    private QyWechatAuthenticationProvider qyWechatAuthenticationProvider;
+
+    /**
      * 鑾峰彇鎵�鏈夋爣娉ㄤ簡@Anonymous鐨刄RL
      */
     private Set<String> getAnonymousUrls() {
@@ -105,7 +112,7 @@
 
     /**
      * 韬唤楠岃瘉瀹炵幇
-     * 鏀寔鐢ㄦ埛鍚嶅瘑鐮佽璇佸拰寰俊璁よ瘉
+     * 鏀寔鐢ㄦ埛鍚嶅瘑鐮佽璇併�佸井淇¤璇佸拰浼佷笟寰俊璁よ瘉
      */
     @Bean
     public AuthenticationManager authenticationManager()
@@ -116,7 +123,7 @@
         daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
         
         // 杩斿洖ProviderManager,鏀寔澶氱璁よ瘉鏂瑰紡
-        return new ProviderManager(daoAuthenticationProvider, wechatAuthenticationProvider);
+        return new ProviderManager(daoAuthenticationProvider, wechatAuthenticationProvider, qyWechatAuthenticationProvider);
     }
 
     /**
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/QyWechatAuthenticationProvider.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/QyWechatAuthenticationProvider.java
new file mode 100644
index 0000000..5dbae65
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/QyWechatAuthenticationProvider.java
@@ -0,0 +1,75 @@
+package com.ruoyi.framework.security;
+
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.web.service.SysPermissionService;
+import com.ruoyi.system.service.ISysUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.stereotype.Component;
+import java.util.Set;
+
+/**
+ * 浼佷笟寰俊鐧诲綍璁よ瘉鎻愪緵鑰�
+ * 绫讳技浜嶥aoAuthenticationProvider
+ * 
+ * @author ruoyi
+ */
+@Component
+public class QyWechatAuthenticationProvider implements AuthenticationProvider
+{
+    @Autowired
+    private ISysUserService userService;
+
+    @Autowired
+    private SysPermissionService permissionService;
+
+    @Override
+    public Authentication authenticate(Authentication authentication) throws AuthenticationException
+    {
+        QyWechatAuthenticationToken qyWechatToken = (QyWechatAuthenticationToken) authentication;
+        
+        String qyUserId = (String) qyWechatToken.getPrincipal();
+        String corpId = (String) qyWechatToken.getCredentials();
+        //qywechat__qyUserId
+        //瀵筿ywechat__杩涜澶勭悊寰楀埌qyUserId
+        qyUserId = StringUtils.substringAfter(qyUserId, "qywechat__");
+        // 鏍规嵁浼佷笟寰俊鐢ㄦ埛ID鏌ヨ鐢ㄦ埛
+        SysUser user = userService.selectUserByQyWechatUserId(qyUserId);
+        
+        if (user == null)
+        {
+            throw new BadCredentialsException("璇ヤ紒涓氬井淇¤处鍙峰皻鏈粦瀹氱郴缁熺敤鎴�");
+        }
+        
+        // 妫�鏌ョ敤鎴风姸鎬�
+        if ("1".equals(user.getStatus()))
+        {
+            throw new BadCredentialsException("鐢ㄦ埛宸茶鍋滅敤锛岃鑱旂郴绠$悊鍛�");
+        }
+        
+        if ("1".equals(user.getDelFlag()))
+        {
+            throw new BadCredentialsException("鐢ㄦ埛宸茶鍒犻櫎锛岃鑱旂郴绠$悊鍛�");
+        }
+        
+        // 鑾峰彇鐢ㄦ埛鏉冮檺
+        Set<String> permissions = permissionService.getMenuPermission(user);
+        
+        // 鍒涘缓LoginUser瀵硅薄
+        LoginUser loginUser = new LoginUser(user.getUserId(), user.getDeptId(), user, permissions);
+        
+        // 杩斿洖宸茶璇佺殑Token
+        return new QyWechatAuthenticationToken(loginUser, corpId, loginUser.getAuthorities());
+    }
+
+    @Override
+    public boolean supports(Class<?> authentication)
+    {
+        return QyWechatAuthenticationToken.class.isAssignableFrom(authentication);
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/QyWechatAuthenticationToken.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/QyWechatAuthenticationToken.java
new file mode 100644
index 0000000..e4bc70e
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/QyWechatAuthenticationToken.java
@@ -0,0 +1,85 @@
+package com.ruoyi.framework.security;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+import java.util.Collection;
+
+/**
+ * 浼佷笟寰俊鐧诲綍璁よ瘉Token
+ * 绫讳技浜嶶sernamePasswordAuthenticationToken
+ * 
+ * @author ruoyi
+ */
+public class QyWechatAuthenticationToken extends AbstractAuthenticationToken
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 璁よ瘉涓讳綋(鐧诲綍鍓嶄负浼佷笟寰俊鐢ㄦ埛ID,鐧诲綍鍚庝负LoginUser)
+     */
+    private final Object principal;
+
+    /**
+     * 璁よ瘉鍑瘉(浼佷笟寰俊CorpID)
+     */
+    private Object credentials;
+
+    /**
+     * 鍒涘缓鏈璇佺殑Token(鐧诲綍鍓�)
+     * 
+     * @param qyUserId 浼佷笟寰俊鐢ㄦ埛ID
+     * @param corpId 浼佷笟寰俊CorpID
+     */
+    public QyWechatAuthenticationToken(String qyUserId, String corpId)
+    {
+        super(null);
+        this.principal = qyUserId;
+        this.credentials = corpId;
+        setAuthenticated(false);
+    }
+
+    /**
+     * 鍒涘缓宸茶璇佺殑Token(鐧诲綍鍚�)
+     * 
+     * @param principal 鐧诲綍鐢ㄦ埛淇℃伅
+     * @param credentials 鍑瘉
+     * @param authorities 鏉冮檺鍒楄〃
+     */
+    public QyWechatAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities)
+    {
+        super(authorities);
+        this.principal = principal;
+        this.credentials = credentials;
+        super.setAuthenticated(true);
+    }
+
+    @Override
+    public Object getCredentials()
+    {
+        return this.credentials;
+    }
+
+    @Override
+    public Object getPrincipal()
+    {
+        return this.principal;
+    }
+
+    @Override
+    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException
+    {
+        if (isAuthenticated)
+        {
+            throw new IllegalArgumentException(
+                "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
+        }
+        super.setAuthenticated(false);
+    }
+
+    @Override
+    public void eraseCredentials()
+    {
+        super.eraseCredentials();
+        credentials = null;
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/QyWechatLoginService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/QyWechatLoginService.java
new file mode 100644
index 0000000..bb88703
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/QyWechatLoginService.java
@@ -0,0 +1,89 @@
+package com.ruoyi.framework.web.service;
+
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.framework.manager.AsyncManager;
+import com.ruoyi.framework.manager.factory.AsyncFactory;
+import com.ruoyi.framework.security.QyWechatAuthenticationToken;
+import com.ruoyi.system.service.ISysUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+/**
+ * 浼佷笟寰俊鐧诲綍鏍¢獙鏂规硶
+ * 绫讳技浜嶴ysLoginService
+ * 
+ * @author ruoyi
+ */
+@Component
+public class QyWechatLoginService
+{
+    @Autowired
+    private TokenService tokenService;
+
+    @Autowired
+    private AuthenticationManager authenticationManager;
+
+    @Autowired
+    private SysLoginService sysLoginService;
+
+    @Autowired
+    private ISysUserService userService;
+
+    /**
+     * 浼佷笟寰俊鐢ㄦ埛ID鐧诲綍楠岃瘉
+     * 
+     * @param qyUserId 浼佷笟寰俊鐢ㄦ埛ID
+     * @param corpId 浼佷笟寰俊CorpID
+     * @return token
+     */
+    public String loginByQyUserId(String qyUserId, String corpId)
+    {
+        try
+        {
+            qyUserId = "qywechat__"+qyUserId;
+            // 鍒涘缓浼佷笟寰俊璁よ瘉Token
+            QyWechatAuthenticationToken authenticationToken = new QyWechatAuthenticationToken(qyUserId, corpId);
+            
+            // 浣跨敤AuthenticationManager杩涜璁よ瘉
+            Authentication authentication = authenticationManager.authenticate(authenticationToken);
+            
+            // 璁よ瘉鎴愬姛,鑾峰彇LoginUser
+            LoginUser loginUser = (LoginUser) authentication.getPrincipal();
+            
+            // 璁板綍鐧诲綍鎴愬姛鏃ュ織
+            AsyncManager.me().execute(AsyncFactory.recordLogininfor(
+                loginUser.getUsername(), 
+                Constants.LOGIN_SUCCESS, 
+                "浼佷笟寰俊鐢ㄦ埛ID鐧诲綍鎴愬姛"));
+            
+            // 璁板綍鐧诲綍淇℃伅(IP鍜屾椂闂�)
+            sysLoginService.recordLoginInfo(loginUser.getUserId());
+            
+            // 鐢熸垚token
+            return tokenService.createToken(loginUser);
+        }
+        catch (BadCredentialsException e)
+        {
+            // 璁板綍鐧诲綍澶辫触鏃ュ織
+            AsyncManager.me().execute(AsyncFactory.recordLogininfor(
+                qyUserId, 
+                Constants.LOGIN_FAIL, 
+                e.getMessage()));
+            throw e;
+        }
+        catch (Exception e)
+        {
+            // 璁板綍鐧诲綍澶辫触鏃ュ織
+            AsyncManager.me().execute(AsyncFactory.recordLogininfor(
+                qyUserId, 
+                Constants.LOGIN_FAIL, 
+                e.getMessage()));
+            throw new BadCredentialsException(e.getMessage());
+        }
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java
index 4680343..02bf836 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java
@@ -40,9 +40,16 @@
 
         // 灏濊瘯鍒ゆ柇鏄墜鏈哄彿銆乷penId杩樻槸鐢ㄦ埛鍚�
         SysUser user = null;
-        
+        //qywechat__
+        if (username.startsWith("qywechat__"))
+        {
+            //浼佷笟寰俊鐧诲綍
+            //qywechat__qyUserId
+            String qyUserId =StringUtils.substringAfter(username, "qywechat__");
+            user = userService.selectUserByQyWechatUserId(qyUserId);
+        }
         // 鍒ゆ柇鏄惁涓哄井淇penID锛堥�氬父浠�"o"寮�澶达紝28浣嶅瓧绗︼級
-        if (username.startsWith("o") && username.length() == 28)
+        else if (username.startsWith("o") && username.length() == 28)
         {
             // 寰俊OpenID鐧诲綍
             log.info("灏濊瘯浣跨敤寰俊OpenID鐧诲綍锛歿}", username);
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
index 05caeba..4811f42 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
@@ -153,6 +153,14 @@
     public SysUser selectUserByOpenId(@Param("openId") String openId);
     
     /**
+     * 閫氳繃浼佷笟寰俊鐢ㄦ埛ID鏌ヨ鐢ㄦ埛
+     * 
+     * @param qyWechatUserId 浼佷笟寰俊鐢ㄦ埛ID
+     * @return 鐢ㄦ埛瀵硅薄淇℃伅
+     */
+    public SysUser selectUserByQyWechatUserId(@Param("qyWechatUserId") String qyWechatUserId);
+    
+    /**
      * 鏍规嵁鍒嗗叕鍙窱D鍒楄〃鏌ヨ鐢ㄦ埛锛堝寘鍚垎鍏徃鍙婂叾鎵�鏈夊瓙閮ㄩ棬鐨勭敤鎴凤級
      * 
      * @param branchDeptIds 鍒嗗叕鍙窱D鍒楄〃
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/IQyWechatAccessTokenService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/IQyWechatAccessTokenService.java
index fc9465c..1e70e70 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/IQyWechatAccessTokenService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/IQyWechatAccessTokenService.java
@@ -12,19 +12,28 @@
      * 鑾峰彇浼佷笟寰俊搴旂敤鐨凙ccessToken
      * 
      * @param corpId 浼佷笟ID
-     * @param corpSecret 搴旂敤瀵嗛挜
+     * @param secret 搴旂敤瀵嗛挜鎴栧皬绋嬪簭瀵嗛挜
      * @return AccessToken
      */
-    String getAppAccessToken(String corpId, String corpSecret);
+    String getAppAccessToken(String corpId, String secret);
 
     /**
      * 鍒锋柊浼佷笟寰俊搴旂敤鐨凙ccessToken
      * 
      * @param corpId 浼佷笟ID
-     * @param corpSecret 搴旂敤瀵嗛挜
+     * @param secret 搴旂敤瀵嗛挜鎴栧皬绋嬪簭瀵嗛挜
      * @return 鏂扮殑AccessToken
      */
-    String refreshAppAccessToken(String corpId, String corpSecret);
+    String refreshAppAccessToken(String corpId, String secret);
+
+    /**
+     * 鑾峰彇浼佷笟寰俊灏忕▼搴忕殑AccessToken
+     * 
+     * @param corpId 浼佷笟ID
+     * @param corpSecret 灏忕▼搴忓瘑閽�
+     * @return AccessToken
+     */
+    String getQyMiniAccessToken(String corpId, String corpSecret);
 
     /**
      * 妫�鏌ヤ紒涓氬井淇℃湇鍔℃槸鍚﹀惎鐢�
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
index 0fa21a2..7f7dcb0 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
@@ -59,6 +59,14 @@
     public SysUser selectUserByOpenId(String openId);
     
     /**
+     * 閫氳繃浼佷笟寰俊鐢ㄦ埛ID鏌ヨ鐢ㄦ埛
+     * 
+     * @param qyWechatUserId 浼佷笟寰俊鐢ㄦ埛ID
+     * @return 鐢ㄦ埛瀵硅薄淇℃伅
+     */
+    public SysUser selectUserByQyWechatUserId(String qyWechatUserId);
+    
+    /**
      * 鏍规嵁oaUserId鏌ヨ鐢ㄦ埛
      * 
      * @param oaUserId SQL Server涓殑OA鐢ㄦ埛ID
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/QyWechatAccessTokenServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/QyWechatAccessTokenServiceImpl.java
index 534a865..ee04163 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/QyWechatAccessTokenServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/QyWechatAccessTokenServiceImpl.java
@@ -31,15 +31,15 @@
      * 鑾峰彇浼佷笟寰俊搴旂敤鐨凙ccessToken
      *
      * @param corpId 浼佷笟ID
-     * @param corpSecret 搴旂敤瀵嗛挜
+     * @param secret 搴旂敤瀵嗛挜鎴栧皬绋嬪簭瀵嗛挜
      * @return AccessToken
      */
     @Override
-    public String getAppAccessToken(String corpId, String corpSecret) {
+    public String getAppAccessToken(String corpId, String secret) {
         try {
             // 鍙傛暟鏍¢獙
-            if (StringUtils.isEmpty(corpId) || StringUtils.isEmpty(corpSecret)) {
-                log.warn("浼佷笟寰俊閰嶇疆鍙傛暟涓嶅畬鏁达紝corpId鎴朿orpSecret涓虹┖");
+            if (StringUtils.isEmpty(corpId) || StringUtils.isEmpty(secret)) {
+                log.warn("浼佷笟寰俊閰嶇疆鍙傛暟涓嶅畬鏁达紝corpId鎴杝ecret涓虹┖");
                 return null;
             }
 
@@ -77,7 +77,7 @@
             }
 
             // Token涓嶅瓨鍦ㄦ垨宸茶繃鏈燂紝鍒锋柊Token
-            return refreshAppAccessToken(corpId, corpSecret);
+            return refreshAppAccessToken(corpId, secret);
         } catch (Exception e) {
             log.error("鑾峰彇浼佷笟寰俊AccessToken澶辫触", e);
             return null;
@@ -88,16 +88,16 @@
      * 鍒锋柊浼佷笟寰俊搴旂敤鐨凙ccessToken
      *
      * @param corpId 浼佷笟ID
-     * @param corpSecret 搴旂敤瀵嗛挜
+     * @param secret 搴旂敤瀵嗛挜鎴栧皬绋嬪簭瀵嗛挜
      * @return 鏂扮殑AccessToken
      */
     @Override
-    public String refreshAppAccessToken(String corpId, String corpSecret) {
+    public String refreshAppAccessToken(String corpId, String secret) {
         try {
             log.info("寮�濮嬪埛鏂颁紒涓氬井淇ccessToken");
 
             // 鏋勫缓璇锋眰URL
-            String url = GET_ACCESS_TOKEN_URL + "?corpid=" + corpId + "&corpsecret=" + corpSecret;
+            String url = GET_ACCESS_TOKEN_URL + "?corpid=" + corpId + "&corpsecret=" + secret;
 
             // 鍙戦�丠TTP璇锋眰鑾峰彇Token
             String response = sendHttpGetRequest(url);
@@ -135,6 +135,112 @@
     }
 
     /**
+     * 鑾峰彇浼佷笟寰俊灏忕▼搴忕殑AccessToken
+     *
+     * @param corpId 浼佷笟ID
+     * @param corpSecret 灏忕▼搴忓瘑閽�
+     * @return AccessToken
+     */
+    @Override
+    public String getQyMiniAccessToken(String corpId, String corpSecret) {
+        try {
+            // 鍙傛暟鏍¢獙
+            if (StringUtils.isEmpty(corpId) || StringUtils.isEmpty(corpSecret)) {
+                log.warn("浼佷笟寰俊灏忕▼搴忛厤缃弬鏁颁笉瀹屾暣锛宑orpId鎴朿orpSecret涓虹┖");
+                return null;
+            }
+
+            // 妫�鏌ユ湇鍔℃槸鍚﹀惎鐢�
+            if (!isEnabled()) {
+                log.info("浼佷笟寰俊鏈嶅姟宸茬鐢紝鏃犳硶鑾峰彇灏忕▼搴廇ccessToken");
+                return null;
+            }
+
+            // 鏋勫缓閰嶇疆閿悕锛堜娇鐢ㄤ笉鍚岀殑閿悕浠ュ尯鍒嗘櫘閫氬簲鐢ㄥ拰灏忕▼搴忥級
+            String tokenKey = "qy_wechat.mini_access_token." + corpId;
+            String expiresKey = "qy_wechat.mini_access_token_expires." + corpId;
+
+            // 浠庨厤缃腑鑾峰彇Token鍜岃繃鏈熸椂闂�
+            String accessToken = configService.selectConfigByKey(tokenKey);
+            String expiresStr = configService.selectConfigByKey(expiresKey);
+
+            // 妫�鏌oken鏄惁瀛樺湪涓旀湭杩囨湡
+            if (StringUtils.isNotEmpty(accessToken) && StringUtils.isNotEmpty(expiresStr)) {
+                try {
+                    long expiresTime = Long.parseLong(expiresStr);
+                    long currentTime = System.currentTimeMillis();
+
+                    // 棰勭暀60绉掑畨鍏ㄨ竟鐣岋紝閬垮厤涓寸晫鐐硅繃鏈�
+                    if (currentTime < expiresTime - 60000) {
+                        log.debug("浣跨敤缂撳瓨鐨勪紒涓氬井淇″皬绋嬪簭AccessToken锛屽墿浣欐湁鏁堟椂闂�: {}绉�", 
+                            (expiresTime - currentTime) / 1000);
+                        return accessToken;
+                    } else {
+                        log.info("浼佷笟寰俊灏忕▼搴廇ccessToken宸茶繃鏈熸垨鍗冲皢杩囨湡锛岄渶瑕佸埛鏂�");
+                    }
+                } catch (NumberFormatException e) {
+                    log.warn("瑙f瀽浼佷笟寰俊灏忕▼搴廇ccessToken杩囨湡鏃堕棿澶辫触: {}", expiresStr);
+                }
+            }
+
+            // Token涓嶅瓨鍦ㄦ垨宸茶繃鏈燂紝鍒锋柊Token
+            return refreshQyMiniAccessToken(corpId, corpSecret);
+        } catch (Exception e) {
+            log.error("鑾峰彇浼佷笟寰俊灏忕▼搴廇ccessToken澶辫触", e);
+            return null;
+        }
+    }
+
+    /**
+     * 鍒锋柊浼佷笟寰俊灏忕▼搴忕殑AccessToken
+     *
+     * @param corpId 浼佷笟ID
+     * @param corpSecret 灏忕▼搴忓瘑閽�
+     * @return 鏂扮殑AccessToken
+     */
+    public String refreshQyMiniAccessToken(String corpId, String corpSecret) {
+        try {
+            log.info("寮�濮嬪埛鏂颁紒涓氬井淇″皬绋嬪簭AccessToken");
+
+            // 鏋勫缓璇锋眰URL
+            String url = GET_ACCESS_TOKEN_URL + "?corpid=" + corpId + "&corpsecret=" + corpSecret;
+
+            // 鍙戦�丠TTP璇锋眰鑾峰彇Token
+            String response = sendHttpGetRequest(url);
+            
+            if (StringUtils.isEmpty(response)) {
+                log.error("鑾峰彇浼佷笟寰俊灏忕▼搴廇ccessToken澶辫触锛屽搷搴斾负绌�");
+                return null;
+            }
+
+            // 瑙f瀽鍝嶅簲
+            QyWechatTokenResponse tokenResponse = parseTokenResponse(response);
+            
+            if (tokenResponse == null || StringUtils.isEmpty(tokenResponse.getAccessToken())) {
+                log.error("瑙f瀽浼佷笟寰俊灏忕▼搴廇ccessToken鍝嶅簲澶辫触: {}", response);
+                return null;
+            }
+
+            // 璁$畻杩囨湡鏃堕棿锛堝綋鍓嶆椂闂� + 鏈夋晥鏈� - 60绉掑畨鍏ㄨ竟鐣岋級
+            long expiresTime = System.currentTimeMillis() + (tokenResponse.getExpiresIn() * 1000L) - 60000L;
+
+            // 鏋勫缓閰嶇疆閿悕锛堜娇鐢ㄤ笉鍚岀殑閿悕浠ュ尯鍒嗘櫘閫氬簲鐢ㄥ拰灏忕▼搴忥級
+            String tokenKey = "qy_wechat.mini_access_token." + corpId;
+            String expiresKey = "qy_wechat.mini_access_token_expires." + corpId;
+
+            // 淇濆瓨鍒扮郴缁熼厤缃〃
+            configService.updateConfigValue(tokenKey, tokenResponse.getAccessToken());
+            configService.updateConfigValue(expiresKey, String.valueOf(expiresTime));
+
+            log.info("浼佷笟寰俊灏忕▼搴廇ccessToken鍒锋柊鎴愬姛锛屾湁鏁堟湡: {}绉�", tokenResponse.getExpiresIn());
+            return tokenResponse.getAccessToken();
+        } catch (Exception e) {
+            log.error("鍒锋柊浼佷笟寰俊灏忕▼搴廇ccessToken澶辫触", e);
+            return null;
+        }
+    }
+
+    /**
      * 妫�鏌ヤ紒涓氬井淇℃湇鍔℃槸鍚﹀惎鐢�
      *
      * @return true-鍚敤锛宖alse-绂佺敤
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
index 90b745b..ddc34de 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -153,6 +153,18 @@
     }
     
     /**
+     * 閫氳繃浼佷笟寰俊鐢ㄦ埛ID鏌ヨ鐢ㄦ埛
+     * 
+     * @param qyWechatUserId 浼佷笟寰俊鐢ㄦ埛ID
+     * @return 鐢ㄦ埛瀵硅薄淇℃伅
+     */
+    @Override
+    public SysUser selectUserByQyWechatUserId(String qyWechatUserId)
+    {
+        return userMapper.selectUserByQyWechatUserId(qyWechatUserId);
+    }
+    
+    /**
      * 鏍规嵁oaUserId鏌ヨ鐢ㄦ埛
      * 
      * @param oaUserId SQL Server涓殑OA鐢ㄦ埛ID
diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
index 6389b38..bdd74b3 100644
--- a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -263,6 +263,12 @@
 		where u.open_id = #{openId} and u.del_flag = '0'
 	</select>
 	
+	<!-- 閫氳繃浼佷笟寰俊鐢ㄦ埛ID鏌ヨ鐢ㄦ埛 -->
+	<select id="selectUserByQyWechatUserId" parameterType="String" resultMap="SysUserResult">
+		<include refid="selectUserVo"/>
+		where u.qy_wechat_user_id = #{qyWechatUserId} and u.del_flag = '0'
+	</select>
+	
 	<!-- 鏍规嵁鍒嗗叕鍙窱D鍒楄〃鏌ヨ鐢ㄦ埛锛堝寘鍚垎鍏徃鍙婂叾鎵�鏈夊瓙閮ㄩ棬鐨勭敤鎴凤級 -->
 	<select id="selectUsersByBranchDeptIds" resultMap="SysUserResult">
 		SELECT DISTINCT
diff --git a/ruoyi-ui/src/router/index.js b/ruoyi-ui/src/router/index.js
index f7295cf..81cbe25 100644
--- a/ruoyi-ui/src/router/index.js
+++ b/ruoyi-ui/src/router/index.js
@@ -5,6 +5,7 @@
 
 /* Layout */
 import Layout from '@/layout'
+import qywechatRouter from './modules/qywechat'
 
 /**
  * Note: 璺敱閰嶇疆椤�
@@ -149,6 +150,7 @@
 
 // 鍔ㄦ�佽矾鐢憋紝鍩轰簬鐢ㄦ埛鏉冮檺鍔ㄦ�佸幓鍔犺浇
 export const dynamicRoutes = [
+  qywechatRouter,
   {
     path: '/system/user-auth',
     component: Layout,
diff --git a/ruoyi-ui/src/router/modules/qywechat.js b/ruoyi-ui/src/router/modules/qywechat.js
new file mode 100644
index 0000000..0cb8aff
--- /dev/null
+++ b/ruoyi-ui/src/router/modules/qywechat.js
@@ -0,0 +1,25 @@
+import Layout from '@/layout'
+
+const qywechatRouter = {
+  path: '/qywechat',
+  component: Layout,
+  redirect: 'noRedirect',
+  name: 'QyWechat',
+  meta: { title: '浼佷笟寰俊', icon: 'wechat' },
+  children: [
+    {
+      path: 'test',
+      component: () => import('@/views/system/qywechat/test'),
+      name: 'QyWechatTest',
+      meta: { title: '鍔熻兘娴嬭瘯', icon: 'guide' }
+    },
+    {
+      path: 'autologin',
+      component: () => import('@/views/system/qywechat/autologin'),
+      name: 'QyWechatAutoLogin',
+      meta: { title: '鍏嶇櫥娴嬭瘯', icon: 'lock' }
+    }
+  ]
+}
+
+export default qywechatRouter
\ No newline at end of file
diff --git a/ruoyi-ui/src/views/system/qywechat/autologin.vue b/ruoyi-ui/src/views/system/qywechat/autologin.vue
new file mode 100644
index 0000000..a665e56
--- /dev/null
+++ b/ruoyi-ui/src/views/system/qywechat/autologin.vue
@@ -0,0 +1,108 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span>浼佷笟寰俊鍏嶇櫥娴嬭瘯</span>
+      </div>
+      
+      <el-alert
+        title="璇存槑"
+        type="info"
+        description="姝ら〉闈㈢敤浜庢祴璇曚紒涓氬井淇″厤鐧诲姛鑳斤紝璇风‘淇濆凡鍦ㄤ紒涓氬井淇′腑閰嶇疆濂界浉鍏冲弬鏁�"
+        show-icon
+        :closable="false"
+        style="margin-bottom: 20px;"
+      >
+      </el-alert>
+      
+      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+        <el-form-item label="鎺堟潈Code" prop="code">
+          <el-input v-model="form.code" placeholder="璇疯緭鍏ヤ紒涓氬井淇℃巿鏉僀ode" />
+          <div class="form-tip">鍙�氳繃浼佷笟寰俊鎵爜鐧诲綍鑾峰彇Code</div>
+        </el-form-item>
+        
+        <el-form-item>
+          <el-button type="primary" @click="handleAutoLogin">娴嬭瘯鍏嶇櫥</el-button>
+          <el-button @click="handleReset">閲嶇疆</el-button>
+        </el-form-item>
+      </el-form>
+      
+      <el-divider />
+      
+      <div v-if="loginResult">
+        <h4>娴嬭瘯缁撴灉锛�</h4>
+        <el-card class="result-card">
+          <pre>{{ loginResult }}</pre>
+        </el-card>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import { getCodeImg } from "@/api/login";
+
+export default {
+  name: "QyWechatAutoLogin",
+  data() {
+    return {
+      form: {
+        code: ""
+      },
+      rules: {
+        code: [
+          { required: true, message: "璇疯緭鍏ユ巿鏉僀ode", trigger: "blur" }
+        ]
+      },
+      loginResult: null
+    };
+  },
+  methods: {
+    /** 娴嬭瘯鍏嶇櫥 */
+    handleAutoLogin() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          // 璋冪敤浼佷笟寰俊鍏嶇櫥鎺ュ彛
+          this.$axios
+            .post("/system/qywechat/autoLogin", { code: this.form.code })
+            .then(response => {
+              this.loginResult = response;
+              if (response.code === 200) {
+                this.$modal.msgSuccess("鍏嶇櫥鎴愬姛");
+              } else {
+                this.$modal.msgError(response.msg || "鍏嶇櫥澶辫触");
+              }
+            })
+            .catch(error => {
+              this.loginResult = error;
+              this.$modal.msgError("璇锋眰寮傚父锛�" + error.message);
+            });
+        }
+      });
+    },
+    
+    /** 閲嶇疆琛ㄥ崟 */
+    handleReset() {
+      this.$refs["form"].resetFields();
+      this.loginResult = null;
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.form-tip {
+  font-size: 12px;
+  color: #999;
+  margin-top: 5px;
+}
+
+.result-card {
+  background-color: #f5f5f5;
+  pre {
+    white-space: pre-wrap;
+    word-wrap: break-word;
+    margin: 0;
+  }
+}
+</style>
\ No newline at end of file
diff --git "a/\344\274\201\344\270\232\345\276\256\344\277\241\345\205\215\347\231\273\345\212\237\350\203\275\344\275\277\347\224\250\350\257\264\346\230\216.md" "b/\344\274\201\344\270\232\345\276\256\344\277\241\345\205\215\347\231\273\345\212\237\350\203\275\344\275\277\347\224\250\350\257\264\346\230\216.md"
new file mode 100644
index 0000000..4c7d22b
--- /dev/null
+++ "b/\344\274\201\344\270\232\345\276\256\344\277\241\345\205\215\347\231\273\345\212\237\350\203\275\344\275\277\347\224\250\350\257\264\346\230\216.md"
@@ -0,0 +1,155 @@
+# 浼佷笟寰俊鍏嶇櫥鍔熻兘浣跨敤璇存槑
+
+## 鍔熻兘姒傝堪
+
+浼佷笟寰俊鍏嶇櫥鍔熻兘鍏佽鐢ㄦ埛閫氳繃浼佷笟寰俊瀹㈡埛绔洿鎺ョ櫥褰曠郴缁燂紝鏃犻渶杈撳叆鐢ㄦ埛鍚嶅拰瀵嗙爜锛屾彁鍗囩敤鎴蜂綋楠屻��
+
+## 瀹炵幇鍘熺悊
+
+1. 鐢ㄦ埛鍦ㄤ紒涓氬井淇″鎴风涓闂郴缁烾RL
+2. 绯荤粺妫�娴嬪埌浼佷笟寰俊鐜锛岃嚜鍔ㄨ烦杞埌浼佷笟寰俊鎺堟潈椤甸潰
+3. 鐢ㄦ埛纭鎺堟潈鍚庯紝浼佷笟寰俊杩斿洖鎺堟潈code
+4. 绯荤粺閫氳繃code鑾峰彇鐢ㄦ埛淇℃伅骞跺畬鎴愮櫥褰�
+
+## 鏂囦欢缁撴瀯
+
+```
+鍓嶇锛�
+- app/pages/qylogin.vue          # 绉诲姩绔厤鐧婚〉闈�
+- ruoyi-ui/src/views/system/qywechat/autologin.vue  # PC绔厤鐧绘祴璇曢〉闈�
+- ruoyi-ui/src/router/modules/qywechat.js  # 璺敱閰嶇疆
+
+鍚庣锛�
+- ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/QyWechatLoginController.java  # 鍏嶇櫥鎺у埗鍣�
+- ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java  # 鐢ㄦ埛Mapper鎺ュ彛
+- ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml  # 鐢ㄦ埛Mapper XML
+- ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java  # 鐢ㄦ埛鏈嶅姟鎺ュ彛
+- ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java  # 鐢ㄦ埛鏈嶅姟瀹炵幇
+```
+
+## 閰嶇疆瑕佹眰
+
+### 1. 浼佷笟寰俊閰嶇疆
+
+鍦ㄧ郴缁熼厤缃腑娣诲姞浠ヤ笅閰嶇疆椤癸細
+
+```sql
+-- 浼佷笟寰俊鍚敤寮�鍏�
+INSERT INTO sys_config VALUES (NULL, '浼佷笟寰俊鍚敤寮�鍏�', 'qy_wechat.enable', 'true', 'Y', '绯荤粺鍐呯疆', '鏄惁鍚敤浼佷笟寰俊鍔熻兘', '1', '1', '2025-12-14 10:00:00', 'admin', '2025-12-14 10:00:00', 'admin', NULL);
+
+-- 浼佷笟寰俊CorpID
+INSERT INTO sys_config VALUES (NULL, '浼佷笟寰俊CorpID', 'qy_wechat.corp_id', 'your_corp_id', 'Y', '绯荤粺鍐呯疆', '浼佷笟寰俊浼佷笟ID', '1', '1', '2025-12-14 10:00:00', 'admin', '2025-12-14 10:00:00', 'admin', NULL);
+
+-- 浼佷笟寰俊搴旂敤Secret锛堢敤浜庤幏鍙栫敤鎴蜂俊鎭級
+INSERT INTO sys_config VALUES (NULL, '浼佷笟寰俊搴旂敤Secret', 'qy_wechat.corp_secret', 'your_corp_secret', 'Y', '绯荤粺鍐呯疆', '浼佷笟寰俊搴旂敤瀵嗛挜', '1', '1', '2025-12-14 10:00:00', 'admin', '2025-12-14 10:00:00', 'admin', NULL);
+
+-- 浼佷笟寰俊灏忕▼搴廠ecret锛堢敤浜庤幏鍙朅ccessToken锛�
+INSERT INTO sys_config VALUES (NULL, '浼佷笟寰俊灏忕▼搴廠ecret', 'qy_wechat.miniprogram_secret', 'your_miniprogram_secret', 'Y', '绯荤粺鍐呯疆', '浼佷笟寰俊鍏宠仈灏忕▼搴忓瘑閽�', '1', '1', '2025-12-14 10:00:00', 'admin', '2025-12-14 10:00:00', 'admin', NULL);
+
+-- 浼佷笟寰俊AgentId
+INSERT INTO sys_config VALUES (NULL, '浼佷笟寰俊AgentId', 'qy_wechat.agent_id', 'your_agent_id', 'Y', '绯荤粺鍐呯疆', '浼佷笟寰俊搴旂敤ID', '1', '1', '2025-12-14 10:00:00', 'admin', '2025-12-14 10:00:00', 'admin', NULL);
+```
+
+### 2. 鐢ㄦ埛缁戝畾
+
+闇�瑕佸皢绯荤粺鐢ㄦ埛涓庝紒涓氬井淇$敤鎴疯繘琛岀粦瀹氾紝鍦╜sys_user`琛ㄤ腑璁剧疆`qy_wechat_user_id`瀛楁銆�
+
+## 浣跨敤娴佺▼
+
+### 绉诲姩绔娇鐢�
+
+1. 鐢ㄦ埛鍦ㄤ紒涓氬井淇′腑鎵撳紑绯荤粺URL
+2. 绯荤粺鑷姩璺宠浆鍒板厤鐧婚〉闈� `/pages/qylogin`
+3. 椤甸潰妫�娴嬩紒涓氬井淇$幆澧冨苟鑾峰彇鎺堟潈code
+4. 璋冪敤鍚庣鍏嶇櫥鎺ュ彛瀹屾垚鐧诲綍
+
+### PC绔祴璇�
+
+1. 鐧诲綍绯荤粺鍚庡彴
+2. 杩涘叆銆愪紒涓氬井淇°��->銆愬厤鐧绘祴璇曘�戣彍鍗�
+3. 杈撳叆浼佷笟寰俊鎺堟潈code杩涜娴嬭瘯
+
+## 鎺ュ彛璇存槑
+
+### 鍏嶇櫥鎺ュ彛
+
+```
+POST /system/qywechat/autoLogin
+
+璇锋眰鍙傛暟锛�
+{
+  "code": "浼佷笟寰俊鎺堟潈code"
+}
+
+鍝嶅簲缁撴灉锛�
+{
+  "code": 200,
+  "msg": "鐧诲綍鎴愬姛",
+  "data": {
+    "token": "鐧诲綍浠ょ墝",
+    "user": {
+      // 鐢ㄦ埛淇℃伅
+    }
+  }
+}
+```
+
+## 娉ㄦ剰浜嬮」
+
+1. 浼佷笟寰俊鍏嶇櫥鍔熻兘浠呮敮鎸佸湪浼佷笟寰俊瀹㈡埛绔腑浣跨敤
+2. 闇�瑕佹纭厤缃紒涓氬井淇″簲鐢ㄧ殑鍙俊鍩熷悕
+3. 鐢ㄦ埛蹇呴』鍏堢粦瀹氫紒涓氬井淇¤处鍙锋墠鑳戒娇鐢ㄥ厤鐧诲姛鑳�
+4. 纭繚鏈嶅姟鍣ㄨ兘澶熻闂紒涓氬井淇PI鎺ュ彛
+
+## 甯歌闂
+
+### 1. 鎺堟潈澶辫触
+
+**闂鐜拌薄**锛氭彁绀�"鑾峰彇鐢ㄦ埛淇℃伅澶辫触"
+
+**瑙e喅鏂规**锛�
+- 妫�鏌ヤ紒涓氬井淇¢厤缃槸鍚︽纭�
+- 纭搴旂敤鐨勫彲淇″煙鍚嶉厤缃�
+- 妫�鏌ョ綉缁滄槸鍚﹁兘璁块棶浼佷笟寰俊API
+
+### 2. 鐢ㄦ埛鏈粦瀹�
+
+**闂鐜拌薄**锛氭彁绀�"璇ヤ紒涓氬井淇¤处鍙锋湭缁戝畾绯荤粺鐢ㄦ埛"
+
+**瑙e喅鏂规**锛�
+- 鍦ㄧ敤鎴风鐞嗕腑鎵惧埌瀵瑰簲鐢ㄦ埛
+- 璁剧疆`qy_wechat_user_id`瀛楁涓轰紒涓氬井淇$敤鎴稩D
+
+### 3. 鐧诲綍寮傚父
+
+**闂鐜拌薄**锛氭彁绀�"鐧诲綍寮傚父"
+
+**瑙e喅鏂规**锛�
+- 鏌ョ湅绯荤粺鏃ュ織瀹氫綅鍏蜂綋閿欒
+- 妫�鏌ョ敤鎴风姸鎬佹槸鍚︽甯�
+- 纭绯荤粺閰嶇疆鏄惁瀹屾暣
+
+## 鎵╁睍鍔熻兘
+
+### 1. 鑷姩璺宠浆閰嶇疆
+
+鍙互鍦ㄥ墠绔〉闈腑閰嶇疆鑷姩璺宠浆閫昏緫锛�
+
+```javascript
+// 妫�鏌ヤ紒涓氬井淇$幆澧�
+isWxWorkEnvironment() {
+  const userAgent = navigator.userAgent.toLowerCase()
+  return userAgent.includes('wxwork')
+}
+```
+
+### 2. 鐢ㄦ埛缁戝畾鎺ュ彛
+
+鍙互寮�鍙戠敤鎴风粦瀹氫紒涓氬井淇$殑鎺ュ彛锛�
+
+```java
+@PostMapping("/bind")
+public AjaxResult bindQyWechatUser(@RequestBody BindRequest request) {
+  // 瀹炵幇鐢ㄦ埛缁戝畾閫昏緫
+}
+```
\ No newline at end of file

--
Gitblit v1.9.1