2026-07-27 11:28:33 +08:00

851 lines
26 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 || "更新失败" };
}
}