start
This commit is contained in:
191
handler/users.js
Normal file
191
handler/users.js
Normal file
@@ -0,0 +1,191 @@
|
||||
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, 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);
|
||||
}
|
||||
|
||||
let user = await DBModel.User.findOne({ "social.wechat.openid": openid });
|
||||
if (!user) {
|
||||
if (!phoneNumber) {
|
||||
return ResponseUtil.badRequest(ctx, "缺少手机号");
|
||||
}
|
||||
|
||||
const newUser = {
|
||||
profile: { name: phoneNumber, mobile: phoneNumber, },
|
||||
social: {
|
||||
wechat: { openid: openid },
|
||||
},
|
||||
status: { account: "normal", },
|
||||
app: {
|
||||
attendant: { role: ["patient"], },
|
||||
},
|
||||
};
|
||||
user = await DBModel.User.setUser(newUser);
|
||||
} else if (phoneNumber && phoneNumber.length > 0 && user.profile.mobile !== phoneNumber) {
|
||||
user.profile.mobile = phoneNumber;
|
||||
}
|
||||
|
||||
const token = await this.genToken(user._id.toString());
|
||||
user.security.token = token;
|
||||
user.security.tokenExpiry = new Date(Date.now() + 7 * 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, "退出登录成功");
|
||||
}
|
||||
|
||||
// 生成 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 };
|
||||
Reference in New Issue
Block a user