369 lines
8.9 KiB
JavaScript
369 lines
8.9 KiB
JavaScript
const jwtUtils = require("../utils/jwt-utils");
|
||
const logger = require("../utils/logger");
|
||
|
||
/**
|
||
* 认证模块
|
||
* 处理用户登录、token 刷新等功能
|
||
*/
|
||
|
||
/**
|
||
* 用户登录
|
||
* @param {Object} params - 登录参数 { username, password, corpId }
|
||
* @param {Object} db - 数据库实例
|
||
* @returns {Object} 登录结果
|
||
*/
|
||
async function login(params, db) {
|
||
const { username, password, corpId } = params;
|
||
|
||
if (!username || !password) {
|
||
return {
|
||
success: false,
|
||
message: "用户名和密码不能为空",
|
||
};
|
||
}
|
||
|
||
// 查询用户信息
|
||
const user = await db.collection("corp-member").findOne(
|
||
{ corpId, mobile: username } // 查询条件
|
||
);
|
||
|
||
if (!user) {
|
||
logger.warn(`登录失败: 用户不存在 - ${username}`);
|
||
return {
|
||
success: false,
|
||
message: "用户名或密码错误",
|
||
};
|
||
}
|
||
|
||
// 验证密码(这里假设密码已加密,实际应该使用 bcrypt 等加密算法)
|
||
if (user.password !== password) {
|
||
logger.warn(`登录失败: 密码错误 - ${username}`);
|
||
return {
|
||
success: false,
|
||
message: "用户名或密码错误",
|
||
};
|
||
}
|
||
|
||
// 检查用户状态
|
||
if (user.status === "disabled") {
|
||
return {
|
||
success: false,
|
||
message: "账号已被禁用",
|
||
};
|
||
}
|
||
|
||
// 生成 token
|
||
const tokens = jwtUtils.generateTokens({
|
||
userId: user._id.toString(),
|
||
username: user.username,
|
||
corpId: user.corpId,
|
||
role: user.role || "user",
|
||
});
|
||
|
||
// 更新最后登录时间
|
||
await usersCollection.updateOne(
|
||
{ _id: user._id },
|
||
{
|
||
$set: {
|
||
lastLoginTime: new Date(),
|
||
lastLoginIp: params.ip || "",
|
||
},
|
||
}
|
||
);
|
||
|
||
logger.info(`用户登录成功: ${username}`);
|
||
|
||
return {
|
||
success: true,
|
||
message: "登录成功",
|
||
data: {
|
||
accessToken: tokens.accessToken,
|
||
refreshToken: tokens.refreshToken,
|
||
expiresIn: tokens.expiresIn,
|
||
tokenType: "Bearer",
|
||
userInfo: {
|
||
userId: user._id.toString(),
|
||
username: user.username,
|
||
corpId: user.corpId,
|
||
role: user.role || "user",
|
||
nickname: user.nickname || user.username,
|
||
},
|
||
},
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 刷新 token
|
||
* @param {Object} params - 刷新参数 { refreshToken }
|
||
* @returns {Object} 刷新结果
|
||
*/
|
||
async function refreshToken(params) {
|
||
const { refreshToken } = params;
|
||
|
||
if (!refreshToken) {
|
||
return {
|
||
success: false,
|
||
message: "刷新令牌不能为空",
|
||
};
|
||
}
|
||
|
||
// 验证并生成新的访问令牌
|
||
const result = jwtUtils.refreshAccessToken(refreshToken);
|
||
|
||
logger.info("Token 刷新成功");
|
||
|
||
return {
|
||
success: true,
|
||
message: "Token 刷新成功",
|
||
data: {
|
||
accessToken: result.accessToken,
|
||
expiresIn: result.expiresIn,
|
||
tokenType: "Bearer",
|
||
},
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 退出登录
|
||
* @param {Object} params - 参数
|
||
* @param {Object} user - 当前用户信息(来自 JWT)
|
||
* @returns {Object} 退出结果
|
||
*/
|
||
async function logout(params, user) {
|
||
// 可以在这里实现 token 黑名单机制
|
||
// 将当前 token 加入黑名单,使其失效
|
||
|
||
logger.info(`用户退出登录: ${user.username}`);
|
||
|
||
return {
|
||
success: true,
|
||
message: "退出成功",
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 小程序授权登录(支付宝/微信)
|
||
* @param {Object} params - 登录参数 { authCode, platform, corpId }
|
||
* @param {Object} db - 数据库实例
|
||
* @returns {Object} 登录结果
|
||
*/
|
||
async function miniProgramLogin(params, db) {
|
||
const { authCode, platform = "alipay", corpId } = params;
|
||
|
||
if (!authCode) {
|
||
return {
|
||
success: false,
|
||
message: "授权码不能为空",
|
||
};
|
||
}
|
||
|
||
try {
|
||
// 这里需要调用支付宝/微信的接口获取用户信息
|
||
// 由于没有具体的获取用户信息的逻辑,我们假设通过 authCode 可以获取到 userId
|
||
// 实际项目中,应该调用对应平台的 API 来验证 authCode 并获取用户信息
|
||
|
||
// 临时方案:使用 authCode 作为用户标识
|
||
// 在实际应用中,需要调用支付宝 API: alipay.system.oauth.token
|
||
const userId = authCode; // 这里应该是从支付宝返回的真实 userId
|
||
|
||
// 查询或创建小程序用户
|
||
const usersCollection = db.collection("miniapp_users");
|
||
let user = await usersCollection.findOne({
|
||
userId,
|
||
platform,
|
||
...(corpId && { corpId }),
|
||
});
|
||
|
||
if (!user) {
|
||
// 首次登录,创建用户记录
|
||
const newUser = {
|
||
userId,
|
||
platform,
|
||
corpId: corpId || "",
|
||
role: "customer",
|
||
status: "active",
|
||
createTime: new Date(),
|
||
lastLoginTime: new Date(),
|
||
lastLoginIp: params.ip || "",
|
||
};
|
||
|
||
const result = await usersCollection.insertOne(newUser);
|
||
user = { _id: result.insertedId, ...newUser };
|
||
|
||
logger.info(`小程序新用户注册: ${userId}`);
|
||
} else {
|
||
// 更新最后登录时间
|
||
await usersCollection.updateOne(
|
||
{ _id: user._id },
|
||
{
|
||
$set: {
|
||
lastLoginTime: new Date(),
|
||
lastLoginIp: params.ip || "",
|
||
},
|
||
}
|
||
);
|
||
|
||
logger.info(`小程序用户登录: ${userId}`);
|
||
}
|
||
|
||
// 检查用户状态
|
||
if (user.status === "disabled") {
|
||
return {
|
||
success: false,
|
||
message: "账号已被禁用",
|
||
};
|
||
}
|
||
|
||
// 生成 JWT token
|
||
const tokens = jwtUtils.generateTokens({
|
||
userId: user._id.toString(),
|
||
miniappUserId: user.userId,
|
||
platform: user.platform,
|
||
corpId: user.corpId,
|
||
role: user.role || "customer",
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
message: "登录成功",
|
||
data: {
|
||
accessToken: tokens.accessToken,
|
||
refreshToken: tokens.refreshToken,
|
||
expiresIn: tokens.expiresIn,
|
||
tokenType: "Bearer",
|
||
userInfo: {
|
||
userId: user._id.toString(),
|
||
miniappUserId: user.userId,
|
||
platform: user.platform,
|
||
corpId: user.corpId,
|
||
role: user.role || "customer",
|
||
},
|
||
},
|
||
};
|
||
} catch (error) {
|
||
logger.error("小程序登录失败:", error);
|
||
return {
|
||
success: false,
|
||
message: "登录失败: " + error.message,
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取当前用户信息
|
||
* @param {Object} user - 当前用户信息(来自 JWT)
|
||
* @param {Object} db - 数据库实例
|
||
* @returns {Object} 用户信息
|
||
*/
|
||
async function getCurrentUser(user, db) {
|
||
// 判断是小程序用户还是普通用户
|
||
if (user.miniappUserId) {
|
||
// 小程序用户
|
||
const usersCollection = db.collection("miniapp_users");
|
||
const userInfo = await usersCollection.findOne({
|
||
_id: require("mongodb").ObjectId.createFromHexString(user.userId),
|
||
});
|
||
|
||
if (!userInfo) {
|
||
return {
|
||
success: false,
|
||
message: "用户不存在",
|
||
};
|
||
}
|
||
|
||
return {
|
||
success: true,
|
||
data: {
|
||
userId: userInfo._id.toString(),
|
||
miniappUserId: userInfo.userId,
|
||
platform: userInfo.platform,
|
||
corpId: userInfo.corpId,
|
||
role: userInfo.role || "customer",
|
||
status: userInfo.status,
|
||
createTime: userInfo.createTime,
|
||
lastLoginTime: userInfo.lastLoginTime,
|
||
},
|
||
};
|
||
} else {
|
||
// 普通用户
|
||
const usersCollection = db.collection("users");
|
||
const userInfo = await usersCollection.findOne(
|
||
{ _id: require("mongodb").ObjectId.createFromHexString(user.userId) },
|
||
{ projection: { password: 0 } } // 不返回密码
|
||
);
|
||
|
||
if (!userInfo) {
|
||
return {
|
||
success: false,
|
||
message: "用户不存在",
|
||
};
|
||
}
|
||
|
||
return {
|
||
success: true,
|
||
data: {
|
||
userId: userInfo._id.toString(),
|
||
username: userInfo.username,
|
||
corpId: userInfo.corpId,
|
||
role: userInfo.role || "user",
|
||
nickname: userInfo.nickname || userInfo.username,
|
||
email: userInfo.email,
|
||
phone: userInfo.phone,
|
||
avatar: userInfo.avatar,
|
||
status: userInfo.status,
|
||
createTime: userInfo.createTime,
|
||
lastLoginTime: userInfo.lastLoginTime,
|
||
},
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 主路由处理函数
|
||
* @param {Object} params - 请求参数
|
||
* @param {Object} db - 数据库实例
|
||
* @param {Object} user - 当前用户信息(可选)
|
||
* @returns {Object} 处理结果
|
||
*/
|
||
async function main(params, db, user) {
|
||
const { type } = params;
|
||
|
||
switch (type) {
|
||
case "login":
|
||
return await login(params, db);
|
||
|
||
case "miniProgramLogin":
|
||
return await miniProgramLogin(params, db);
|
||
|
||
case "refreshToken":
|
||
return await refreshToken(params);
|
||
|
||
case "logout":
|
||
if (!user) {
|
||
return { success: false, message: "未登录" };
|
||
}
|
||
return await logout(params, user);
|
||
|
||
case "getCurrentUser":
|
||
if (!user) {
|
||
return { success: false, message: "未登录" };
|
||
}
|
||
return await getCurrentUser(user, db);
|
||
|
||
default:
|
||
return {
|
||
success: false,
|
||
message: "未知的操作类型",
|
||
};
|
||
}
|
||
}
|
||
|
||
module.exports = {
|
||
main,
|
||
login,
|
||
miniProgramLogin,
|
||
refreshToken,
|
||
logout,
|
||
getCurrentUser,
|
||
};
|