userController.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. import dayjs from 'dayjs';
  2. import { isEnv } from '../config/config.default.js';
  3. import { Log, User } from '../model/index.js';
  4. import { getTokenFu } from '../middleware/jwt.js';
  5. import { passWordJia, passWordJie } from '../util/pass.js';
  6. import resSend from '../util/resSend.js';
  7. import { generateCaptcha, ipLocResFu } from '../util/index.js';
  8. // 登录模块 需要做定时器处理,防止短时间多次发送
  9. let loginFlag: any = {};
  10. export const clearLoginCode = () => {
  11. loginFlag = {};
  12. };
  13. const loginTimeFu = (key: string) => {
  14. if (loginFlag[key].loginFlagTime === 0) loginFlag[key].loginFlagTime = Date.now();
  15. const nowTime = Date.now();
  16. if (nowTime - loginFlag[key].loginFlagTime <= 60000) loginFlag[key].loginFlagNum++;
  17. };
  18. const user = {
  19. getCode: async (req: any, res: any) => {
  20. req.apiDescription = '用户模块-获取验证码';
  21. const clientIp = ipLocResFu(req);
  22. const captcha = generateCaptcha();
  23. // 将验证码文本存入session(小写以便不区分大小写校验)
  24. const captchaTxt = captcha.text.toLowerCase();
  25. if (loginFlag[clientIp]) loginFlag[clientIp].loginFlagCode = captchaTxt;
  26. else loginFlag[clientIp] = { loginFlagCode: captchaTxt };
  27. // console.log('生成的验证码(开发调试用):', captchaTxt); // 调试时可查看
  28. // 设置响应头,告诉浏览器这是SVG图片
  29. res.type('svg');
  30. res.send(captcha.data);
  31. },
  32. login: async (req: any, res: any) => {
  33. req.apiDescription = '用户模块-用户登录';
  34. const clientIp = ipLocResFu(req);
  35. if (!loginFlag[clientIp]) {
  36. loginFlag[clientIp] = {
  37. loginFlagNum: 0,
  38. loginFlagTime: 0,
  39. loginFlagRef: null,
  40. loginFlagCode: '',
  41. };
  42. }
  43. if (loginFlag[clientIp].loginFlagNum >= 5) {
  44. if (loginFlag[clientIp].loginFlagRef) clearTimeout(loginFlag[clientIp].loginFlagRef);
  45. loginFlag[clientIp].loginFlagRef = setTimeout(() => {
  46. loginFlag[clientIp].loginFlagNum = 0;
  47. loginFlag[clientIp].loginFlagTime = 0;
  48. loginFlag[clientIp].loginFlagRef = null;
  49. }, 60 * 1 * 1000);
  50. return resSend(res, 500, '请稍后重试');
  51. } else {
  52. const codeTxt = req.body.code.toLowerCase();
  53. if (loginFlag[clientIp].loginFlagCode === codeTxt || (isEnv && codeTxt === '1111')) {
  54. const dbUser = await User.findOne({
  55. userName: req.body.userName,
  56. }).select('+passWord');
  57. if (dbUser && dbUser._id) {
  58. const pass1 = passWordJie(dbUser.passWord);
  59. const pass2 = passWordJie(req.body.passWord);
  60. if (pass1 === pass2) {
  61. loginFlag[clientIp].loginFlagCode = '';
  62. loginFlag[clientIp].loginFlagNum = 0;
  63. loginFlag[clientIp].loginFlagTime = 0;
  64. delete loginFlag[clientIp];
  65. const dbUserJson: any = dbUser.toObject();
  66. delete dbUserJson.passWord;
  67. // 获取token
  68. const token = await getTokenFu(dbUserJson);
  69. // 登录模块记录日志,由于没有token,需要特殊处理
  70. req.userName = dbUserJson.userName;
  71. return resSend(res, 0, '登录成功', { user: dbUserJson, token });
  72. } else {
  73. loginTimeFu(clientIp);
  74. return resSend(res, 400, '密码错误');
  75. }
  76. } else {
  77. loginTimeFu(clientIp);
  78. return resSend(res, 400, '用户名错误');
  79. }
  80. } else {
  81. loginTimeFu(clientIp);
  82. return resSend(res, 400, '验证码错误');
  83. }
  84. }
  85. },
  86. loginText: async (req: any, res: any) => {
  87. req.apiDescription = '用户模块-用户登录调试';
  88. const dbUser = await User.findOne({
  89. userName: req.body.userName,
  90. });
  91. if (dbUser && dbUser._id) {
  92. const dbUserJson: any = dbUser.toJSON();
  93. // 获取token
  94. const token = await getTokenFu(dbUserJson);
  95. return resSend(res, 0, '获取token成功', { token });
  96. } else {
  97. return resSend(res, 400, '用户名错误');
  98. }
  99. },
  100. addOrEdit: async (req: any, res: any) => {
  101. // console.log('xxxxxqqqxxx', JSON.stringify(req.body));
  102. // 拿到参数模型
  103. if (req.body._id) {
  104. // 编辑用户
  105. // 检查用户是否存在
  106. const existingUser: any = await User.findById(req.body._id);
  107. if (!existingUser) return resSend(res, 404, '用户不存在');
  108. // 更新字段
  109. // 过滤一些字段
  110. const filetStr = ['userName', 'passWord'];
  111. Object.keys(req.body).forEach((key) => {
  112. if (key !== '_id' && req.body[key] !== undefined) {
  113. if (!filetStr.includes(key)) {
  114. existingUser[key] = req.body[key];
  115. }
  116. }
  117. });
  118. existingUser.updateTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
  119. const updatedUser = await existingUser.save();
  120. const userObj = updatedUser.toObject();
  121. delete userObj.passWord;
  122. req.apiDescription = `用户模块-编辑用户-${userObj.userName}`;
  123. return resSend(res, 0, '编辑用户成功', userObj);
  124. } else {
  125. const infoModel = new User({ ...req.body, passWord: passWordJia('Aa147852') });
  126. // 新增用户
  127. // 判断用户名是否已经存在
  128. const dbUser = await User.findOne({ userName: req.body.userName });
  129. if (dbUser && dbUser._id) {
  130. return resSend(res, 400, '用户名已存在');
  131. } else {
  132. infoModel.createTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
  133. infoModel.updateTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
  134. // 保存数据到数据库
  135. const dbBack = await infoModel.save();
  136. // 将文档转换为普通对象并删除密码字段
  137. const userObj = dbBack.toObject();
  138. req.apiDescription = `用户模块-新增用户-${userObj.userName}`;
  139. delete userObj.passWord;
  140. return resSend(res, 0, '新增用户成功', userObj);
  141. }
  142. }
  143. },
  144. list: async (req: any, res: any) => {
  145. req.apiDescription = '用户模块-获取用户列表';
  146. // 拿到参数模型
  147. // const userModel = new User(req.body);
  148. const { pageNum = 1, pageSize = 10, searchKey = '' } = req.body;
  149. // 构建查询条件
  150. const query: any = {};
  151. if (searchKey) {
  152. // 使用正则表达式实现模糊查询,'i'表示不区分大小写
  153. query.userName = { $regex: searchKey, $options: 'i' };
  154. }
  155. // 计算跳过的文档数量
  156. const skip = (pageNum - 1) * pageSize;
  157. // 并行执行:获取总条数和查询当前页数据
  158. const [total, data] = await Promise.all([
  159. // 获取满足条件的总记录数
  160. User.countDocuments(query),
  161. // 查询当前页数据
  162. User.find(query).skip(skip).limit(parseInt(pageSize)).sort({ createdAt: -1 }), // 按创建时间倒序
  163. ]);
  164. // 计算总页数
  165. const totalPages = Math.ceil(total / pageSize);
  166. return resSend(res, 0, '获取用户列表成功', {
  167. list: data,
  168. pageNum: parseInt(pageNum),
  169. pageSize: parseInt(pageSize),
  170. total,
  171. totalPages,
  172. });
  173. },
  174. log: async (req: any, res: any) => {
  175. req.apiDescription = '用户模块-获取操作日志';
  176. // 拿到参数模型
  177. // const userModel = new User(req.body);
  178. const { pageNum = 1, pageSize = 10, searchKey = '', startTime = '', endTime = '' } = req.body;
  179. // 构建查询条件
  180. const query: any = {};
  181. // 1. 处理模糊搜索条件
  182. if (searchKey) {
  183. query.userName = { $regex: searchKey, $options: 'i' }; // 'i'表示不区分大小写
  184. }
  185. // 2. 处理时间范围条件
  186. if (startTime && endTime) {
  187. query.createTime = {
  188. $gte: startTime,
  189. $lte: endTime,
  190. };
  191. }
  192. // 计算跳过的文档数量
  193. const skip = (pageNum - 1) * pageSize;
  194. // 并行执行:获取总条数和查询当前页数据
  195. const [total, data] = await Promise.all([
  196. // 获取满足条件的总记录数
  197. Log.countDocuments(query),
  198. // 查询当前页数据,按时间倒序排列
  199. Log.find(query).sort({ createTime: -1 }).skip(skip).limit(parseInt(pageSize)),
  200. ]);
  201. // 计算总页数
  202. const totalPages = Math.ceil(total / pageSize);
  203. return resSend(res, 0, '获取日志列表成功', {
  204. list: data,
  205. pageNum: parseInt(pageNum),
  206. pageSize: parseInt(pageSize),
  207. total,
  208. totalPages,
  209. });
  210. },
  211. resetPassWord: async (req: any, res: any) => {
  212. const _id = req.params._id;
  213. if (_id) {
  214. const resPassWord = passWordJia('Aa147852');
  215. const filter = { _id };
  216. const update = {
  217. $set: {
  218. passWord: resPassWord,
  219. updateTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
  220. },
  221. };
  222. const result = await User.findOneAndUpdate(filter, update);
  223. if (result) {
  224. req.apiDescription = `用户模块-重置密码-${result.userName}`;
  225. return resSend(res, 0, '重置密码成功');
  226. } else return resSend(res, 404, '未找到对应用户或密码未变更');
  227. } else return resSend(res, 400, '用户_id不能为空');
  228. },
  229. editPassWord: async (req: any, res: any) => {
  230. req.apiDescription = `用户模块-修改自己的密码`;
  231. const dbUser = await User.findOne({
  232. userName: req.user.userName,
  233. }).select('+passWord');
  234. const { oldPassword, newPassword } = req.body;
  235. const oldPassRes = passWordJie(oldPassword);
  236. const pass1 = passWordJie(dbUser!.passWord);
  237. if (oldPassRes === pass1) {
  238. const filter = { userName: req.user.userName };
  239. const update = {
  240. $set: {
  241. passWord: newPassword,
  242. updateTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
  243. },
  244. };
  245. const result = await User.updateOne(filter, update);
  246. if (result.modifiedCount === 1) return resSend(res, 0, '修改密码成功');
  247. else return resSend(res, 404, '未找到对应用户或密码未变更');
  248. } else return resSend(res, 400, '旧密码错误');
  249. },
  250. del: async (req: any, res: any) => {
  251. const { _id } = req.params; // 从URL参数中获取用户ID
  252. // 1. 根据ID查找用户
  253. const user = await User.findById(_id);
  254. if (!user) return resSend(res, 404, '用户不存在');
  255. if (user.isAdmin === 1) return resSend(res, 500, '管理员无法删除');
  256. const deletedUser: any = await User.findByIdAndDelete(_id);
  257. req.apiDescription = `用户模块-删除用户-${deletedUser.userName}`;
  258. return resSend(res, 0, '删除用户成功');
  259. },
  260. setAuthority: async (req: any, res: any) => {
  261. const { _id, authorityIds } = req.body;
  262. if (!_id) return resSend(res, 400, '_id不能为空');
  263. let authorityIdsRes: any[] = authorityIds || [];
  264. authorityIdsRes = authorityIdsRes.map((v) => Number(v));
  265. if (!authorityIdsRes.length) return resSend(res, 400, '权限数组不能为空');
  266. const updatedUser = await User.findByIdAndUpdate(
  267. _id, // 用户ID
  268. {
  269. authorityIds: authorityIdsRes,
  270. updateTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
  271. }, // 要更新的字段
  272. {
  273. new: true, // 返回更新后的文档
  274. runValidators: true, // 确保更新操作也运行Schema级别的验证
  275. }
  276. );
  277. if (!updatedUser) return resSend(res, 404, '未找到该用户');
  278. req.apiDescription = `用户模块-修改用户页面权限-${updatedUser.userName}`;
  279. return resSend(res, 0, '用户权限更新成功');
  280. },
  281. getInfo: async (req: any, res: any) => {
  282. const { _id } = req.params;
  283. if (!_id) return resSend(res, 400, '_id不能为空');
  284. // 根据ID查询用户信息
  285. const user = await User.findById(_id);
  286. if (!user) {
  287. return resSend(res, 404, '未找到该用户');
  288. }
  289. req.apiDescription = `用户模块-获取用户详情-${user.userName}`;
  290. return resSend(res, 0, '获取用户详情成功', user);
  291. },
  292. };
  293. export default user;