前几天有个学弟拿了个毕设题目来问我,题目就是“ssm+vue剧本杀分享社区网站”,自带论文和程序要求。这个题目在毕业设计里太常见了——一个标准的CRUD社区项目,但真正把它做得完整、能正常演示、能写进论文里的同学其实不多。原因倒不是技术有多难,而是很多人一上来就闷头写代码,需求边界没想清楚,数据库表设计随手乱建,结果前后端一联调全是问题,论文也只能硬凑。
剧本杀分享社区本质上是“内容社区 + 剧本库 + 社交互动”的综合体,核心用户是剧本杀玩家和店家,功能至少包含登录注册、剧本浏览与搜索、剧本详情与评分、帖子发布与评论、点赞收藏、组队约本、个人中心。今天我不打算泛泛讲这个系统的功能清单,而是从需求拆解、技术选型、库表设计、核心接口实现、联调问题排查到论文写作,把整个项目的实操过程完整捋一遍。你后面做同类系统的时候,直接照着这个思路走,能少踩很多坑。
1. 项目到底做了什么——需求拆解与功能规划
1.1 从课题标题读出真实业务需求
“剧本杀分享社区网站”这个标题,关键词是“分享”和“社区”。所以你第一反应不能是单纯做一个剧本信息展示站,而是要突出UGC(用户生成内容)的属性。
我当时给学弟做的第一步就是梳理角色和业务。这个系统里至少有三种角色:
- 普通用户:注册登录、浏览剧本、发布帖子、评论帖子、点赞收藏、参与组队。
- 剧本作者/店家:除了普通用户的功能,还可以上传剧本信息、维护剧本目录。
- 管理员:管理用户、审核帖子、审核剧本、管理系统公告。
如果要简化,第一版可以把作者和普通用户合并,只保留“用户”和“管理员”两类角色。因为毕业设计时间有限,角色过多会导致权限逻辑变复杂,写论文时不好聚焦。先把用户端做扎实,再把管理端的基本管理功能做出来,答辩的时候已经很够看了。
这个系统的核心业务链路是:用户打开网站 → 浏览剧本库 → 查看某个剧本的详情和用户评价 → 决定是否去玩 → 在社区里发帖约人组队 → 玩完回来发评测帖 → 其他用户在你帖子下评论和点赞。这个闭环就是“社区”的灵魂,你设计功能的时候必须保证这条链路是通的,否则系统就是个没有生命力的信息展示页。
1.2 前后台功能边界划分
功能规划我建议画一个功能树,不一定要用专业工具,白纸上画都行。我习惯把功能分成两大类:前台用户可见的功能,后台管理可见的功能。
前台功能:
- 用户注册、登录、退出、修改个人信息、修改密码。
- 剧本列表展示,支持按类型(古风、恐怖、推理、机制、情感等)、人数、难易程度筛选。
- 剧本详情页:包含剧本封面、简介、类型标签、玩家评分、玩家评价列表。
- 社区帖子模块:帖子列表、发帖、编辑自己的帖子、删除自己的帖子、帖子详情。
- 互动功能:帖子评论、帖子点赞、收藏剧本、收藏帖子。
- 组队约本:发起组队(选择剧本、设定时间、人数上限),其他用户可以报名加入。
后台功能:
- 管理员登录与鉴权。
- 用户管理:查看用户列表、禁用/启用账号。
- 剧本管理:对用户上传的剧本信息进行审核、编辑、上下架。
- 帖子管理:审核帖子状态(正常、隐藏、删除),处理违规内容。
- 数据概览:首页展示用户数、剧本数、帖子数、评论数等基础统计数字。
这套功能列表覆盖了论文里“系统需求分析”一章的全部内容,后续写论文时几乎不用二改,直接对照功能树画用例图就能用。这也是为什么我建议你把功能边界先列清楚再动手写代码,不然写论文的时候你会发现功能描述前后对不上,特别狼狈。
2. 技术栈选型——为什么是 SSM + Vue
2.1 SSM 这套组合到底经典在哪
SSM 是 Spring + SpringMVC + MyBatis 的统称。在很多学校毕业设计的技术选型里,它几乎是“默认选项”,原因有两个:一是教学内容里普遍还在讲这套框架,二是它足够轻量,对小型项目来说恰到好处。
Spring 管对象和依赖注入,SpringMVC 管请求路由和参数绑定,MyBatis 管数据库操作,三层各司其职。一个请求进来后,流程是:前端把请求发到 SpringMVC 的 Controller,Controller 调用 Service 层处理业务逻辑,Service 层再通过 MyBatis 的 Mapper 接口操作数据库,数据返回后由 SpringMVC 渲染 JSON 响应回前端。
用 SSM 做这类系统,最舒服的一点是框架的约束感很强。Controller、Service、ServiceImp、Mapper、实体类、XML 文件,每一层该放什么代码都有固定习惯,对新手来说反而是好事——你不用纠结某段代码该放哪,照着目录结构放就行了。相比 Spring Boot 那种“约定优于配置”的自动装配,SSM 需要手动写很多配置,虽然繁琐,但对理解框架运行原理很有帮助,论文的“核心框架介绍”那一章也有得写。
提示:如果你们的选题要求里没有强制指定 SSM,我的建议是问清楚导师是否允许用 Spring Boot。如果允许,尽量用 Spring Boot,开发效率高很多。如果题目明文写了“SSM”,那就老老实实用 SSM,答辩时框架匹配度会高一些。
2.2 为什么选 Vue 做前端
前端选 Vue 而不是 JSP,核心原因是“前后端分离”这个模式在毕设答辩时更受认可。用 JSP 做传统页面,数据渲染和页面跳转都揉在服务端,写是简单,但你跟老师解释“前后端分离架构”时会有一点心虚。用 Vue 之后,前端通过 Axios 请求后端接口,拿到 JSON 数据再做页面渲染,逻辑清晰,代码结构也更好看。
Vue 用起来很直接,模板语法、组件复用、数据双向绑定,上手门槛比 React 低。我再三跟你强调一个事:毕设不是大厂面试,不需要炫技,用最稳健的工具把功能做出来才是核心。Vue 2 的 Element UI 组件库还真就是为这类后台管理场景准备的,表格、表单、弹窗、分页、导航菜单,全都有现成的组件可以用,页面档次感一下子就上去了。如果你用 Vue 3,则搭配 Element Plus,用法基本一致。
前端项目的目录结构我也顺手给你列出来,做参考:
src ├─ api // 接口请求封装 │ ├─ user.js // 用户模块接口 │ ├─ script.js // 剧本模块接口 │ ├─ post.js // 帖子模块接口 │ └─ comment.js // 评论模块接口 ├─ assets // 静态资源,如图片、全局样式 ├─ components // 公共组件,如分页、卡片 ├─ router // 前端路由配置 ├─ store // Vuex 状态管理(登录信息、token) ├─ views // 页面组件 │ ├─ Login.vue │ ├─ Register.vue │ ├─ ScriptList.vue │ ├─ ScriptDetail.vue │ ├─ PostList.vue │ ├─ PostDetail.vue │ ├─ PublishPost.vue │ ├─ GroupList.vue │ ├─ Profile.vue │ └─ admin │ ├─ AdminLogin.vue │ ├─ UserManage.vue │ ├─ ScriptManage.vue │ └─ PostManage.vue └─ main.js目录架子搭好了,后面写起来就很顺,不会出现找不到文件改哪里的情况。
2.3 数据库设计——这个项目的命根子
很多人写代码到一半发现需求对不上,根子就在数据库表设计上。剧本杀分享社区的数据库表我拆成三大块:系统基础表、内容业务表、互动关系表。
基础表有用户表sys_user,管理员的表可以直接用同一个用户表加角色字段区分,也可以单独建admin_user表,我习惯单独建,逻辑更清楚,权限校验也更简单。
业务表有剧本表script_info(字段包括剧本名、封面图、类型、人数区间、难度、时长、简介、作者、状态)、帖子表community_post(用户id、标题、正文、图片、浏览数、点赞数、状态)、评论表comment_info(用户id、帖子id或剧本id、评论内容、评论时间)、组队表group_info(剧本id、发起人id、预计时间、人数上限、已报名人数、状态)。
互动关系表有用户-剧本收藏表user_script_fav、用户-帖子点赞表user_post_like、用户-组队报名表group_apply。
这张表设计有几个细节特别容易填坑:
- 点赞和收藏必须用独立的关系表,不要给帖子表加一个“点赞数”字段然后靠更新字段来维护,否则并发操作时数据容易出问题,而且后续论文里写“数据表设计”也不够有内容。
- 用户表一定要加
status字段,用于管理员禁用账号,不然你没法实现管理员功能里的“封禁用户”。 - 所有表都要加
create_time和update_time,一是排序要用,二是论文的数据表展示里多两个时间字段会让表看起来更专业。 - 剧本表和帖子的正文建议用
TEXT类型,因为剧本杀评测帖一般都比较长,VARCHAR(255)根本装不下。
3. 后端核心实现——Controller / Service / Mapper 三层怎么配合
3.1 一个模块的完整代码链路
后面写代码的时候,你会发现整个后端其实就是在写“一个模块反复的一套流程”。拿最简单的“发布帖子”来说,大致链路是这样的:
前端拿到用户填写的表单数据,通过 Axios POST 到后端接口。Controller 接收请求,调 Service 层方法,Service 处理逻辑(校验是否登录、帖子内容是否为空),然后调 Mapper 的插入方法,最终把结果返回给前端。
Controller 层的代码大致长这样:
@RestController @RequestMapping("/api/post") public class CommunityPostController { @Autowired private CommunityPostService communityPostService; @PostMapping("/publish") public Result publish(@RequestBody CommunityPost post, HttpServletRequest request) { Integer userId = TokenUtils.getUserIdFromRequest(request); post.setUserId(userId); communityPostService.publishPost(post); return Result.success("发布成功"); } }Service 层负责处理一些前端不好处理的逻辑,比如发帖时需要给帖子做敏感词过滤,或者设置初始状态为“待审核”:
@Override public void publishPost(CommunityPost post) { if (post.getTitle() == null || post.getTitle().trim().length() == 0) { throw new BusinessException("帖子标题不能为空"); } post.setViews(0); post.setLikes(0); post.setStatus(1); // 1表示正常显示 post.setCreateTime(new Date()); communityPostMapper.insert(post); }Mapper 层代码就是一个接口加一个 XML 文件,接口里定义方法,XML 里写 SQL。如果表结构简单,直接用注解@Insert也行,但建议还是用 XML 写,因为论文里可以贴一段 SQL 展示你的数据库操作能力。
写这个过程有个建议:后端代码不要一个 Controller 写完所有接口,尽量按业务模块拆分。用户相关、剧本相关、帖子相关、评论相关、组队相关、后台管理相关,每个模块一个 Controller。这样代码长度拆开后,后续查找问题和论文画模块图都省事。
3.2 登录状态管理——Token 到底怎么用
这类前后端分离项目,最常用的登录状态方案是 JWT Token。用户在登录页输入用户名和密码,后端校验通过后生成一个 token 字符串返回前端,前端把它存到 localStorage 或者 Vuex 里,之后每次请求都在请求头的Authorization字段带上这个 token。后端用一个拦截器统一校验 token 是否有效,有效就放行,无效就返回 401,前端收到 401 就跳转到登录页。
这是一个很简洁的闭环,但写的时候有几个容易翻车的地方:
Token 里一般放用户 id 和用户名,但不要放用户密码。签发的 token 要设置过期时间,我建议设成 24 小时。短了用户用着用着就掉线,长了安全性差一些。有的同学图省事把 token 直接设成 7 天不过期,被老师问一句“安全性怎么保证”就有点难回答。
拦截器里判断请求路径,像/api/user/login、/api/user/register这种接口应该放行,其他接口统一拦截。很多同学一开始没注意,把登录接口也拦截了,导致前端登录一直失败,这个坑特别基础但特别常见。
Vue 前端这边,Axios 的拦截器也要配置一下。每次请求前从 localStorage 里取 token,加到请求头里。后端返回 401 时,清理本地存储并跳转到登录页。
3.3 文件上传——图片到底存哪儿
剧本封面、帖子配图,这些都是文件上传的典型场景。我的建议是:后端配置一个本地存储路径,前端用 Element UI 的 Upload 组件把文件上传到后端接口,后端把文件保存到指定目录,并把可访问的 URL 存到数据库里。
这里要特别强调一个坑:文件上传路径在前后端联调时非常容易出问题。后端把图片存到D:/upload/xxx.jpg,但前端要访问的时候只能通过 URL 访问,比如http://localhost:8080/upload/xxx.jpg。所以后端必须做一个静态资源映射,把本地磁盘目录映射成 URL 访问路径。
在 SpringMVC 的配置里加上资源映射:
<mvc:resources mapping="/upload/**" location="file:D:/upload/" />如果你用了 Spring Boot,则在配置类里这样写:
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceMapping("/upload/**") .addResourceHandler("/upload/**") .addResourceLocations("file:D:/upload/"); }注意,这个路径如果写错了,前端图片永远显示不出来。我在项目里遇到的情况往往是图片上传成功,数据库里也有路径,但刷新页面后图片加载失败,细查才发现是映射路径和存储路径不一致。上传时保存到数据库的 URL 也建议统一用/upload/xxx.jpg这种相对路径,不要带完整 IP 和端口,这样部署环境变了也不影响。
4. 核心业务场景实操——以“剧本评价”和“组队约本”为例
4.1 剧本信息展示与评价闭环
剧本详情页是整个网站里信息量最大的页面。顶部是封面图和基础信息,中间是剧本简介,下方是用户评价列表和评价表单。这个页面的数据来自三个接口:剧本详情接口、评价列表接口、用户是否收藏剧本的校验接口。
写设计的时候可以注意一下“评分”这个数据。用户对剧本的评分是单个字段score,但在剧本列表中,我们需要展示的是这个剧本的平均分。平均分不该在列表接口里遍历计算,那样性能很差,而且逻辑容易错。最靠谱的方案是在script_info表里加一个avg_score字段,在用户提交评分的时候,后端同时更新该字段。
更新方式是一个简单的 SQL 统计:
UPDATE script_info s SET s.avg_score = ( SELECT AVG(score) FROM script_comment sc WHERE sc.script_id = #{scriptId} AND sc.score IS NOT NULL ) WHERE s.id = #{scriptId}这个字段在剧本列表页和详情页都能直接拿来用,不用重复计算,页面响应快,代码也简洁。我在项目里就是这么做的,实测在几百条数据规模下完全没问题,而且论文里还能讲一下“冗余字段提升查询效率”这个设计思想,属于加分项。
4.2 组队约本功能——一张关联表的设计好与坏
组队约本的业务本质是:某个用户发起一个组队邀请,其他用户看到后申请加入。这个业务涉及两张核心表:组队信息表group_info和报名表group_apply。
组队信息表存的是这个队的元信息(哪个剧本、什么时候玩、几点集合、还差多少人)。报名表存的是“谁加入了哪个队”。
group_apply表的关键字段:
group_id:关联组队表。user_id:报名的用户id。apply_status:报名状态,0待确认、1已通过、2已拒绝。create_time:报名时间。
组队列表页的逻辑是查所有group_info,并且关联查出“发起人昵称”和“已报名人数/还差多少人”。这一步联表查询很常用,SQL 大概是:
SELECT g.*, u.nickname AS owner_name, (SELECT COUNT(*) FROM group_apply ga WHERE ga.group_id = g.id AND ga.apply_status = 1) AS join_count FROM group_info g LEFT JOIN sys_user u ON g.owner_id = u.id ORDER BY g.create_time DESC用户点击“加入队伍”后,后端要做一个校验:不能重复报名同一个队伍,也不能加入一个已经满员的队伍。这两个校验都要在 Service 层完成,不要指望前端自己判断,因为前端判断不可靠。
这类“两个用户之间发生一次动作”的业务,在毕设里出现频率很高。你把这个套路吃透了,后面做点赞、收藏、关注,全是同一个套路。
4.3 社区帖子的审核与敏感内容处理
剧本杀社区这种平台,帖子内容审核是管理员的核心功能之一。前端发帖时,帖子默认状态为“正常”就行,但在管理端,管理员可以对帖子进行“隐藏”或“删除”操作。隐藏的意思是不直接删数据,只是把帖子的status字段改为 0,前台列表查询时加一个where status = 1的条件,帖子就消失了。
这个“软删除”思想你必须要会,论文里也将是亮点。好处是数据不丢,用户如果申诉可以恢复。硬删除就直接DELETE,虽然也能实现,但老师问起来怎么处理误删恢复时,你不好回答。
帖子列表接口的 SQL 建议这样写:
SELECT p.id, p.title, u.nickname AS author_name, p.create_time, p.views, p.likes FROM community_post p LEFT JOIN sys_user u ON p.user_id = u.id WHERE p.status = 1 ORDER BY p.views DESC, p.create_time DESC帖子详情页的浏览量更新,可以在进入详情页时调一个接口执行UPDATE community_post SET views = views + 1 WHERE id = #{id},不需要写复杂的逻辑,但能让你的系统“有数据可看”,答辩时演示数据也不会显得空。
5. 开发中踩过的坑与排查实录
5.1 后端经典报错信息速查
最近帮学弟调一个项目,前后端联调时后端控制台报了一堆错,下面这些代码如果在你本地跑出来了,可以直接对照定位:
| 报错信息 | 可能原因 | 解决方案 |
|---|---|---|
HTTP Status 404 | 接口 URL 写错,或后端没有对应路由 | 检查请求地址和 Controller 里的@RequestMapping是否一致 |
HTTP Status 500 | 后端代码异常,往往是 SQL 或空指针 | 看控制台完整异常栈,搜最後一段 java.lang.NullPointerException 或 SQLException |
报错 Unknown column 'xxx' in 'field list' | 实体类字段和数据库字段对不上 | MyBatis 里用resultMap做映射,或改实体字段名和表字段一致 |
Access denied for user 'root'@'localhost' | 数据库账号密码错误 | 检查jdbc.properties |
Invalid bound statement (not found) | Mapper 接口和 XML 文件不匹配 | 检查 XML 文件的 namespace 是否等于 Mapper 接口完整路径 |
Required request body is missing | 前端提交的数据格式和@RequestBody不对 | 检查前端是否设置了Content-Type: application/json |
5.2 前后端联调里的跨域问题
前后端分离项目中,前端在 8080 端口,后端在 8081 端口,前端通过 Axios 请求后端接口时,浏览器会拦截跨域请求。解决方式是在后端加一个 CORS 配置。
如果是 SpringMVC,可以在 Web.xml 里加 CorsFilter,或者直接在 SpringMVC 配置中注册。如果是 Spring Boot,就简单了:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:8080") .allowedMethods("*") .allowedHeaders("*") .allowCredentials(true); } }这个配置加的位置很关键。很多同学写在 Controller 里用@CrossOrigin,也能生效,但每个 Controller 都要加,很麻烦。全局配置一次管用。allowedOrigins 如果写*会和 allowCredentials true 冲突,所以最稳妥是写明前端地址。
5.3 数据库乱码问题
中文乱码在 SSM 项目里太常见。通常有两个原因:一个是 MySQL 连接串里没有指定编码格式,另一个是数据库或表的字符集设置不对。
连接串里一定要加这串参数:
jdbc.url=jdbc:mysql://localhost:3306/script_community?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai建表的时候,字符集也要显式指定:
CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL, nickname VARCHAR(50), avatar VARCHAR(255), status INT DEFAULT 1, create_time DATETIME ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;MySQL 5.7 以上版本建议直接用utf8mb4,比utf8支持更完整的字符(包括 emoji),剧本名带个别生僻字也不怕。
5.4 按需求补接口的“开发次序”问题
很多同学做项目时习惯从后台管理开始做,先把管理员的增删改查页面做出来,再做前台首页。我建议反过来,先做前台用户主流程:注册登录 → 剧本列表 → 剧本详情 → 发帖 → 评论。把这条链路跑通了,系统的主心骨就立起来了。然后再往上面叠加管理端功能。
原因很简单,前台主流程完成之后,你手里的接口、数据表、业务逻辑已经足够写论文的主体部分了,哪怕后期时间紧,管理端功能砍掉一些,论文也能完整自洽。反过来如果只做了管理端没有前台,论文和演示都没法看。
还有一个加密的问题,用户的密码在后端存储必须用 MD5 加盐或 BCrypt 加密,不能明文存。很多毕设项目里密码直接明文入库,答辩被老师一眼看穿。这不仅仅是安全问题,更是一个暴露“工程素养不够”的细节。用 Spring 自带的DigestUtils.md5DigestAsHex就能做 MD5,代码量不大,效果却很明显。
6. 论文写作与答辩演示的实践经验
6.1 论文结构怎么摆
如果你选择这个题目,论文总体结构我建议这么排:绪论、相关技术介绍、需求分析、系统设计、系统实现、系统测试、总结与展望。
最上面的是“绪论”,这部分别写太多项目背景的宏大叙事,适当提一下剧本杀行业现状和社区网站的发展即可,重点放到“现有同类平台存在什么问题、本项目做什么”上面。导师最反感的就是绪论写三页行业背景、跟你的系统毫无关系。
“系统设计”一章里最重要的三块是:功能模块图、数据库设计(ER图和表结构)、核心业务流程。这部分内容在你开发过程中已经沉淀出来了,写的时候只需要把内容转成规范的学术表达即可。比如数据库表可以直接引你的建表语句,在论文里用三线表展示字段信息。
“系统实现”一章按模块写,每个模块配一张页面截图加一段需求描述,再贴核心代码做支撑。千万不要整页贴代码,导师看到满页代码会很崩溃;挑 10 行左右最核心的放上去就行,重点是说明你这个功能是怎么设计出来的。
“系统测试”一章很多同学不知道怎么写。不要只写“功能测试全部通过”这种一句话带过的内容。做一个测试表格,列“测试用例编号、测试模块、操作步骤、预期结果、实际结果”几个字段,凑 10 个以上关键测试用例,表格一摆,这一章就能顺利过关。
6.2 答辩演示时怎么讲
答辩演示建议走“业务故事线”,而不是“功能清单轰炸”。正确的演示顺序是:
先以用户身份从注册登录开始,到浏览剧本、查看剧本详情、收藏剧本;然后展示发帖流程,写一个剧本杀评测帖子,请另一个账号登录来评论和点赞;再展示组队约本流程,发一个组队邀请,另一个用户报名;最后切管理员账号,展示用户管理、剧本审核和帖子管理功能。
这条线走通整个业务闭环,答辩老师会看到你的系统不只是几张页面拼凑,而是一整个业务体系。演示过程中如果某个功能出 bug,比如图片加载失败或者某个按钮没反应,不要慌了乱点,直接跟老师说“这个环境下的某个配置有问题,我代码里这个是通的”,这个回答至少不会减分太多。
组队约本功能要重点讲一下报名状态的流转逻辑,因为这个模块比单纯的增删改查更有“业务深度”。你如果能把“待确认、已通过、已拒绝”三种状态的管理逻辑讲清楚,并且在页面上演示出来,那这个项目在答辩老师心里的评价就会拉开一个档次。
实操总结与个人体会
做这个项目我最深的体会是:毕设的选题不在于多花哨,而在于你能不能把一个“普通”的题目做得完整、有逻辑、能自洽。剧本杀分享社区确实不是什么新东西,技术栈也是老一套,但正因为这样,它适合用来验证你对业务分析、数据库设计、前后端交互这些基本功的掌握程度。
结果是好是坏,很大程度上不是由技术决定的,而是由你的“完成度”决定的。也就是说,与其同时开几个功能模块最后每个都烂尾,不如把一条主流程彻底打通,再从这条主线往外扩展小功能。每个功能都做到“能点、能查、能演示”比任何炫技都重要。
学弟最后拿这个系统去答辩的时候,老师提了个问题:“你这个系统如果用户量上来了,有什么瓶颈?”这个问题我当时让他回答的是数据库层面加索引、热点数据可以做缓存、对组件库按需加载,看上去答案中规中矩,但已经足以体现他对系统设计有思考了。这一点上大家也可以借鉴——做毕设不只是把代码跑起来,重要的是从工程角度看系统,哪怕只是一个很基础的架构,你也得知道它好在哪、有哪些局限。
最后再分享一个小技巧:数据库里所有表的创建时间字段,用DEFAULT CURRENT_TIMESTAMP让数据库自动生成,这样你插入数据的时候不用手动new Date(),省心不少。前端展示时间时,用dayjs格式化一下,比手动用new Date().toLocaleString()更统一规范。这些小细节,都是藏在评分标准里的隐形加分项。