|
|
@@ -0,0 +1,343 @@
|
|
|
+import dayjs from 'dayjs';
|
|
|
+import { isEnv } from '../config/config.default.js';
|
|
|
+import { Log, User } from '../model/index.js';
|
|
|
+import { getTokenFu } from '../middleware/jwt.js';
|
|
|
+import { passWordJia, passWordJie } from '../util/pass.js';
|
|
|
+import resSend from '../util/resSend.js';
|
|
|
+import { generateCaptcha, ipLocResFu } from '../util/index.js';
|
|
|
+
|
|
|
+// 登录模块 需要做定时器处理,防止短时间多次发送
|
|
|
+let loginFlag: any = {};
|
|
|
+
|
|
|
+setTimeout(() => {
|
|
|
+ loginFlag = {};
|
|
|
+}, 60 * 1000 * 60 * 2);
|
|
|
+
|
|
|
+const loginTimeFu = (key: string) => {
|
|
|
+ if (loginFlag[key].loginFlagTime === 0) loginFlag[key].loginFlagTime = Date.now();
|
|
|
+ const nowTime = Date.now();
|
|
|
+ if (nowTime - loginFlag[key].loginFlagTime <= 60000) loginFlag[key].loginFlagNum++;
|
|
|
+};
|
|
|
+
|
|
|
+const user = {
|
|
|
+ getCode: async (req: any, res: any) => {
|
|
|
+ req.apiDescription = '用户模块-获取验证码';
|
|
|
+ const clientIp = ipLocResFu(req);
|
|
|
+
|
|
|
+ const captcha = generateCaptcha();
|
|
|
+ // 将验证码文本存入session(小写以便不区分大小写校验)
|
|
|
+ const captchaTxt = captcha.text.toLowerCase();
|
|
|
+ if (loginFlag[clientIp]) loginFlag[clientIp].loginFlagCode = captchaTxt;
|
|
|
+ else loginFlag[clientIp] = { loginFlagCode: captchaTxt };
|
|
|
+
|
|
|
+ // console.log('生成的验证码(开发调试用):', captchaTxt); // 调试时可查看
|
|
|
+
|
|
|
+ // 设置响应头,告诉浏览器这是SVG图片
|
|
|
+ res.type('svg');
|
|
|
+ res.send(captcha.data);
|
|
|
+ },
|
|
|
+ login: async (req: any, res: any) => {
|
|
|
+ req.apiDescription = '用户模块-用户登录';
|
|
|
+
|
|
|
+ const clientIp = ipLocResFu(req);
|
|
|
+ if (!loginFlag[clientIp]) {
|
|
|
+ loginFlag[clientIp] = {
|
|
|
+ loginFlagNum: 0,
|
|
|
+ loginFlagTime: 0,
|
|
|
+ loginFlagRef: null,
|
|
|
+ loginFlagCode: '',
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ if (loginFlag[clientIp].loginFlagNum >= 5) {
|
|
|
+ if (loginFlag[clientIp].loginFlagRef) clearTimeout(loginFlag[clientIp].loginFlagRef);
|
|
|
+
|
|
|
+ loginFlag[clientIp].loginFlagRef = setTimeout(() => {
|
|
|
+ loginFlag[clientIp].loginFlagNum = 0;
|
|
|
+ loginFlag[clientIp].loginFlagTime = 0;
|
|
|
+ loginFlag[clientIp].loginFlagRef = null;
|
|
|
+ }, 60 * 1 * 1000);
|
|
|
+ return resSend(res, 500, '请稍后重试');
|
|
|
+ } else {
|
|
|
+ const codeTxt = req.body.code.toLowerCase();
|
|
|
+
|
|
|
+ if (loginFlag[clientIp].loginFlagCode === codeTxt || (isEnv && codeTxt === '1111')) {
|
|
|
+ const dbUser = await User.findOne({
|
|
|
+ userName: req.body.userName,
|
|
|
+ }).select('+passWord');
|
|
|
+ if (dbUser && dbUser._id) {
|
|
|
+ const pass1 = passWordJie(dbUser.passWord);
|
|
|
+ const pass2 = passWordJie(req.body.passWord);
|
|
|
+
|
|
|
+ if (pass1 === pass2) {
|
|
|
+ loginFlag[clientIp].loginFlagCode = '';
|
|
|
+
|
|
|
+ loginFlag[clientIp].loginFlagNum = 0;
|
|
|
+ loginFlag[clientIp].loginFlagTime = 0;
|
|
|
+ const dbUserJson: any = dbUser.toObject();
|
|
|
+ delete dbUserJson.passWord;
|
|
|
+
|
|
|
+ // 获取token
|
|
|
+ const token = await getTokenFu(dbUserJson);
|
|
|
+
|
|
|
+ // 登录模块记录日志,由于没有token,需要特殊处理
|
|
|
+ req.userName = dbUserJson.userName;
|
|
|
+
|
|
|
+ return resSend(res, 0, '登录成功', { user: dbUserJson, token });
|
|
|
+ } else {
|
|
|
+ loginTimeFu(clientIp);
|
|
|
+ return resSend(res, 400, '密码错误');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ loginTimeFu(clientIp);
|
|
|
+ return resSend(res, 400, '用户名错误');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ loginTimeFu(clientIp);
|
|
|
+ return resSend(res, 400, '验证码错误');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ loginText: async (req: any, res: any) => {
|
|
|
+ req.apiDescription = '用户模块-用户登录调试';
|
|
|
+
|
|
|
+ const dbUser = await User.findOne({
|
|
|
+ userName: req.body.userName,
|
|
|
+ });
|
|
|
+
|
|
|
+ if (dbUser && dbUser._id) {
|
|
|
+ const dbUserJson: any = dbUser.toJSON();
|
|
|
+
|
|
|
+ // 获取token
|
|
|
+ const token = await getTokenFu(dbUserJson);
|
|
|
+
|
|
|
+ return resSend(res, 0, '登录成功', { user: dbUserJson, token });
|
|
|
+ } else {
|
|
|
+ return resSend(res, 400, '用户名错误');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ addOrEdit: async (req: any, res: any) => {
|
|
|
+ // console.log('xxxxxqqqxxx', JSON.stringify(req.body));
|
|
|
+ // 拿到参数模型
|
|
|
+
|
|
|
+ if (req.body._id) {
|
|
|
+ // 编辑用户
|
|
|
+ // 检查用户是否存在
|
|
|
+ const existingUser: any = await User.findById(req.body._id);
|
|
|
+ if (!existingUser) return resSend(res, 404, '用户不存在');
|
|
|
+ // 更新字段
|
|
|
+
|
|
|
+ // 过滤一些字段
|
|
|
+ const filetStr = ['userName', 'passWord'];
|
|
|
+
|
|
|
+ Object.keys(req.body).forEach((key) => {
|
|
|
+ if (key !== '_id' && req.body[key] !== undefined) {
|
|
|
+ if (!filetStr.includes(key)) {
|
|
|
+ existingUser[key] = req.body[key];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ existingUser.updateTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
|
|
+ const updatedUser = await existingUser.save();
|
|
|
+
|
|
|
+ const userObj = updatedUser.toObject();
|
|
|
+ delete userObj.passWord;
|
|
|
+ req.apiDescription = `用户模块-编辑用户-${userObj.userName}`;
|
|
|
+ return resSend(res, 0, '编辑用户成功', userObj);
|
|
|
+ } else {
|
|
|
+ const userModel = new User({ ...req.body, passWord: passWordJia('Aa147852') });
|
|
|
+ // 新增用户
|
|
|
+ // 判断用户名是否已经存在
|
|
|
+ const dbUser = await User.findOne({ userName: req.body.userName });
|
|
|
+ if (dbUser && dbUser._id) {
|
|
|
+ return resSend(res, 400, '用户名已存在');
|
|
|
+ } else {
|
|
|
+ // 保存数据到数据库
|
|
|
+ const dbBack = await userModel.save();
|
|
|
+ // 将文档转换为普通对象并删除密码字段
|
|
|
+ const userObj = dbBack.toObject();
|
|
|
+ req.apiDescription = `用户模块-新增用户-${userObj.userName}`;
|
|
|
+ delete userObj.passWord;
|
|
|
+ return resSend(res, 0, '新增用户成功', userObj);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ list: async (req: any, res: any) => {
|
|
|
+ req.apiDescription = '用户模块-获取用户列表';
|
|
|
+ // 拿到参数模型
|
|
|
+ // const userModel = new User(req.body);
|
|
|
+ const { pageNum = 1, pageSize = 10, searchKey = '' } = req.body;
|
|
|
+
|
|
|
+ // 构建查询条件
|
|
|
+ const query: any = {};
|
|
|
+ if (searchKey) {
|
|
|
+ // 使用正则表达式实现模糊查询,'i'表示不区分大小写
|
|
|
+ query.userName = { $regex: searchKey, $options: 'i' };
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算跳过的文档数量
|
|
|
+ const skip = (pageNum - 1) * pageSize;
|
|
|
+
|
|
|
+ // 并行执行:获取总条数和查询当前页数据
|
|
|
+ const [total, data] = await Promise.all([
|
|
|
+ // 获取满足条件的总记录数
|
|
|
+ User.countDocuments(query),
|
|
|
+ // 查询当前页数据
|
|
|
+ User.find(query).skip(skip).limit(parseInt(pageSize)).sort({ createdAt: -1 }), // 按创建时间倒序
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 计算总页数
|
|
|
+ const totalPages = Math.ceil(total / pageSize);
|
|
|
+
|
|
|
+ return resSend(res, 0, '获取用户列表成功', {
|
|
|
+ list: data,
|
|
|
+ pageNum: parseInt(pageNum),
|
|
|
+ pageSize: parseInt(pageSize),
|
|
|
+ total,
|
|
|
+ totalPages,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ log: async (req: any, res: any) => {
|
|
|
+ req.apiDescription = '用户模块-获取操作日志';
|
|
|
+ // 拿到参数模型
|
|
|
+ // const userModel = new User(req.body);
|
|
|
+ const { pageNum = 1, pageSize = 10, searchKey = '', startTime = '', endTime = '' } = req.body;
|
|
|
+ // 构建查询条件
|
|
|
+ const query: any = {};
|
|
|
+
|
|
|
+ // 1. 处理模糊搜索条件
|
|
|
+ if (searchKey) {
|
|
|
+ query.userName = { $regex: searchKey, $options: 'i' }; // 'i'表示不区分大小写
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 处理时间范围条件
|
|
|
+ if (startTime && endTime) {
|
|
|
+ query.createTime = {
|
|
|
+ $gte: startTime,
|
|
|
+ $lte: endTime,
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算跳过的文档数量
|
|
|
+ const skip = (pageNum - 1) * pageSize;
|
|
|
+
|
|
|
+ // 并行执行:获取总条数和查询当前页数据
|
|
|
+ const [total, data] = await Promise.all([
|
|
|
+ // 获取满足条件的总记录数
|
|
|
+ Log.countDocuments(query),
|
|
|
+ // 查询当前页数据,按时间倒序排列
|
|
|
+ Log.find(query).sort({ createTime: -1 }).skip(skip).limit(parseInt(pageSize)),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 计算总页数
|
|
|
+ const totalPages = Math.ceil(total / pageSize);
|
|
|
+
|
|
|
+ return resSend(res, 0, '获取日志列表成功', {
|
|
|
+ list: data,
|
|
|
+ pageNum: parseInt(pageNum),
|
|
|
+ pageSize: parseInt(pageSize),
|
|
|
+ total,
|
|
|
+ totalPages,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ resetPassWord: async (req: any, res: any) => {
|
|
|
+ const _id = req.params._id;
|
|
|
+ if (_id) {
|
|
|
+ const resPassWord = passWordJia('Aa147852');
|
|
|
+ const filter = { _id };
|
|
|
+ const update = {
|
|
|
+ $set: {
|
|
|
+ passWord: resPassWord,
|
|
|
+ updateTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ const result = await User.findOneAndUpdate(filter, update);
|
|
|
+
|
|
|
+ if (result) {
|
|
|
+ req.apiDescription = `用户模块-重置密码-${result.userName}`;
|
|
|
+ return resSend(res, 0, '重置密码成功');
|
|
|
+ } else return resSend(res, 404, '未找到对应用户或密码未变更');
|
|
|
+ } else return resSend(res, 400, '用户_id不能为空');
|
|
|
+ },
|
|
|
+ editPassWord: async (req: any, res: any) => {
|
|
|
+ req.apiDescription = `用户模块-修改自己的密码`;
|
|
|
+ const dbUser = await User.findOne({
|
|
|
+ userName: req.user.userName,
|
|
|
+ }).select('+passWord');
|
|
|
+
|
|
|
+ const { oldPassword, newPassword } = req.body;
|
|
|
+ const oldPassRes = passWordJie(oldPassword);
|
|
|
+
|
|
|
+ const pass1 = passWordJie(dbUser!.passWord);
|
|
|
+
|
|
|
+ if (oldPassRes === pass1) {
|
|
|
+ const filter = { userName: req.user.userName };
|
|
|
+ const update = {
|
|
|
+ $set: {
|
|
|
+ passWord: newPassword,
|
|
|
+ updateTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ const result = await User.updateOne(filter, update);
|
|
|
+
|
|
|
+ if (result.modifiedCount === 1) return resSend(res, 0, '修改密码成功');
|
|
|
+ else return resSend(res, 404, '未找到对应用户或密码未变更');
|
|
|
+ } else return resSend(res, 400, '旧密码错误');
|
|
|
+ },
|
|
|
+ del: async (req: any, res: any) => {
|
|
|
+ const { _id } = req.params; // 从URL参数中获取用户ID
|
|
|
+ // 1. 根据ID查找用户
|
|
|
+ const user = await User.findById(_id);
|
|
|
+ if (!user) return resSend(res, 404, '用户不存在');
|
|
|
+ if (user.isAdmin === 1) return resSend(res, 500, '管理员无法删除');
|
|
|
+
|
|
|
+ const deletedUser: any = await User.findByIdAndDelete(_id);
|
|
|
+ req.apiDescription = `用户模块-删除用户-${deletedUser.userName}`;
|
|
|
+ return resSend(res, 0, '删除用户成功');
|
|
|
+ },
|
|
|
+ setAuthority: async (req: any, res: any) => {
|
|
|
+ const { _id, authorityIds } = req.body;
|
|
|
+
|
|
|
+ if (!_id) return resSend(res, 400, '_id不能为空');
|
|
|
+
|
|
|
+ let authorityIdsRes: any[] = authorityIds || [];
|
|
|
+
|
|
|
+ authorityIdsRes = authorityIdsRes.map((v) => Number(v));
|
|
|
+
|
|
|
+ if (!authorityIdsRes.length) return resSend(res, 400, '权限数组不能为空');
|
|
|
+ const updatedUser = await User.findByIdAndUpdate(
|
|
|
+ _id, // 用户ID
|
|
|
+ {
|
|
|
+ authorityIds: authorityIdsRes,
|
|
|
+ updateTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
|
|
+ }, // 要更新的字段
|
|
|
+ {
|
|
|
+ new: true, // 返回更新后的文档
|
|
|
+ runValidators: true, // 确保更新操作也运行Schema级别的验证
|
|
|
+ }
|
|
|
+ );
|
|
|
+ if (!updatedUser) return resSend(res, 404, '未找到该用户');
|
|
|
+ req.apiDescription = `用户模块-修改用户页面权限-${updatedUser.userName}`;
|
|
|
+ return resSend(res, 0, '用户权限更新成功');
|
|
|
+ },
|
|
|
+ getInfo: async (req: any, res: any) => {
|
|
|
+ const { _id } = req.params;
|
|
|
+
|
|
|
+ if (!_id) return resSend(res, 400, '_id不能为空');
|
|
|
+
|
|
|
+ // 根据ID查询用户信息
|
|
|
+ const user = await User.findById(_id);
|
|
|
+
|
|
|
+ if (!user) {
|
|
|
+ return resSend(res, 404, '未找到该用户');
|
|
|
+ }
|
|
|
+ req.apiDescription = `用户模块-获取用户详情-${user.userName}`;
|
|
|
+ return resSend(res, 0, '获取用户详情成功', user);
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+export default user;
|