/**
* 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;