前言
最近朋友在写个人博客,我友情客串帮忙做一个简易的后端,于是有了这篇笔记,我选择的技术栈是node(express) + mysql
,部署使用docker
,简单记录一下,先说明本人没有专业的node
后端开发经验,纯属自己硬凑,有不专业的地方敬请谅解
项目结构思考
由于没有专业的node
后端开发经验,我个人感觉应该将数据库操作和业务操作单独分开,另外统一接口返回格式,具体操作再细分,所以有了以下项目结构
敲代码
文件太多,只挑几个代表示例,大部分操作都是不困难的,所有注释均是Ai
生成
接口返回模版
/*** 类 IRM 用于管理响应结果,提供成功和错误响应的标准化格式。*/
class IRM {/*** 构造函数,初始化响应对象。*/constructor() {// 初始化响应对象,默认状态码为 -1,消息为空,数据为空对象this.response = {code: -1,msg: '',data: {}};}/*** 重置响应对象为初始状态。*/$initRes() {this.response = {code: -1,msg: '',data: {}};}/*** 设置成功的响应。* * @param {Object} data - 成功时返回的数据。* @returns {Object} 包含成功状态码、消息和数据的响应对象。*/success(data) {this.$initRes();this.response.code = 200;this.response.msg = "success";this.response.data = data;return this.response;}/*** 设置错误的响应。* * @param {number} code - 错误状态码。* @param {string} msg - 错误消息。* @returns {Object} 包含错误状态码、消息和空数据的响应对象。*/error(code, msg) {this.$initRes();this.response.code = code;this.response.msg = msg;this.response.data = {};return this.response;}
}// 导出两个 IRM 实例,IRT 和 DBIRT,区分数据库模版和业务模版
export const IRT = new IRM();
export const DBIRT = new IRM();
获取数据库连接
/*** 获取数据库实例* 如果已经连接,则直接返回实例* 否则等待连接*/
export const getDBInstannce = async () => {if (connectionInstance) {return connectionInstance;}return new Promise(async (resolve) => {console.error('数据库未连接,无法获取db实例');await connectUserDatabase()let checkInterval = setInterval(() => {console.log('等待数据库连接...');if (connectionInstance) {clearInterval(checkInterval);console.log('db连接健康');resolve(connectionInstance);}}, 1000);})
}
处理登录请求(业务层操作)
/*** 处理用户登录的路由* @param {Object} req - 请求对象,包含email和password* @param {Object} res - 响应对象*/
router.post('/login', async (req, res) => {try {// 从请求体中解构出用户登录信息const { email, password } = req.body;// 调用用户登录函数let { data, code, msg } = await userLogin(email, password);if (code === 200) {if (data.data.length === 0) {res.json(IRT.error(40101, "用户不存在"))return}// 使用MD5加密密码进行比较let pwd = generateMD5(password);if (data.data[0].password !== pwd) {res.json(IRT.error(40102, "密码错误"))return}// 准备用户信息和登录tokenlet user_id = data.data[0].idlet remember_token = data.data[0].remember_token// 查询用户登录状态let login_Status = await queryUserLoginStatus(remember_token)// 根据登录状态返回相应的结果if (login_Status?.data?.results[0]?.login_token) {res.json(IRT.success({login_token: login_Status?.data?.results[0].login_token,userInfo: {...data.data[0],password: null,remember_token: null}}))return}// 生成新的登录tokenlet login_token = generateMD5(email + Date.now());// 记录用户登录tokenlet login_data = await userLoginToken(user_id, remember_token, login_token);if (login_data.code == 200) {res.json(IRT.success({login_token: login_token,userInfo: {...data.data[0],password: null,remember_token: null}}))return}if (login_data.code == 40103) {res.json(IRT.error(40103, login_data.msg))return}return}res.json(IRT.error(500, msg))} catch (error) {console.log(error);res.json(IRT.error(500))}
})
处理用户登录(数据层)
// 用户登录函数
/*** 用户登录* @param {string} email - 用户邮箱* @param {string} password - 用户密码* @returns {Object} - 登录结果*/
export async function userLogin(email, password) {// 1. 验证输入数据是否完整if (!email || !password) {return DBIRT.error(400, "请输入完整的登录信息");}const query = 'SELECT * FROM users WHERE email = ?';try {let [results] = await db.execute(query, [email]);return DBIRT.success({data: results});} catch (error) {return DBIRT.error(400, error);}
}
…
敲完代码之后就是部署了,因为docker
部署最简单方便,所以就这种方式了,总体就是下面几步
- 编写
Dockerfile
# 使用官方 Node.js 镜像作为基础镜像
FROM node:20# 创建并设置工作目录
WORKDIR /usr/src/app# 复制 package.json 和 package-lock.json(如果有的话)
COPY package*.json ./# 安装依赖
RUN npm install# 复制其他项目文件
COPY . .# 暴露应用运行的端口
EXPOSE 3000# 启动应用
CMD ["node", "index.js"]
- 构建
docker
镜像
docker build -t node-server .
- 登录到阿里云镜像服务
- 使用
docker login xxxx
登录到阿里云 - 使用
docker tag
为本地镜像设置别名 - 使用
docker push
推送到阿里云镜像服务器上 - 登录到自己的服务器上,使用
docker login xxxx
登录阿里云 - 使用
docker pull
拉取镜像到本地 - 使用
docker run -d -p 80:3000 --name node-server xxxxx
运行容器,并将3000端口映射到本机的80端口上
端口根据具体的需求改
测试
最后请求服务器的接口,测试一下是否正常运行,很幸运,正常返回数据
{"code": 200,"msg": "success","data": {"login_token": "a0151db81e9f622719e1b66ca57e7d42","userInfo": {"id": 12,"username": "test","email": "test@test.com","password": null,"nickname": "test","profile_picture": null,"bio": null,"role": "user","status": "active","phone_number": "18800008998","address": null,"created_at": "2025-02-19T09:26:54.000Z","updated_at": "2025-02-19T09:26:54.000Z","last_login": null,"remember_token": null}}
}
总结
此文主要是简记,总结一下经验,长路漫漫,诸君共勉