const jwt = require("jsonwebtoken"); const { ObjectId } = require("mongodb"); const utils = require("./account-utils.js"); const api = require("../../api"); const jwtUtils = require("../../utils/jwt-utils"); const tokenStore = require("../../middleware/useTokenStore"); const SECRET_KEY = process.env.CONFIG_JWT_SECRET_KEY || "YOUCAN365"; const TOKEN_EXPIRES = process.env.CONFIG_JWT_EXPIRE_TIME || "1d"; let db = null; // 自定义默认密码 const defaultPassword = "zytzyy6789"; exports.main = async (event, mongodb, req) => { db = mongodb; switch (event.type) { case "updatePassword": return await exports.updatePassword(event); case "login": return await exports.login(event, req); case "simpleLogin": return await exports.simpleLogin(event); case "loginout": return await loginout(event, req); case "loginWithToken": return await exports.loginWithToken(event); case "refreshToken": return await refreshToken(event); case "getLoginLog": return await getLoginLog(event); case "resetPassword": return await resetPassword(event); case "getAccountList": return await getAccountList(event); case "getAccountOptions": return await getAccountOptions(event); case "setAccountState": return await setAccountState(event); case "addCorpAccount": return await addCorpAccount(event); case "editCorpAccount": return await editCorpAccount(event); case "updateAccountDrugStoreId": return updateAccountDrugStoreId(event); } }; function getIp(req) { return req.headers['x-forwarded-for'] || req.headers['x-real-ip'] || req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress || ''; } // 修改密码 exports.updatePassword = async (event) => { const { corpId, username, oldPassword, newPassword } = event; try { // 查询员工账户里的account是否存在 const user = await db.collection("corp-member").findOne( { corpId, mobile: username } // 查询条件 ); // 如果不存在,则提示账户不存在 if (!user) { return { success: false, message: "账户不存在", }; } const { password = "" } = user || {}; if ( (password && password !== oldPassword) || (!password && oldPassword !== defaultPassword) ) { return { success: false, message: "原密码错误", }; } // 更新密码 await db.collection("corp-member").updateOne( { corpId, mobile: username }, // 查询条件 { $set: { password: newPassword, updateTime: Date.now() } } // 更新内容 ); return { success: true, message: "更新成功", }; } catch (error) { return { success: false, message: error.message, }; } }; exports.simpleLogin = async (event) => { const { corpId, username, password, fetchPharmacy } = event; const platform = typeof event.platform === 'string' ? event.platform.trim().toUpperCase() : ''; if (typeof corpId !== 'string' || corpId.trim() === '') { return { success: false, message: "机构ID不能为空" }; } if (typeof username !== 'string' || username.trim() === '') { return { success: false, message: "账号不能为空" }; } if (typeof password !== 'string' || password.trim() === '') { return { success: false, message: "密码不能为空" }; } try { const user = await db.collection("corp-member").findOne({ corpId, mobile: username }); if (!user) { return { success: false, message: "账户不存在或者密码不正确" }; } if (user.accountState !== "enable") { return { success: false, message: "账户未启用,请联系管理员" }; } const { password: dbPassword = "" } = user || {}; if (!dbPassword) { if (password === defaultPassword) { return { success: false, message: "密码未设置" }; } return { success: false, message: "默认密码不正确" }; } if (password !== dbPassword) { return { success: false, message: "账户不存在或者密码不正确" }; } if (fetchPharmacy && typeof user.drugStoreId === 'string' && user.drugStoreId.trim()) { const api = require("../../api"); const res = await api.getInternetHospitalApi({ type: "getDrugStoreList", storeId: user.drugStoreId, pageSize: 1, }); const drugStore = Array.isArray(res.list) && res.list[0] ? res.list[0] : null; user.drugStore = drugStore; } const { password: _password, ...data } = user; const loginId = new ObjectId().toString(); return { success: true, message: "登录成功", user: { ...data, loginId }, }; } catch (error) { return { success: false, message: error.message }; } }; // 登陆账号 exports.login = async (event, req) => { const { corpId, username, password, fetchPharmacy } = event; // 规范 platform:去空格并转为大写,方便后续判断(例如 'pc' / 'PC' / ' Pc ' 都视为 PC) const platform = typeof event.platform === 'string' ? event.platform.trim().toUpperCase() : ''; try { // 查询员工账户里的account是否存在 const user = await db.collection("corp-member").findOne( { corpId, mobile: username } // 查询条件 ); // 如果不存在,则提示账户不存在 if (!user) { return { success: false, message: "账户不存在或者密码不正确", }; } if (user.accountState !== "enable") { return { success: false, message: "账户未启用,请联系管理员" }; } const { password: dbPassword = "" } = user || {}; if (!dbPassword) { if (password === defaultPassword) { return { success: false, message: "密码未设置", }; } else { return { success: false, message: "默认密码不正确", }; } } if (password !== dbPassword) { return { success: false, message: "账户不存在或者密码不正确", }; } if (fetchPharmacy && typeof user.drugStoreId === 'string' && user.drugStoreId.trim()) { const api = require("../../api"); const res = await api.getInternetHospitalApi({ type: "getDrugStoreList", storeId: user.drugStoreId, pageSize: 1, }); const drugStore = Array.isArray(res.list) && res.list[0] ? res.list[0] : null; user.drugStore = drugStore; } const { password: _password, ...data } = user; const loginId = new ObjectId().toString(); // 使用统一的 JWT 工具生成 token const tokens = jwtUtils.generateTokens({ userId: user._id.toString(), platform: platform, username: user.mobile, corpId: corpId, role: user.roleIds || [], mobile: user.mobile, drugStoreId: user.drugStoreId || '', loginId: loginId }); await addLoginLog(data, loginId, platform, getIp(req), tokens.accessToken); return { success: true, message: "登录成功", user: { ...data, loginId }, accessToken: tokens.accessToken, refreshToken: tokens.refreshToken, expiresIn: tokens.expiresIn, tokenType: "Bearer", // 保持向后兼容,同时提供旧的 token 字段 token: tokens.accessToken }; } catch (error) { return { success: false, message: error.message, }; } }; exports.loginWithToken = async function (ctx) { const { token, corpId: reqCorpId } = ctx; if (typeof token !== "string" || token.trim() === "") { return { success: false, message: "token无效" }; } // 先尝试使用统一的 JWT 验证 let user = await getUserFromToken(token); // 如果统一验证失败,尝试使用旧的验证方式(向后兼容) if (!user) { user = await getUserFromTokenLegacy(token); } if (!user) { return { success: false, message: "登录已失效,请重新登录" }; } // 支持两种数据结构:新的 userId/username 和旧的 mobile const mobile = user.username || user.mobile; const userId = user.userId; const drugStoreId = user.drugStoreId; const corpId = user.corpId; if (!mobile || reqCorpId !== corpId) { return { success: false, message: "登录已失效,请重新登录" }; } // 构建查询条件 const query = { corpId, mobile }; if (drugStoreId) { query.drugStoreId = drugStoreId; } const account = await db.collection("corp-member").findOne(query); if (!account) { return { success: false, message: "登录已失效,请重新登录" }; } if (account.accountState !== "enable") { return { success: false, message: "账户未启用,请联系管理员" }; } // 获取药店信息 if (drugStoreId) { const api = require("../../api"); const res = await api.getInternetHospitalApi({ type: "getDrugStoreList", storeId: drugStoreId, pageSize: 1, }); const drugStore = Array.isArray(res.list) && res.list[0] ? res.list[0] : null; account.drugStore = drugStore; } return { success: true, user: account, message: "登录成功" }; }; async function loginout(ctx) { try { const query = { corpId: typeof ctx.corpId === "string" ? ctx.corpId.trim() : "", loginId: typeof ctx.loginId === "string" ? ctx.loginId.trim() : "", userId: typeof ctx.userId === "string" ? ctx.userId.trim() : "", type: { $in: ["login", "loginout"] }, }; const list = await db.collection("login-log").find(query).toArray(); const login = list.find((item) => item.type === "login"); const logout = list.find((item) => item.type === "loginout"); if (login && !logout) { const { _id, ...rest } = login; await db .collection("login-log") .insertOne({ ...rest, type: "loginout", createTime: Date.now() }); } return { success: true, message: "登出成功" }; } catch (e) { return { success: false, message: e.message }; } } // 使用统一的 JWT 验证(新) function getUserFromToken(token) { return new Promise((resolve) => { const secret = process.env.JWT_SECRET || 'your-secret-key'; jwt.verify(token, secret, (err, user) => { if (err) return resolve(null); resolve(user); }); }); } // 使用旧的 JWT 验证(向后兼容) function getUserFromTokenLegacy(token) { return new Promise((resolve) => { jwt.verify(token, SECRET_KEY, (err, user) => { if (err) return resolve(null); resolve(user); }); }); } // Token 刷新功能 async function refreshToken(ctx) { const { refreshToken } = ctx; if (!refreshToken) { return { success: false, message: '刷新令牌不能为空' }; } const result = jwtUtils.refreshAccessToken(refreshToken); return { success: true, message: 'Token 刷新成功', accessToken: result.accessToken, expiresIn: result.expiresIn, tokenType: 'Bearer' }; } async function getLoginLog(ctx) { const { corpId, accountId, userId } = ctx; try { if (corpId && typeof corpId !== "string") { return { success: false, message: "机构id无效" }; } if (accountId && typeof accountId !== "string") { return { success: false, message: "账号id无效" }; } if (userId && typeof userId !== "string") { return { success: false, message: "用户id无效" }; } const query = { corpId, accountId, userId, }; const page = Number.isInteger(ctx.page) && ctx.page > 0 ? ctx.page : 1; const pageSize = Number.isInteger(ctx.pageSize) && ctx.pageSize > 0 ? ctx.pageSize : 10; const total = await db.collection("login-log").countDocuments(query); const list = await db .collection("login-log") .find(query) .skip(page * pageSize - pageSize) .limit(pageSize) .sort({ createTime: -1 }) .toArray(); return { success: true, data: list, message: "获取登录日志成功", total }; } catch (e) { return { success: false, message: e.message }; } } async function addLoginLog(ctx, loginId, platform, ip, token) { const { corpId, _id, userid: userId } = ctx; try { const { insertedId } = await db .collection("login-log") .insertOne({ corpId, accountId: _id.toString(), userId, createTime: Date.now(), loginId, type: "login", ip: typeof ip === "string" ? ip.trim() : "", platform: ["PC", "MATEPAD"].includes(platform) ? platform : "", token }); if (insertedId) { tokenStore.setToken(_id.toString(), token); } return { success: true, message: "添加登录日志成功" }; } catch (e) { console.log('addLoginLog error:', e) return { success: false, message: e.message }; } } async function resetPassword(ctx) { // const { corpId, userId, accountId } = ctx; const corpId = typeof ctx.corpId === "string" ? ctx.corpId.trim() : ""; const userId = typeof ctx.userId === "string" ? ctx.userId.trim() : ""; const accountId = typeof ctx.accountId === "string" ? ctx.accountId.trim() : ""; if (!corpId || !userId || !accountId) { return { success: false, message: "参数错误,缺少必要的参数" }; } try { const query = { corpId, userid: userId }; if (ObjectId.isValid(accountId)) { query["$or"] = [{ _id: new ObjectId(accountId) }, { _id: accountId }]; } else { query._id = accountId; // 如果不是ObjectId,则直接使用accountId } const res = await db .collection("corp-member") .updateOne(query, { $set: { password: defaultPassword, updateTime: Date.now() }, }); if (res.modifiedCount === 1) { return { success: true, message: "重置密码成功", password: defaultPassword, }; } return { success: false, message: "重置密码失败", }; } catch (error) { return { success: false, message: error.message, }; } } async function addCorpAccount(ctx) { try { const { success, message, data } = utils.getAccountData(ctx); if (!success) { return { success, message }; } const { relatedHlwPeople, ...accountData } = data; if (/\W/.test(accountData.mobile)) { return { success: false, message: "登录账号不能包含特殊字符" }; } // 校验 phone 字段是否存在 if (!accountData.phone || typeof accountData.phone !== 'string' || accountData.phone.trim() === '') { return { success: false, message: "手机号不能为空" }; } const sameMobile = { corpId: data.corpId, mobile: data.mobile }; // 账号相同 const samePhone = { corpId: data.corpId, phone: data.phone }; // 手机号相同 const sameRelatedPeople = relatedHlwPeople ? [{ corpId: data.corpId, relatedHlwPeople }] : []; // 关联人员相同 const account = await db .collection("corp-member") .findOne( { $or: [sameMobile, samePhone, ...sameRelatedPeople] }, { projection: { mobile: 1, phone: 1, relatedHlwPeople: 1 } } ); if (account && account.mobile === data.mobile) { return { success: false, message: "该账号已存在" }; } else if (account && account.phone === data.phone) { return { success: false, message: "该手机号已被使用" }; } else if (account) { return { success: false, message: "该人员已关联其他账号" }; } if ( typeof relatedHlwPeople === "string" && relatedHlwPeople.trim() !== "" ) { const { success, message, data } = await api.getInternetHospitalApi({ type: "getHlwPersonList", page: 1, pageSize: 1, doctorNo: relatedHlwPeople, }); const doctor = Array.isArray(data) && data[0] ? data[0] : null; if (!success || !doctor) { return { success: false, message: message || "关联人员信息不正确或者不存在", }; } if (["doctor", "pharmacist"].includes(doctor.job)) { accountData[doctor.job === "doctor" ? "doctorNo" : "workNo"] = doctor.doctorNo; } } const res = await db.collection("corp-member").insertOne({ ...accountData, relatedHlwPeople, password: defaultPassword, createTime: Date.now(), accountState: "enable", }); return { success: true, message: "新增账户成功", id: res.insertedId }; } catch (e) { return { success: false, message: e.message || "新增账户失败" }; } } async function editCorpAccount(ctx) { if (typeof ctx.id !== "string" || ctx.id.trim() === "") { return { success: false, message: "账号id无效" }; } try { const { success, message, data } = utils.getAccountData(ctx, false); if (!success) { return { success, message }; } if (JSON.stringify(data) === "{}") { return { success: true, message: "没有需要更新的字段" }; } const record = await db.collection("corp-member").findOne( { $or: [ { corpId: typeof ctx.corpId == "string" ? ctx.corpId : "", _id: ObjectId.isValid(ctx.id) ? new ObjectId(ctx.id) : ctx.id, }, { corpId: typeof ctx.corpId == "string" ? ctx.corpId : "", _id: ctx.id, }, ], }, { projection: { _id: 1, mobile: 1, phone: 1, doctorNo: 1, workNo: 1, corpId: 1 } } ); if (!record) { return { success: false, message: "账号不存在" }; } const { relatedHlwPeople, mobile, userid, ...accountData } = data; if (/\W/.test(accountData.mobile)) { return { success: false, message: "登录账号不能包含特殊字符" }; } if ( record.mobile && accountData.mobile && accountData.mobile !== record.mobile ) { return { success: false, message: "登录账号不能修改" }; } const sameMobile = { corpId: data.corpId, mobile: data.mobile }; // 账号相同 const samePhone = accountData.phone ? { corpId: data.corpId, phone: accountData.phone } : null; // 手机号相同 const sameRelatedPeople = typeof relatedHlwPeople == "string" && relatedHlwPeople ? [{ corpId: data.corpId, relatedHlwPeople }] : []; // 关联人员相同 const queryConditions = [sameMobile, ...sameRelatedPeople]; if (samePhone) { queryConditions.push(samePhone); } const account = await db.collection("corp-member").findOne( { $or: queryConditions, _id: { $ne: record._id } }, // 排除当前账号 { projection: { mobile: 1, phone: 1, doctorNo: 1, workNo: 1 } } ); if (account && account.mobile === data.mobile) { return { success: false, message: "该账号已存在" }; } else if (account && accountData.phone && account.phone === accountData.phone) { return { success: false, message: "该手机号已被使用" }; } else if (account) { return { success: false, message: "该人员已关联其他账号" }; } if ( typeof relatedHlwPeople === "string" && relatedHlwPeople.trim() !== "" ) { const { success, message, data } = await api.getInternetHospitalApi({ type: "getHlwPersonList", corpId: record.corpId, page: 1, pageSize: 1, doctorNo: relatedHlwPeople, }); const doctor = Array.isArray(data) && data[0] ? data[0] : null; if (!success || !doctor) { return { success: false, message: message || "关联人员信息不正确或者不存在", }; } if (record.doctorNo) { accountData.doctorNo = ""; } if (record.workNo) { accountData.workNo = ""; } if (["doctor", "pharmacist"].includes(doctor.job)) { accountData[doctor.job === "doctor" ? "doctorNo" : "workNo"] = doctor.doctorNo; } accountData.relatedHlwPeople = relatedHlwPeople; } else if (relatedHlwPeople === "") { if (record.doctorNo) { accountData.doctorNo = ""; } if (record.workNo) { accountData.workNo = ""; } accountData.relatedHlwPeople = ""; } const res = await db .collection("corp-member") .updateOne({ _id: record._id }, { $set: accountData }); if (res.matchedCount === 0) { return { success: false, message: "修改账户失败" }; } return { success: true, message: "修改账户成功" }; } catch (e) { return { success: false, message: e.message || "修改账户失败" }; } } async function getAccountList(ctx) { const page = Number.isInteger(ctx.page) ? ctx.page : 1; const pageSize = Number.isInteger(ctx.pageSize) ? ctx.pageSize : 10; try { const query = { corpId: typeof ctx.corpId === "string" ? ctx.corpId : "" }; if (typeof ctx.roleId === "string" && ctx.roleId.trim() !== "") { query["roleIds"] = { $in: [ctx.roleId] }; } if (typeof ctx.name === "string" && ctx.name.trim() !== "") { query["anotherName"] = { $regex: ctx.name, $options: "i" }; } if (typeof ctx.mobile === "string" && ctx.mobile.trim() !== "") { query["mobile"] = { $regex: ctx.mobile.trim(), $options: "i" }; } if (typeof ctx.accountState === "boolean") { query["accountState"] = ctx.accountState ? "enable" : "disable"; } const list = await db .collection("corp-member") .aggregate([ { $match: query }, { $sort: { createTime: -1, _id: -1 } }, { $lookup: { from: "sys-role", localField: "roleIds", foreignField: "_id", pipeline: [{ $project: { roleName: 1, _id: 0 } }], as: "roles", }, }, { $lookup: { from: "login-log", let: { accountId: { $toString: "$_id" } }, pipeline: [ { $match: { $expr: { $eq: ["$accountId", "$$accountId"] }, type: "login", }, }, { $sort: { createTime: -1 } }, { $limit: 1 }, { $project: { _id: 0, createTime: 1, ip: 1 } }, ], as: "lastLogin", }, }, { $project: { _id: 1, anotherName: 1, mobile: 1, phone: 1, accountState: 1, roles: 1, roleIds: 1, lastLogin: 1, userid: 1, doctorNo: 1, workNo: 1, relatedHlwPeople: 1, }, }, { $skip: (page - 1) * pageSize }, { $limit: pageSize }, ]) .toArray(); const total = await db.collection("corp-member").countDocuments(query); return { success: true, message: "获取成功", list, total }; } catch (e) { return { success: false, message: e.message || "获取失败" }; } } async function setAccountState(ctx) { const { corpId, accountIds, state } = ctx; const accountIdList = Array.isArray(accountIds) ? accountIds.filter((i) => typeof i === "string") : []; try { const ids = []; for (const id of accountIdList) { if (ObjectId.isValid(id)) { ids.push(new ObjectId(id), id); } else if (typeof id === "string" && id.trim() !== "") { ids.push(id.trim()); } } if (typeof state !== "boolean") { return { success: false, message: "参数错误" }; } const res = await db .collection("corp-member") .updateMany( { _id: { $in: ids }, corpId: typeof corpId === "string" ? corpId : "" }, { $set: { accountState: state ? "enable" : "disable" } } ); return { success: true, message: "设置成功", data: res }; } catch (e) { return { success: false, message: e.message || "设置失败" }; } } async function getAccountOptions(ctx) { try { const query = { corpId: typeof ctx.corpId === "string" ? ctx.corpId : "" }; const list = await db .collection("corp-member") .find(query, { projection: { anotherName: 1, _id: 1 } }) .toArray(); return { success: true, message: "获取成功", list }; } catch (e) { return { success: false, message: e.message || "获取失败" }; } } async function updateAccountDrugStoreId(ctx) { const corpId = typeof ctx.corpId === "string" ? ctx.corpId.trim() : ""; const drugStoreId = typeof ctx.drugStoreId === "string" ? ctx.drugStoreId.trim() : ""; const oldDrugStoreId = typeof ctx.oldDrugStoreId === "string" ? ctx.oldDrugStoreId.trim() : ""; const accountId = typeof ctx.accountId === "string" ? ctx.accountId.trim() : ""; const oldAccountId = typeof ctx.oldAccountId === "string" ? ctx.oldAccountId.trim() : ""; try { let account = null; let oldAccount = null; if (accountId) { const query = ObjectId.isValid(accountId) ? { $or: [ { corpId, _id: new ObjectId(accountId) }, { corpId, _id: accountId }, ], } : { corpId, _id: accountId }; account = await db.collection("corp-member").findOne(query, { _id: 1 }); if (!account) { return { success: false, message: "关联账号不存在" }; } } if (oldAccountId) { const oldQuery = ObjectId.isValid(oldAccountId) ? { $or: [ { corpId, _id: new ObjectId(oldAccountId) }, { corpId, _id: oldAccountId }, ], } : { corpId, _id: oldAccountId }; oldAccount = await db .collection("corp-member") .findOne(oldQuery, { _id: 1 }); } if (oldAccount) { await db .collection("corp-member") .updateMany({ _id: oldAccount._id }, { $set: { drugStoreId: "" } }); } if (oldDrugStoreId && account) { await db .collection("corp-member") .updateMany( { corpId, drugStoreId: oldDrugStoreId, _id: { $ne: account._id } }, { $set: { drugStoreId: "" } } ); await db .collection("corp-member") .updateMany({ _id: account._id }, { $set: { drugStoreId } }); } else if (oldDrugStoreId) { await db .collection("corp-member") .updateMany( { corpId, drugStoreId: oldDrugStoreId }, { $set: { drugStoreId } } ); } else if (account) { await db .collection("corp-member") .updateMany({ _id: account._id }, { $set: { drugStoreId } }); } return { success: true, message: "更新成功" }; } catch (e) { return { success: false, message: e.message || "更新失败" }; } }