/** * Node.js + Express 企业微信 OAuth 后端示例 * * 安装依赖: * npm install express axios cors dotenv morgan * * 启动服务: * node server.js */ require('dotenv').config(); const express = require('express'); const cors = require('cors'); const morgan = require('morgan'); const axios = require('axios'); const path = require('path'); const app = express(); // 中间件 app.use(cors()); app.use(morgan('combined')); app.use(express.json()); app.use(express.static(path.join(__dirname, '../dist'))); // 企业微信配置 const WECOM_CONFIG = { corpId: process.env.WECOM_CORP_ID, appSecret: process.env.WECOM_APP_SECRET, agentId: process.env.WECOM_AGENT_ID }; // 缓存 access_token let accessTokenCache = { token: null, expireTime: 0 }; /** * 获取企业微信访问令牌 */ async function getAccessToken() { const now = Date.now(); // 如果 token 未过期,直接返回缓存 if (accessTokenCache.token && accessTokenCache.expireTime > now) { return accessTokenCache.token; } try { const response = await axios.get('https://qyapi.weixin.qq.com/cgi-bin/gettoken', { params: { corpid: WECOM_CONFIG.corpId, corpsecret: WECOM_CONFIG.appSecret } }); if (response.data.errcode === 0) { const token = response.data.access_token; const expiresIn = response.data.expires_in || 7200; accessTokenCache = { token: token, expireTime: now + (expiresIn - 300) * 1000 // 提前5分钟刷新 }; return token; } else { throw new Error(`获取 token 失败: ${response.data.errmsg}`); } } catch (error) { console.error('获取 access_token 错误:', error.message); throw error; } } /** * API: 使用授权码获取用户信息 */ app.post('/api/wecom/getUserInfo', async (req, res) => { const { code } = req.body; if (!code) { return res.status(400).json({ code: -1, message: '缺少授权码' }); } try { const accessToken = await getAccessToken(); // 获取用户信息 const response = await axios.get('https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail', { params: { access_token: accessToken, code: code } }); if (response.data.errcode === 0) { const userData = { userid: response.data.userid, name: response.data.name, mobile: response.data.mobile || '', email: response.data.email || '', department: (response.data.department && response.data.department[0]) || '', position: response.data.position || '', gender: response.data.gender || 0, avatar: response.data.avatar || '', extattr: response.data.extattr || {} }; // 可选: 将用户信息存储到数据库 // await saveUserToDatabase(userData); res.json({ code: 0, message: 'success', data: userData }); } else { res.status(401).json({ code: response.data.errcode, message: response.data.errmsg || '获取用户信息失败' }); } } catch (error) { console.error('获取用户信息错误:', error.message); res.status(500).json({ code: -1, message: '获取用户信息失败: ' + error.message }); } }); /** * API: 获取用户详情 */ app.get('/api/wecom/userDetail/:userId', async (req, res) => { const { userId } = req.params; try { const accessToken = await getAccessToken(); const response = await axios.get('https://qyapi.weixin.qq.com/cgi-bin/user/get', { params: { access_token: accessToken, userid: userId } }); if (response.data.errcode === 0) { res.json({ code: 0, message: 'success', data: response.data }); } else { res.status(404).json({ code: response.data.errcode, message: response.data.errmsg }); } } catch (error) { console.error('获取用户详情错误:', error.message); res.status(500).json({ code: -1, message: '获取用户详情失败' }); } }); /** * API: 验证用户权限 */ app.get('/api/wecom/verify/:userId', async (req, res) => { const { userId } = req.params; try { // 实现自己的权限验证逻辑 // 例如: 检查用户是否在允许的部门列表中 const authorized = await checkUserPermission(userId); res.json({ code: 0, message: 'success', data: { authorized: authorized } }); } catch (error) { console.error('验证用户权限错误:', error.message); res.status(500).json({ code: -1, message: '验证失败' }); } }); /** * API: 同步用户到本地系统 */ app.post('/api/user/sync', async (req, res) => { const { userid, name, mobile } = req.body; try { // 实现自己的用户同步逻辑 // 例如: 保存到数据库、更新缓存等 console.log(`同步用户: ${userid}, ${name}, ${mobile}`); res.json({ code: 0, message: 'success', data: { synced: true } }); } catch (error) { console.error('同步用户错误:', error.message); res.status(500).json({ code: -1, message: '同步用户失败' }); } }); /** * 健康检查 */ app.get('/api/health', (req, res) => { res.json({ code: 0, message: 'OK', timestamp: new Date().toISOString() }); }); /** * SPA 路由处理 */ app.get('*', (req, res) => { res.sendFile(path.join(__dirname, '../dist/index.html')); }); /** * 错误处理中间件 */ app.use((error, req, res, next) => { console.error('未捕获的错误:', error); res.status(500).json({ code: -1, message: '服务器内部错误' }); }); /** * 权限检查辅助函数 (示例) */ async function checkUserPermission(userId) { // 实现你的权限验证逻辑 // 例如: // 1. 检查用户是否在允许列表中 // 2. 检查用户的部门是否被授权 // 3. 查询数据库的权限配置 // 这里返回 true 作为示例 return true; } /** * 数据库保存函数 (示例) */ async function saveUserToDatabase(userData) { // 实现保存到数据库的逻辑 // 例如使用 MongoDB: // const user = new User(userData); // await user.save(); console.log('保存用户到数据库:', userData); } // 启动服务器 const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`服务器运行在 http://localhost:${PORT}`); console.log(`企业微信配置:`); console.log(` CorpID: ${WECOM_CONFIG.corpId}`); console.log(` AgentID: ${WECOM_CONFIG.agentId}`); }); module.exports = app;