You need to enable JavaScript to run this app. Node.js下/addEmployee路由使用.then前置校验员工存在性的实现咨询
优惠活动
大模型
产品
解决方案
定价
更多

Node.js下/addEmployee路由使用.then前置校验员工存在性的实现咨询

现有实现的问题判定

你的当前实现是完全不正确的,根本达不到你想要的“等校验通过再执行后续操作”的效果,核心问题包括:

  • 流程完全不同步:你写的getEmployeeNum是基于回调的异步函数,既没有返回Promise,也没有任何流程阻断逻辑,主路由调用它之后,根本不会等待校验完成,会立刻往下执行密码加密、数据库插入的代码,哪怕员工已经存在,插入流程也会照常跑完。
  • 服务端API误用:getEmployeeNum里用了浏览器端才有的alert方法,Node.js环境运行这段代码会直接抛出alert is not defined的错误,根本没法给用户返回提示。
  • 校验逻辑效率极低:每次校验都要把员工全表数据拉到服务端内存里循环比对,数据量稍大就会出现明显的性能瓶颈,而且代码里就算查到重复,渲染完添加员工页面之后,主流程里的数据库插入、页面跳转逻辑还是会继续执行,根本拦不住。
  • 流程顺序混乱:目前代码里bcrypt的密码哈希计算还没完成,下面打开数据库连接、插入员工信息的同步代码就已经开始运行,后续用到的pass变量根本拿不到正确的哈希值,业务逻辑完全跑不通。
  • 安全隐患:所有SQL都是直接拼接用户输入的字符串,存在严重的SQL注入风险。
更优的实现方案

核心思路是先把所有带回调的异步操作(数据库查询、密码加密)封装成标准Promise,再通过Promise链式调用严格按照业务顺序执行流程,任何一步校验不通过、操作出错,直接中断后续逻辑。

具体改造步骤

  1. 封装统一的数据库查询方法,把重复的打开连接、执行查询、关闭连接的逻辑抽离,避免重复代码,同时从根源上避免连接泄漏。
  2. 把员工存在校验的SQL改成条件查询,不再全表拉数据,查到重复直接抛错中断流程。
  3. 按照业务顺序串起所有Promise节点:会话校验→员工号重复校验→密码哈希→员工信息入库→登录信息写入→发送注册邮件→重定向到员工列表。
  4. 用统一的catch分支捕获所有错误,把提示信息返回给前端页面渲染,替代无效的alert。

优化后的参考代码

// 通用数据库查询Promise封装
const queryDb = (sql, params = []) => {
  return new Promise((resolve, reject) => {
    ibmdb.open(ibmdbconnMaster, (err, conn) => {
      if (err) return reject(err);
      conn.query(sql, params, (err, rows) => {
        // 查询完成主动关闭连接
        conn.close(() => {});
        if (err) return reject(err);
        resolve(rows);
      });
    });
  });
};

// 员工存在校验函数,返回Promise
const checkEmployeeExist = (employeenum) => {
  // 直接按员工号条件查询,不全表扫描
  const checkSql = "SELECT EMPLOYEE_NUM FROM EMPLOYEES WHERE EMPLOYEE_NUM = ?";
  return queryDb(checkSql, [employeenum]).then(rows => {
    if (rows.length > 0) {
      throw new Error("员工编号已存在,请核对后重试");
    }
  });
};

// bcrypt密码哈希的Promise封装
const getPasswordHash = (plainPwd) => {
  return new Promise((resolve, reject) => {
    bcrypt.hash(plainPwd, saltRounds, (err, hash) => {
      if (err) return reject(err);
      resolve(hash);
    });
  });
};

// 改造后的添加员工路由
app.post('/addEmployee', (req, res) => {
  if (!req.session.loggedin) {
    return res.render('login.ejs');
  }

  // 统一取请求参数,避免后续代码混写
  const {
    firstname, lastname, username, sales, salary,
    location, role, admin, employee_num: employeenum,
    phone_num: phonenum, org, password: newPassword
  } = req.body;

  // 按业务顺序串起Promise流程
  checkEmployeeExist(employeenum)
    .then(() => getPasswordHash(newPassword))
    .then(pass => {
      // 使用参数化查询,彻底规避SQL注入
      const addEmployeeSql = `
        INSERT INTO EMPLOYEES 
        (FIRSTNAME, LASTNAME, USERNAME, SALES, SALARY, LOCATION, ROLE, ADMIN, EMPLOYEE_NUM, PHONENUM, ORGANIZATION)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
      `;
      return queryDb(addEmployeeSql, [
        firstname, lastname, username, sales, salary,
        location, role, admin, employeenum, phonenum, org
      ]).then(() => pass); // 把密码哈希值透传给下一个流程
    })
    .then(pass => {
      // 员工基础信息入库完成后,再写入登录账号信息
      return addLogin(req, res, pass, firstname, lastname, username, sales, salary, location, role, admin, employeenum, phonenum);
    })
    .then(() => {
      return registerEmail(username, firstname, lastname, req, res);
    })
    .then(() => {
      res.redirect('/employees');
    })
    .catch(err => {
      // 统一处理所有流程错误
      console.error('添加员工失败:', err);
      // 把错误信息传入页面渲染,给用户明确提示
      res.render('addEmployee.ejs', { errorMsg: err.message });
    });
});

额外注意事项

  • 所有涉及用户输入的SQL都要使用占位符传参,不要直接拼接字符串,避免SQL注入风险。
  • 如果后续业务逻辑更复杂,可以把Promise链换成async/await写法,可读性会比纯.then链式调用更高。
  • 数据库查询完成后一定要主动关闭连接,不然高并发场景下会快速耗尽数据库连接池。

内容的提问来源于stack exchange,提问作者Gianluca

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.29 22:36:00