ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于Node.js+Express的文学交流平台实现复盘

基于Node.js+Express的文学交流平台实现复盘 从毕设题目到可跑通的文学交流平台我基于 Node.js Express 的完整实现复盘如果你正对着“nodejs基于express的文学交流平台的设计与实现”这种题目发愁或者已经写了好几版代码但总觉得心里没底那这篇文章就是给你准备的。我最近刚好完整做了一个基于 Node.js 和 Express 的文学交流平台从前端页面到后端接口、从数据库设计到部署上线全流程走了一遍踩了不少坑也沉淀了一些真正能落地的经验。这篇文章不搞虚的直接把我从需求拆解到最终实现的过程拆开讲把遇到的典型问题、排查思路和解决方案都写清楚你们照着这个思路走大概率能少走很多弯路。先说清楚这个项目到底是什么。它是一个典型的 Web 全栈项目用户可以在平台注册登录、发布文学类文章小说、诗歌、散文、书评都行、对别人的作品进行评论、给喜欢的作品点赞还能按分类浏览和搜索内容。这类平台本质上就是“内容社区型的 CRUD 应用”但它比普通的博客系统多了一层社交属性所以权限控制和数据表设计会比单纯写文章的系统复杂一点。这个项目非常适合做毕业设计、课程设计或者作品集项目因为它的业务场景清晰技术栈主流往上可拓展的空间也大——比如后续加聊天室、加入推荐系统都能和现有架构衔接得很自然。整篇复盘我会从六个部分来展开先聊聊拿到题目时我是怎么做需求拆解和技术选型的再讲项目目录结构和数据库设计然后是核心模块的代码实现与设计原因接着是完整的实操过程再把我遇到过的常见问题和排查思路整理成一张速查表最后分享几个我现在回过头看觉得最值得注意的经验。每一部分我都会尽量解释清楚“为什么这么做”而不只是贴一段代码就完事。1. 拿到题目后需求拆解与技术选型1.1 不要把题目当成文案要把题目翻译成功能清单第一次看到“文学交流平台”这几个字我脑子里其实是一团浆糊的。经验告诉我拿到这类题目第一件事不是急着写代码而是先把它翻译成一张功能清单。我当时就是拿一张纸把“文学交流”这四个字往细了拆用户侧注册、登录、退出登录、个人资料查看与编辑、查看自己发布过的内容内容侧发布文章需要选择分类、填标题、写正文、浏览文章列表、查看文章详情、编辑和删除自己的文章互动侧对文章发表评论、查看评论列表、删除自己的评论、点赞/取消点赞检索侧按分类筛选文章、按关键词搜索标题或正文、按最新/最热排序后台侧可以后置管理用户、审核或删除违规内容。这一步做完项目的大致规模就清楚了。我当时的判断是后面四个模块是核心后台管理可以放到最后有富余精力时再做但前四个必须优先保证。这里顺带讲一下我选型时的想法。题目只写了 nodejs 和 express数据库没有指定这意味着我可以自由选。我最终用的是 Express MySQL EJS 模板引擎的组合。为什么不用 React 或者 Vue因为这类课程设计性质的项目页面的主要目标是“能用、完整、流程闭环”用服务端模板引擎EJS可以把后端渲染和前端逻辑绑在一起减少前后端分离带来的跨域和 token 处理复杂度能省掉非常多调试时间。但你如果对前端框架更熟也不是不能用改成 Express 提供 JSON API、前端单独跑一个 Vue 项目也完全可以只是工作量会上去一截。我的建议是如果你的重点是想展示后端能力就选 EJS 这种传统方案如果你想展示全栈能力且时间充裕再考虑前后端分离。1.2 技术栈确认与版本选型里的门道技术栈我建议锁定在这样一套组合上都是我实测稳定的版本组合Node.js 14 或 16建议 16兼容性最稳18 和 20 也没问题但个别老教程的写法在新版本里会报弃用警告Express 4.x虽然 Express 5 已经出到 beta 很久了但网上资料和中间件生态还是以 4.x 为主别在高版本上纠结MySQL 8.x5.7 也带得动但 8.x 的窗口函数、JSON 字段类型对后面的评论统计、复杂查询都有帮助建议直接 8.0EJS 3.x模板引擎语法简单跟原生 HTML 几乎一样sequelize 或 mysql2 驱动我实名推荐 mysql2轻量、好用、支持 Promise写起来比原生 mysql 包舒服太多。另外有个容易踩的坑是 npm 安装依赖的速度和稳定性问题。如果你在 install 的时候频繁超时或报错建议把镜像源切换到国内镜像命令行执行npm config set registry https://registry.npmmirror.com这个操作能直接解决 90% 的下载失败问题。2. 项目结构设计与数据库建模2.1 目录结构按“路由-控制器-服务”拆不要全堆在 index.js 里我在很多同学的项目里看到过这种情况所有路由、数据库查询、业务逻辑全堆在一个app.js里一个文件写了三四百行看着能跑但后续每加一个功能都要在文件里反复翻找改出 Bug 的概率直线上升。这种代码风格一旦面试官或答辩老师问起“你的项目是怎么组织模块的”你很难讲出像样的设计思路。我第一次做这个项目时用的是比较经典的三层结构你们可以参考literature-platform/ ├── app.js # 入口文件创建应用、挂载中间件和路由 ├── package.json ├── config/ │ └── db.js # 数据库连接池配置 ├── routes/ │ ├── index.js # 公共页面路由 │ ├── auth.js # 注册、登录、退出 │ ├── article.js # 文章发布、列表、详情、编辑、删除 │ └── comment.js # 评论相关路由 ├── controllers/ │ ├── authController.js │ ├── articleController.js │ └── commentController.js ├── services/ │ ├── userService.js │ ├── articleService.js │ └── commentService.js ├── views/ # EJS 模板 │ ├── layout.ejs │ ├── index.ejs │ ├── login.ejs │ ├── register.ejs │ ├── article/ │ │ ├── list.ejs │ │ ├── detail.ejs │ │ ├── create.ejs │ │ └── edit.ejs ├── public/ │ ├── css/ │ ├── js/ │ └── images/ └── middleware/ └── auth.js # 登录状态校验中间件routes 层只管“段路径对应哪个函数”controller 层只做参数接收、调用服务、返回响应services 层放真实的业务逻辑和数据库操作。这样分层最大的好处是可测试性高比如你后面想把 500 行数据库查询逻辑换成缓存只需要改 service路由和控制器一行不用动。如果你觉得三层冗余至少要做到“路由逻辑”分离千万不要把 SQL 语句写在路由回调里这一点面试官非常看重。2.2 数据库表设计五张核心表与字段设计思路表设计关系到后面所有功能的实现复杂度我在这一块反复改过好几版最终稳定下来的核心表结构是这样的users用户表id、username、password加密存储、nickname、avatar、bio、created_at。username 做唯一索引登录和显示名称分开是考虑到用户体验——用户名一旦注册不好改但昵称可以随时换。articles文章表id、user_id外键关联用户、category文章分类、title、content、view_count、like_count、comment_count、created_at、updated_at。文章正文我用的是 TEXT 类型MySQL 里 TEXT 最多存 64KB 字符对文学类文章完全够用不需要上 LONGTEXT。comments评论表id、article_id、user_id、content、created_at。每篇文章的评论不用单独建表用 article_id 关联查询即可。likes点赞表id、user_id、article_id、created_at并且要用 (article_id,user_id) 建联合唯一索引防止同一个人重复点赞。categories分类表id、name、description。分类数据量很小可以预先手工插入也可以在应用启动时初始化。这里分享一个我实际设计时踩过的坑最开始我在 articles 表里用了一个status字段来标记文章状态正常/删除但后来发现这个字段经常没被用到反而让所有查询都要记得加WHERE status 1漏加就会导致已删除的文章出现在列表里。后来我把删除操作改成了物理删除省掉了这个隐性问题。如果你的平台强调“回收站”概念再保留 status 不迟但没必要一开始就设计过度。3. 核心功能模块实现与设计思路3.1 用户注册登录密码加密与 Session 会话方案用户模块是所有平台的基础这里我会把关键决策和实现技巧拆开讲。注册登录要解决的核心问题有两个密码怎么存登录状态怎么保持。密码存的方案我直接排除了 md5 和 sha1用的 bcryptjs 这个库。为什么不用 md5因为 md5 是快速哈希攻击者可以用彩虹表反查虽然加上 salt随机盐之后安全性会提升但 bcrypt 本身就是为密码设计慢哈希算法内置 salt 机制计算速度刻意设计得慢这让暴力破解的成本高到不划算。使用方式也简单const bcrypt require(bcryptjs); // 注册时生成哈希 const saltRounds 10; const hashedPassword await bcrypt.hash(password, saltRounds); // 登录时比对哈希 const isValid await bcrypt.compare(inputPassword, hashedPassword);登录状态的保持方案有两条路Session-Cookie 和 JWT。我选的是 Session。原因很简单这个项目是服务端渲染的 EJS 页面浏览器每次请求都会带上 Cookie服务端通过 Session 就能识别用户身份逻辑简单清晰。JWT 更适合前后端分离的场景。在 Express 里实现 Session 是一套非常成熟的老三样const session require(express-session); const MySQLStore require(express-mysql-session)(session); app.use(session({ secret: your-secret-key, resave: false, saveUninitialized: false, store: new MySQLStore(dbConfig), cookie: { maxAge: 1000 * 60 * 60 * 24 } // 24小时 }));这里有个细节默认的内存 SessionStore 只适合开发环境因为数据存在内存里服务一重启登录状态全部丢失而且内存占用会不断上涨。条件允许的话建议直接上 MySQLStore把 Session 数据持久化到数据库表里重启服务也不掉线这也是答辩时一个不错的加分点。登录后标识当前用户的方式也很直观登录成功后设置req.session.user { id, username, nickname }在后续任何请求里通过判断req.session.user是否存在来确认是否已登录。模板里也能直接用这个变量来显示用户昵称或“登录/注册”按钮。3.2 文章发布与展示文本存储、分页查询与排序策略文章模块是整个平台的核心。发布文章的表单包含三个字段分类select 选择、标题input、正文textarea。提交时后端要做两件事验证参数合法性写入数据库。参数验证这一块我强烈建议不要信任前端的任何输入所有验证都必须在后端重复一遍。标题空字符串、内容长度小于 5 个字、分类不在预设列表里这些都属于非法请求直接返回错误提示。因为前端校验用浏览器开发者工具绕过太轻松了。我实际用的是 express-validator 这个库它可以用链式调用的方式把验证逻辑写得非常清晰const { body, validationResult } require(express-validator); [ body(title).trim().isLength({ min: 2, max: 100 }).withMessage(标题长度需在2-100字之间), body(content).trim().isLength({ min: 10 }).withMessage(正文内容不能少于10个字) ]文章列表页的实现要重点说分页。如果一上来就SELECT * FROM articles数据量只要超过几百条页面就会越滚越长响应速度也肉眼可见地变慢。分页的 SQL 写法很简单SELECT a.*, u.nickname FROM articles a LEFT JOIN users u ON a.user_id u.id ORDER BY a.created_at DESC LIMIT ? OFFSET ?LIMIT 是每页条数OFFSET 是偏移量第几页 * 每页条数 - 每页条数。前端页码导航加上首页、上一页、下一页、末页四个按钮配合 Express 的查询参数实现起来非常顺滑。排序策略上我一开始只做了按时间排序后来加了一个“热门”排序选项按view_count like_count * 3这种加权值来排。加权的系数我调了好几次最终感觉“点赞权重略高于浏览”的效果比较好否则高浏览量但低质量的文章会一直霸榜用户刷几次就觉得平台内容没品位这是我观察自己部署后用户真实反馈得出的结论。3.3 评论与点赞事务一致性与防止重复点赞评论和点赞虽然看起来简单但对数据一致性要求特别高。评论的逻辑是用户在某篇文章详情页提交评论后后端要做两件关联动作——插入一条评论记录然后把文章的 comment_count 加 1。这两个动作必须在一个数据库事务里执行不然就会出现评论插入成功但计数没更新、或者反过来计数变了但看不到评论的脏数据。用 mysql2 的 connection 来操作事务的代码如下const conn await pool.getConnection(); try { await conn.beginTransaction(); await conn.query(INSERT INTO comments (article_id, user_id, content) VALUES (?, ?, ?), [articleId, userId, content]); await conn.query(UPDATE articles SET comment_count comment_count 1 WHERE id ?, [articleId]); await conn.commit(); } catch (err) { await conn.rollback(); throw err; } finally { conn.release(); }点赞的逻辑核心是防止重复。我的做法是建唯一索引加“先查后插有则删”的策略。用户点击点赞按钮时前端发送请求后端先查 likes 表里有没有记录有就执行删除并把文章的 like_count 减一没有就插入一条新记录并把计数加一。这就是一个优雅的“切换点赞状态”的接口。唯一索引article_id, user_id保证了并发情况下也不会出现同一个人对同一篇文章点赞两条。这里我再多提醒一句不要低估数据库索引的重要性没有这个唯一索引的时候连点两次按钮就能制造出脏数据而且线上排查这种 Bug 会非常痛苦。4. 完整实操过程从初始化到核心接口实现4.1 环境准备Node.js 安装与 npm 初始化中常见的坑Node.js 安装本身并不复杂但这里我必须提醒几个极其常见、几乎每个新手都会遇到的坑。如果你想用 npm 命令却发现报错npm : 无法加载文件 D:\Program Files\nodejs\npm.ps1因为在此系统上禁止运行脚本这是 Windows PowerShell 的执行策略限制不是 Node 安装坏了。解决办法是以管理员身份打开 PowerShell执行Set-ExecutionPolicy -ExecutionPolicy RemoteSigned然后选“是”或“Y”确认。这个操作会允许本机运行的脚本签名验证但禁止从网络下载的未签名脚本运行属于安全性可接受的折中方案。安装完 Node 后在项目目录里跑npm init -y生成 package.json然后安装本次项目需要的基础依赖npm install express ejs mysql2 bcryptjs express-session express-validator如果有需要再加开发依赖nodemon它可以监听文件变化自动重启服务省去每次改代码手动重启的麻烦。你需要在 package.json 里把启动脚本改成start: nodemon app.js这样每次保存代码服务都会自动加载调试体验直线上升。4.2 数据库初始化建库建表脚本的完整写法数据库方面我直接先用命令行或 Navicat 建好库然后在应用启动时执行建表语句。库名我用的是literature_db字符集选 utf8mb4。这里强调一下必须用 utf8mb4 而不是 utf8因为 utf8 在 MySQL 里实际上是 utf8mb3它不支持存储 emoji 和部分生僻字。文学交流平台天天都有用户评论一个表情符号把整段内容存挂是极其影响体验的问题。建表脚本大致长这样CREATE DATABASE IF NOT EXISTS literature_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE literature_db; CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, nickname VARCHAR(50) DEFAULT 文友, avatar VARCHAR(200) DEFAULT /default-avatar.png, bio VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB; CREATE TABLE articles ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, category VARCHAR(20) NOT NULL, title VARCHAR(100) NOT NULL, content TEXT NOT NULL, view_count INT DEFAULT 0, like_count INT DEFAULT 0, comment_count INT DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_category (category), INDEX idx_created (created_at) ) ENGINEInnoDB;注意文章的 title 和 content 都不是空字符串空串也能被存进去但这是业务逻辑问题需要靠后端验证解决。外键ON DELETE CASCADE的意思是用户注销时他的所有文章会被一起删除避免留下孤儿数据。这个行为在早期可以粗放一点但如果你做的是真实可运营平台更稳妥的做法是软删除或者把外键改成SET NULL这个细节可以根据你项目的答辩侧重点取舍。4.3 核心接口实现登录校验中间件与文章详情页的完整响应这个项目里最有价值的代码不是实现某个功能的简单路由而是如何优雅地复用登录校验逻辑。我写了两个中间件一个是requireLogin一个是requireOwnership// middleware/auth.js function requireLogin(req, res, next) { if (req.session.user) { next(); } else { res.redirect(/login?redirect encodeURIComponent(req.originalUrl)); } } function requireOwnership(req, res, next) { const articleId req.params.id; // 查询文章并比对 user_id 与当前登录用户 id // 不匹配就返回 403 页面 next(); }这样的中间件可以用在任何需要登录才能访问的路由上一行代码就能声明清楚router.get(/create, requireLogin, articleController.showCreatePage)。后续你加“编辑资料”“删除评论”等功能时同样的权限逻辑直接复用代码总量和 Bug 数量都会显著下降。文章详情页是功能最密集的页面完整流程是这样的后端接收文章 id先执行一条 UPDATE 语句把 view_count 加一然后再执行 SELECT 把文章详情和作者信息查出来接着执行 SELECT 把评论列表带出来最后渲染到 EJS 模板。一个页面上交互了三个查询需要注意 SQL 的执行顺序先更新浏览量再查询这样页面上显示的数字才是包含本次访问的最新值。不过这里要提醒一点把view_count的更新放在每次访问请求里的写法在高并发下会有性能损耗但这是后置优化问题课程设计阶段完全够用。4.4 异常处理与错误页不要让你的平台在崩溃时一片空白Express 默认的错误处理是返回一个 HTML 错误页但默认页面极其简陋且不友好。我在项目里做了一套统一错误处理中间件app.use((err, req, res, next) { console.error(err.stack); res.status(500).render(error, { message: 服务器开小差了请稍后再试 }); });所有业务逻辑里的throw new Error(...)都会被这个中间件捕获用户看到的是一个友好的提示页面而不是一堆堆栈信息。开发调试阶段你会想看到完整的日志所以这里加一行console.error(err.stack)是非常必要的日志是排查线上问题的第一手情报。另外404 页面也值得花两分钟设置。把所有无法匹配到路由的请求都渲染一个“页面不存在”的模板同时给出返回首页的链接用户的体验分能提升不少。5. 常见问题与排查技巧实录说实话这个项目调试过程中遇到的大部分问题都不是什么高深算法反而是环境配置、语法细节和数据库层面的常见坑。我整理了一个速查表你们做的时候可以直接对照排查。现象可能原因排查与解决方案npm 安装依赖报错 ERR_SOCKET_TIMEOUT网络连接不到 npm 源切换镜像源npm config set registry https://registry.npmmirror.comnpm 报无法加载 npm.ps1PowerShell 执行策略限制管理员身份运行Set-ExecutionPolicy RemoteSigned启动服务后浏览器访问 localhost:3000 白屏端口被占用或路由未注册改端口app.listen(3001)或检查路由是否app.use挂载登录后刷新页面又变回未登录Session Store 使用内存模式换成 MySQLStore 持久化 Session或在单机部署时检查 Cookie 是否成功写入文章内容存进去了但页面显示乱码数据库字符集不是 utf8mb4建库时指定字符集或执行ALTER TABLE articles CONVERT TO CHARACTER SET utf8mb4查询文章列表时报 “Unknown column”表结构跟查询字段不一致先DESC 表名查看结构确认字段名拼写和大小写发布文章时一直不跳转、也没有错误提示表单提交路径与路由不一致检查 form 的 action 路径是否与 router 的路径完全匹配注意前后斜杠点赞按钮点了没反应前端脚本请求接口路径写错打开浏览器开发者工具看 Network 标签里请求的状态码和响应体无法连接数据库 ECONNREFUSEDMySQL 服务没启动或端口不对检查 MySQL 服务状态确认端口是 3306用户名密码是否正确有几个问题我单独展开说说。第一个是端口问题。很多同学喜欢固定用 3000但系统其它服务比如某个前端项目时不时也会占用这个端口。启动时报错Error: listen EADDRINUSE: address already in use要么换端口要么杀掉占用的进程。在 Windows 上查找占用端口的命令是netstat -ano | findstr :3000然后记下最后一列的 PID用taskkill /PID 这里是PID /F强制干掉它。Linux/macOS 上位命令是lsof -i :3000和kill -9 PID。第二个是“中文内容存入 MySQL 报错 Incorrect string value”。这个基本就是字符集问题。如果你在建数据库时偷懒用了默认字符集通常是 latin1存中文必炸。前面讲的 utf8mb4 一定要在建库时就设置好。如果库已经建了可以这样补救ALTER DATABASE literature_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE articles CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;另外连接字符串里也要指定 charsetconst dbConfig { host: localhost, user: root, password: 123456, database: literature_db, charset: utf8mb4 };三是时间显示问题。MySQL 的TIMESTAMP类型默认存储的是 UTC 时间而你在东八区直接查出来显示在页面上会看到比本地时间慢 8 个小时。解决办法有两类一是数据库连接时设置timezone: 08:00让驱动在取数据时自动做时区转换二是查出来后手动格式化。如果你用的mysql2连接配置里加一行timezone: Z或者timezone: 08:00都能解决。千万不要为了图省事直接在 SQL 里DATE_FORMAT(created_at, %Y-%m-%d %H:%i:%s)这种做法会导致排序和后续时间操作的不一致到时候改起来非常难受。第四个是安全问题评论区里被人发脚本这事真实发生过。核心原因是直接把用户输入的 HTML 拼接进了页面。解决方式有两种一种是在渲染时对所有变量做转义EJS 默认% %会转义但%- %不会你要特别注意别误用另一种是在后端入库前就过滤掉危险标签。更稳妥的做法是两者都做。我项目里用的方法是后端在服务端渲染时统一转义凡是用%- %输出用户内容的代码全部改成% %保证安全后再考虑富文本需求如果有富文本就引入 xss 过滤库。6. 部署上线与后续扩展的经验分享6.1 本地测试与部署的基本流程开发完代码后我建议你按照下面的顺序做一遍完整的回归测试注册新账号、登录、修改资料、发文章、编辑文章、评论、点赞、退出、重新登录、验证 Session 是否还在。这些是全部核心链路只要它们全通过了项目就算基本达标。部署的话最省事的方案是在本地 Windows/Linux 上直接跑 Node 服务配合 Nginx 反向代理Nginx 负责监听 80 端口转发到 Node 的 3000 端口。如果你有云服务器还可以用 PM2 做进程守护这样服务崩溃后会自动重启不会出现“人不在服务器边服务挂了没人管”的尴尬情况。PM2 的常用命令极简三行就够了npm install -g pm2 pm2 start app.js --name literature pm2 save还有一个部署细节把项目里的敏感配置数据库密码、Session secret放到环境变量里而不是硬编码在代码中。比如用.env文件配合dotenv这个库加载然后代码里process.env.DB_PASSWORD。这个习惯在答辩时提出来非常加分它体现了你对配置管理和信息安全的意识。6.2 从毕设到真实项目的扩展思路如果你的项目想表现得更“能打”下面这几个点可以作为后续迭代的方向引入分页组件优化文章列表和评论列表前端加一个“加载更多”按钮而非纯翻页增加“个人中心”页面展示我发布的文章、我的评论历史再来一个简单的统计面板发布总数、获赞总数把分类从硬编码改成后台可维护同时加上“热门分类”排行接入 Markdown 编辑器让正文支持富文本排版这会明显提升文学类内容的阅读体验加入全文搜索MySQL 的 LIKE 配合全文索引或者引入 Elasticsearch但不是必须做一个简单的后台管理页面支持管理员登录后查看文章列表、删除违规内容、封禁用户。不要一口气全做完挑一两个感受一下把一个扩展点做深做透比你堆十个半成品功能更有说服力。写在最后的一点心得这个项目给我的整体感觉是表面上看是一个标准的“增删改查”系统但真正动手做起来把用户登录态、数据一致性、权限校验、页面渲染这些环节全部串在一起时还是有很多细节值得反复打磨。我做第一版的时候也犯了“重页面轻逻辑”的毛病把大量精力花在调 CSS 上后来发现几个页面之间的跳转逻辑拧成一团不得不重构才真正意识到好的项目架构能省下多少后期时间。如果你正在做类似的题目我给一句掏心窝的建议先确定好功能清单和数据表结构再写任何业务代码。表结构一旦定下来整个项目的主心骨就稳定了后面再怎么改都只是加加减减。数据表设计阶段多花一小时后期能少熬三个通宵这是我踩过多次坑之后最深的体会。希望这篇复盘能帮你顺利跑通这个项目也欢迎在评论区聊你在实现过程中遇到的卡点我尽量回复。
返回列表