import { DBModel } from "../models/index.js"; import ResponseUtil from "../utils/api_response.js"; import config from "../conf.json" with { type: "json" }; class HandlerUser { constructor() { } // 获取微信的手机号码 async wxGetPhoneNumber(ctx) { try { const { code, appId } = ctx.request.body; if (!code || !appId) { return ResponseUtil.badRequest(ctx, "缺少手机号凭证 code 或 appId"); } let app = config.app[appId]; if (!app) { return ResponseUtil.badRequest(ctx, `未配置 appId: ${appId}`); } // 获取access_token const client_credential_url = `https://api.weixin.qq.com/cgi-bin/token?appid=${app.appid}&secret=${app.secret}&grant_type=client_credential`; const fetch = (await import("node-fetch")).default; let sessionRes = await fetch(client_credential_url); const resp = await sessionRes.json(); if (!resp.access_token) { return ResponseUtil.internalError(ctx, "获取微信 access_token 失败"); } // 获取phoneNumber const phoneUrl = `https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=${resp.access_token}`; const phoneRes = await fetch(phoneUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code: code }) }); const phoneData = await phoneRes.json(); if (phoneData.errcode) { return ResponseUtil.error(ctx, `获取手机号失败: ${phoneData.errmsg}`, null, 400); } // 从上图获取手机号 const phoneNumber = phoneData.phone_info?.phoneNumber; return ResponseUtil.success(ctx, { phoneNumber }, "获取手机号成功"); } catch (err) { return ResponseUtil.internalError(ctx, err.message); } } // 微信登录 async wxSignin(ctx) { try { const { code, phoneNumber, name, appId } = ctx.request.body; if (!code || !appId) { return ResponseUtil.badRequest(ctx, "缺少微信登录凭证 code 或 appId"); } let app = config.app[appId]; if (!app) { return ResponseUtil.badRequest(ctx, `未配置 appId: ${appId}`); } // 通过 code 换取 openid/session_key const sessionUrl = `https://api.weixin.qq.com/sns/jscode2session?appid=${app.appid}&secret=${app.secret}&js_code=${code}&grant_type=authorization_code`; const wxSessionRes = await fetch(sessionUrl); const sessionData = await wxSessionRes.json(); if (sessionData.errcode) { return ResponseUtil.error(ctx, `微信接口错误: ${sessionData.errmsg}`, null, 400); } const { openid } = sessionData; if (!openid) { return ResponseUtil.error(ctx, "微信登录失败,未获取到 openid", null, 400); } // 使用openid和phoneNumber查询用户 let key = `app.${appId}.wxopenid`; let user = await DBModel.User.findOne({ [key]: openid }); if (!user) { if (!phoneNumber) { return ResponseUtil.badRequest(ctx, "缺少手机号"); } user = await DBModel.User.findOne({ "profile.mobile": phoneNumber }); if (!user) { const newUser = { profile: { name: name || phoneNumber, mobile: phoneNumber, }, status: { account: "normal", }, app: {}, }; newUser.app[appId] = { role: ["user"], wxopenid: openid }; user = await DBModel.User.setUser(newUser); } } // if (user) { if (phoneNumber && phoneNumber.length > 0 && user.profile.mobile !== phoneNumber) { user.profile.mobile = phoneNumber; } if (!(appId in user.app)) { user.app[appId] = { role: ["user"], wxopenid: openid }; } user.app[appId].wxopenid = openid; } else { return ResponseUtil.internalError(ctx, "用户不存在"); } // 更新Token const isTokenValid = user.security.token && user.security.tokenExpiry && new Date() < user.security.tokenExpiry; if (!isTokenValid) { const token = await this.genToken(user._id.toString()); user.security.token = token; } user.security.tokenExpiry = new Date(Date.now() + 15 * 24 * 60 * 60 * 1000); await user.save(); // 安全起见删除密码相关字段 delete user.security.passwd; delete user.security.passwdSalt; return ResponseUtil.success(ctx, { user }, "登录成功"); } catch (err) { return ResponseUtil.internalError(ctx, err.message); } } // update user async updateUser(ctx) { try { const userInfo = ctx.request.body; if (!userInfo) { return ResponseUtil.badRequest(ctx, "缺少用户信息"); } // 从 token 获取当前用户 const token = ctx.request.body?.token || ctx.request.query?.token || ctx.header?.authorization || ctx.header?.token; if (!token) { return ResponseUtil.badRequest(ctx, "缺少 token"); } const user = await DBModel.User.findOne({ "security.token": token }); if (!user) { return ResponseUtil.unauthorized(ctx, "用户未登录或 token 无效"); } // 检查 token 是否过期 if (user.security.tokenExpiry && new Date() > user.security.tokenExpiry) { return ResponseUtil.unauthorized(ctx, "登录已过期,请重新登录"); } const updatedUser = await DBModel.User.updateFromUserInfo( user._id, userInfo ); if (!updatedUser) { return ResponseUtil.internalError(ctx, "更新用户失败"); } // 安全起见删除密码相关字段 const safeUser = updatedUser.toObject(); delete safeUser.security?.passwd; delete safeUser.security?.passwdSalt; return ResponseUtil.success(ctx, { user: safeUser }, "更新成功"); } catch (err) { return ResponseUtil.internalError(ctx, err.message); } } // 退出登录 async signout(ctx) { const token = ctx.request.body?.token || ctx.request.query?.token || ctx.header?.authorization || ctx.header?.token; if (!token) { return ResponseUtil.badRequest(ctx, "缺少 token"); } const user = await DBModel.User.findOne({ "security.token": token }); if (user) { user.security.token = null; user.security.tokenExpiry = null; await user.save(); } return ResponseUtil.success(ctx, null, "退出登录成功"); } // 获取用户信息 async userInfo(ctx) { try { const { token, userId } = ctx.request.body; if (!token && !userId) { return ResponseUtil.badRequest(ctx, "缺少 token 或 userId"); } let user = null; if (token) { user = await DBModel.User.findOne({ "security.token": token }); } else { user = await DBModel.User.findOne({ "_id": userId }); } if (!user) { return ResponseUtil.unauthorized(ctx, "用户未登录或 token 无效"); } const isTokenValid = user.security.token && user.security.tokenExpiry && new Date() < user.security.tokenExpiry; if (!isTokenValid) { return ResponseUtil.unauthorized(ctx, "登录已过期,请重新登录"); } // 安全起见删除密码相关字段 delete user.security.passwd; delete user.security.passwdSalt; return ResponseUtil.success(ctx, { user }, "获取用户信息成功"); } catch (err) { return ResponseUtil.internalError(ctx, err.message); } } // 获取用户列表 async userList(ctx) { try { const { page = 1, pageSize = 100 } = ctx.request.body; // 从 token 获取当前用户 const token = ctx.request.body?.token || ctx.request.query?.token || ctx.header?.authorization || ctx.header?.token; // 通过token获取用户 const user = await DBModel.User.findOne({ "security.token": token }); if (!user) { return ResponseUtil.unauthorized(ctx, "用户未登录或 token 无效"); } if (!('wxapp-escort-admin' in user.app)) { return ResponseUtil.unauthorized(ctx, "用户无管理员权限"); } const isTokenValid = user.security.token && user.security.tokenExpiry && new Date() < user.security.tokenExpiry; if (!isTokenValid) { return ResponseUtil.unauthorized(ctx, "登录已过期,请重新登录"); } // 查询所有user.app包含wxapp-escort的用户 const users = await DBModel.User.find({ "app.wxapp-escort": { $exists: true } }) .skip((page - 1) * pageSize) .limit(pageSize); // 安全起见删除密码相关字段 users.forEach(u => { delete u.security.passwd; delete u.security.passwdSalt; }); return ResponseUtil.success(ctx, { users }, "获取用户列表成功"); } catch (err) { return ResponseUtil.internalError(ctx, err.message); } } // 生成 token async genToken(uid) { const crypto = await import("crypto"); const hash = crypto.createHash("md5"); hash.update(uid + Date.now() + Math.random()); return hash.digest("hex"); } } export { HandlerUser };