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