每年带毕业生做项目,小说阅读平台几乎是出现频率最高的选题之一。SpringBoot版本也好,SSM版本也好,核心需求其实没变——一个能看书的网站,后台能管书、管用户、看数据。就这么简单,但要把这几张表、十几个页面、几千行代码做成一个能演示、能答辩、能跑通完整闭环的系统,工作量并不比想象中少。
这个题目的热度有很现实的原因:小说平台天然自带用户端和管理端两端结构,功能边界非常清晰,从注册登录到阅读追书,从后台录入书籍到统计报表,一条线串下来,刚好覆盖毕设评审关心的所有模块。而且在此基础上,不管往哪个方向加亮点都可行——接入推荐算法,就是“智能化电子小说服务平台”;把阅读行为做成数据统计和用户画像,就是“网络文学数字化阅读平台”。本质上还是同一个系统,只是包装不同。
这篇文章就结合我实际带项目的经验,把这类项目的核心设计思路、数据库建模、功能实现要点、常见坑点和答辩展示技巧完整拆一遍。不管你是正准备开题,还是已经写到一半想补充亮点,都可以直接参考。
1. 项目整体定位与技术选型拆解
1.1 小说项目天然适合做毕业设计的底层原因
先把话说透:这个项目之所以每年都有人做,核心不是小说好看,而是它的业务模型非常“完整”。一个小说平台要跑起来,用户端得有首页展示、注册登录、书籍分类、搜索、详情页、阅读器、书架、历史记录;管理端得有书籍管理、章节管理、分类管理、用户管理、轮播图和公告配置。这个功能矩阵恰好覆盖了Web开发最常见的所有基本功:增删改查、分页搜索、文件上传、权限拦截、关联表查询。
同时它又保留了大量“可拔高”的空间。同样是做完基础功能,你可以在阅读记录上做时长统计,再基于用户偏好做内容推荐,这就是“智能化”的落点;也可以在后台设计一套数据看板,把每天的新增用户数、阅读人数、热门书籍排行做成可视化图表,这就是“数字化”的落点。也就是说,三个项目标题指向的本质是同一个系统,只是表达侧重点不同,答辩时完全可以结合自己实际做的功能去解释题目内涵。
还有一点很实际:小说项目的数据容易获取。网上的开源小说数据包很多,一本书带几十个章节的文本很容易找到,导入数据库后系统马上就能演示出“像样”的效果。这一点在时间紧张的毕业季里,价值非常大。
1.2 技术栈选型的底层逻辑
现在这类项目的主流技术栈已经收敛得比较统一了,我建议按下面这套来组合,稳定性最高:
| 层次 | 选型 | 理由 |
|---|---|---|
| 基础框架 | SpringBoot 2.7.x | 生态成熟,社区资料多,碰到问题一搜就有答案 |
| ORM框架 | MyBatis-Plus | 单表CRUD不用写SQL,分页插件开箱即用 |
| 数据库 | MySQL 8.0 | 最常用的开源关系型数据库,支持JSON、窗口函数 |
| 缓存 | Redis 5.x以上 | 缓存章节内容、Token、排行榜数据,也是“高性能”亮点 |
| 鉴权 | JWT + 拦截器 | 无状态认证,代码简洁,比Session更适合前后端分离 |
| 前端 | Layui或Vue + ElementUI | 管理端用Layui最快,用户端推荐Vue,按自己熟悉程度选 |
SpringBoot版本选2.7.x而不是3.x,主要是考虑到兼容性。3.x要求JDK17,而且部分老依赖的适配还不完善,毕设阶段没必要在这上面折腾。JDK8配SpringBoot2.7是全行业验证过无数次的稳定组合,跑起来不闹心。
MyBatis-Plus在这个项目里是“省时间神器”。一张书表、一张章节表、一张用户表,所有单表查询都不用写XML,直接用LambdaQueryWrapper链式查询。分页也简单,配置一个PaginationInnerInterceptor,然后调用Page方法就能拿到分页结果,手写SQL的工作量至少减掉三分之一。真正需要手写SQL的地方只有复杂的统计报表和多表关联查询,集中处理这一小部分即可。
Redis缓存这一步我劝你不要省。虽然不用Redis项目也能跑,但用了Redis之后就有三处实打实的优化点:章节阅读内容缓存、首页热门书籍缓存、Token的主动失效控制。这三处代码量不大,但答辩时随便讲一个都是加分项,属于性价比极高的投入。
2. 功能模块拆解与数据库核心表设计
2.1 用户端功能清单
用户端是这个系统的门面,功能可以拆成六个核心板块,每一块背后都对应数据库的表设计和接口设计。
注册登录:用户名加密码是最基本的,密码必须用MD5加盐或BCrypt加密存储,不能明文入库。登录成功后后端签发JWT返回给前端,前端把Token存到localStorage,之后每次请求在请求头带上。这里建议加一个“记住我”的逻辑,用Redis给Token设置7天的过期时间,超过就强制重新登录。
首页展示:一般包含顶部轮播图、热门小说推荐、最新更新、分类入口。这些数据都来自book表,通过一个查询接口按不同条件排序返回。为了避免每次都查数据库,首页数据可以用Redis缓存10到15分钟,数据量不大,缓存命中率极高。
书籍分类与搜索:分类用category表维护一级分类,比如玄幻、都市、历史、科幻。搜索结果支持按书名和作者模糊搜索,简单场景下用MySQL的LIKE就够。如果你想在答辩时讲“全文检索”,可以把搜索引擎引进来,但对毕设来说不是必需项,后面我会再分析。
阅读器和书架:阅读器是用户高频使用的核心页面,包含章节列表、正文内容、翻页按钮、上一章下一章跳转。书架表专门记录用户收藏的书籍以及读到哪一章,这样“继续阅读”功能才有数据支撑。阅读过程中后端记录阅读历史,包括阅读时长和阅读进度。
书评和点赞:用户可以在书籍详情页发表评论,后台管理端有评论审核功能。这个小模块涉及一对多关联查询,是考验联表能力的好地方,也是完善业务闭环的必要环节。
个人中心:展示用户的头像、昵称、阅读统计(读过多少本书、总阅读时长),以及我的书架、我的评论列表。这些数据都是从不同表里聚合出来的,很自然地引出了统计查询的需求。
2.2 管理端功能清单
管理端不是简单地把用户端表结构套个管理界面,它的核心价值在于“管理”和“统计”两条线。
书籍管理是最重的一块。它的功能包含添加书籍、上传封面、维护书籍状态(连载中、已完结)、编辑书籍基本信息、上下架操作,以及章节管理的全部操作——在小说的章节树里增加、删除、修改章节内容。很多同学会把精力全放在用户端展示上,到了管理端草草写个列表就完事,这是很吃亏的。评审老师翻后台看到章节管理、评论管理是能直观感受到工作量差异的。
用户管理包括用户列表查询、禁用和启用用户、重置密码。整个管理端还应该配上管理员登录验证,普通用户Token调后台接口需要拦截器做权限判断,Method级别用注解标识角色,这一步也体现权限控制的要点。
数据统计这块,我建议做一个数据看板页面,用图表展示近7天新增用户、每日阅读人数、热门书籍TOP10、分类占比。这正好呼应“数字化阅读平台”的定位,而且图表库用ECharts很容易搞定,代码量不大,展示效果却很直观。后面第4部分我详细拆这个模块的实现。
2.3 数据库设计:核心表与字段梳理
数据库设计是整个项目的地基,表关系理不顺,后面写代码会处处别扭。小说平台的核心表大概有7张,字段设计我按实际项目经验整理如下。
user用户表:id主键,username唯一索引,password加密后的密码,nickname用户昵称,avatar头像地址,phone手机号,email邮箱,status用户状态(1正常、0禁用),create_time注册时间。需要注意password字段长度至少64,因为BCrypt加密后的字符串长度是60位,太短会报错。
book书籍表:id,book_name书名,author作者,category_id分类ID(关联category表),cover封面图URL,intro书籍简介,word_count总字数,status连载状态,click_count点击量,subscription_count收藏数(冗余字段,避免频繁count查询),is_delete逻辑删除标记,create_time上架时间。这里有一点要特别提醒:书名、作者、分类ID这三个字段要建联合索引,因为搜索和列表筛选都会用到这三个条件组合。
chapter章节表:id,book_id所属书籍,chapter_no章节序号,title章节标题,content章节内容(用longtext类型),word_count章节字数,create_time发布时间。content字段是大字段,列表查询时千万注意不要select所有字段,只查id、title、chapter_no这些轻量字段,内容等进阅读页再单独按id查。
category分类表:id,category_name分类名称,sort排序值,status是否展示。
book_shelf书架表:id,user_id用户ID,book_id书籍ID,last_read_chapter_id最近阅读章节ID,create_time加入时间。这张表的核心是“书架+阅读进度”二合一,加一个唯一索引uniq_user_book(user_id, book_id),防止同一本书重复加入书架。
read_history阅读历史表:id,user_id,book_id,chapter_id,read_percent阅读进度百分比,duration_seconds本次阅读时长,create_time。这张表是“数字化阅读”功能的数据基础,按用户和书籍可以聚合出阅读行为和用户偏好。
comment评论表:id,user_id,book_id,content评论内容,status审核状态,create_time。表之间通过外键字段逻辑关联,不需要物理外键,否则删除和导入数据时都会被约束拖慢速度。
3. 核心功能实现要点详解
3.1 登录鉴权与拦截器方案
登录鉴权是整个系统的安全底座,这里用一个标准方案来落地。用户登录成功后,后端生成JWT字符串返回给前端,Token里只放userId和username,过期时间设置为7天。同时把Token存入Redis,key为login:token:{userId},value为Token字符串,过期时间同样是7天。为什么Redis里还要存一份?因为这样后台禁用一个用户时可以直接删掉他的RedisToken,实现“踢人下线”的效果,这是纯JWT做不到的主动失效控制。
拦截器负责统一鉴校。定义一个HandlerInterceptor,preHandle方法里从请求头取Token,解析成功且与Redis中存储的一致就放行,否则返回401状态码。放行后把解析出来的userId放入ThreadLocal,这样后续业务代码里随时能拿到当前登录用户ID,不用每个接口重复解析一遍Token。
配置拦截器时注意排除登录、注册、首页、书籍列表这些公开接口,不然用户没登录就没法浏览网站了。具体的拦截器配置代码如下:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/api/**") .excludePathPatterns("/api/user/login", "/api/user/register") .excludePathPatterns("/api/book/**"); // 书籍浏览接口公开 } }登录接口的核心逻辑也很清晰:查用户表验证密码,通过后生成Token,再往Redis存一份。密码加密我建议用BCryptPasswordEncoder,不用自己实现加密算法,安全级别高而且SpringSecurity里可以直接复用这个类。
3.2 章节阅读与缓存设计
章节阅读是性能优化最好的切入点,因为每本书的章节内容基本不变,特别适合用Redis缓存。阅读接口的业务逻辑是:前端传入chapterId,后端先查Redis,key设计为chapter:content:{chapterId},如果命中就直接返回;没命中就查数据库再写回缓存,并设置比如12小时的过期时间。
缓存这块有个细节我要重点提醒:管理员修改章节内容后,因为章节表里有长时间存在的Redis缓存,修改内容在12小时内不会生效,所以后台修改章节必须调用一次删除缓存逻辑,删除key为chapter:content:{chapterId}的数据,再重新查询时才会从数据库加载新内容。代码实现非常简单:
public ChapterVO getChapterContent(Integer chapterId) { String cacheKey = "chapter:content:" + chapterId; // 先查缓存 Object cacheData = redisTemplate.opsForValue().get(cacheKey); if (cacheData != null) { return (ChapterVO) cacheData; } // 缓存没命中,查数据库 Chapter chapter = chapterMapper.selectById(chapterId); ChapterVO vo = convertToVO(chapter); // 回写缓存,12小时过期 redisTemplate.opsForValue().set(cacheKey, vo, 12, TimeUnit.HOURS); return vo; }阅读进度的跟踪也在阅读接口里完成。每次请求阅读接口时,根据userId、bookId、chapterId更新书架表的lastReadChapterId,同时往阅读历史表插入一条记录。这里建议做一个简单的去重逻辑:如果一分钟内重复阅读同一章,就不再插入重复记录,而是累加阅读时长。
这里再提一个查询性能细节:章节列表展示时不要查正文内容,只查章节表里除content之外的字段。不然首页展示书籍列表时会把所有章节约几十万字的文本全部查出来,内存瞬间爆炸。可以用MyBatis-Plus的select方法只查指定列,或者手写一个轻量查询SQL。
3.3 书架与继续阅读逻辑
“继续阅读”是小说平台体验最核心的细节,实现起来不复杂,但表设计必须提前想清楚。书架表里我特意放了lastReadChapterId这个字段,它的作用就是记录用户读到哪一章。
用户点击“继续阅读”按钮时,后端根据用户ID从书架表查出所有记录,再联章节表查出这些书的最新阅读章节信息,最后按更新时间倒序返回。前端拿到列表后直接展示“更新时间”和“阅读到第X章”,用户点击即可跳转到对应章节。
加入书架的接口要注意防重复。前端可以先调一次检查接口,如果这本书已经在用户书架上就直接提示“已在书架”,后端也用唯一索引兜底,捕捉重复插入的异常并给出友好提示。删除书架就简单了,直接按user_id和book_id删除即可。
3.4 搜索与排行榜的实现路径
搜索功能在毕设阶段用MySQL的LIKE就能覆盖需求,但要写出效率不差的查询,关键在“搜什么字段、怎么搜”。书名和作者是最高频的搜索条件,用联合索引能大幅提升查询速度。SQL类似这样:
SELECT id, book_name, author, cover FROM book WHERE book_name LIKE CONCAT('%', #{keyword}, '%') OR author LIKE CONCAT('%', #{keyword}, '%') ORDER BY click_count DESC LIMIT 20;如果你想让搜索功能显得更有技术含量,可以引入全文索引或者搜索引擎,但这里我要说句实在话:对于几万本书的测试数据来说,MySQL的LIKE查询在百万行以内性能完全够用,搜索引擎带来的架构复杂度对毕设来说反而是负担,不一定能说出深度来实现细节。与其把时间花在Elasticsearch部署上,不如把推荐和统计这些业务价值更高、更容易讲清楚的模块做好,答辩时性价比更高。
排行榜功能是小说网站的标配,实现方案有很多种。简单做法是直接查books表按click_count、subscription_count排序;如果想展示近7日热榜,就基于read_history表做时间范围聚合。SQL示例如下:
SELECT b.id, b.book_name, COUNT(*) AS read_count FROM read_history r INNER JOIN book b ON r.book_id = b.id WHERE r.create_time >= DATE_SUB(NOW(), INTERVAL 7 DAY) GROUP BY r.book_id ORDER BY read_count DESC LIMIT 10;这种SQL在数据量小时直接查没问题,如果阅读历史表数据量大,需要给create_time和book_id建联合索引。排行榜数据变化不算频繁,可以缓存1小时再刷新,接口响应速度会快很多。
4. 智能化与数字化亮点落地
4.1 阅读行为数字化:字段设计与数据采集
题目里的“数字化阅读平台”落到代码层面,核心就是把阅读行为变成可统计的数据。这就要靠read_history表里埋的字段:用户ID、书籍ID、章节ID、阅读时长、阅读进度、阅读时间。
阅读时长的统计逻辑在前端做的话,可以在进入阅读页时记录时间戳,退出页面或翻页时计算停留时间,把时长传给后端;前端逻辑比较多,但结果准确。更简单的方案是在后端记录每次请求阅读接口的时间差,但受网络影响误差偏大,数据不太准确。我建议双端配合:前端轮询上报,每30秒上报一次当前章节的阅读进度和累计时长,后端接口做幂等处理,按user_id和chapter_id累加时长。
这套数据采集完,就能很容易地看到这些有价值的统计结果:每个用户的日均阅读时长、每本书的完读率、每个分类的阅读热度分布。完读率的计算很有意思,它是判断小说质量的重要指标,统计的是读过某本书90%以上章节的用户占所有读过该书用户的比例。这些指标一算出来,“数字化”的味道就出来了。
4.2 简单可用的个性化推荐逻辑
推荐系统听起来高大上,但毕设阶段完全没有必要上协同过滤或矩阵分解那套复杂算法。一条非常实用且能讲清楚的逻辑是“基于分类偏好的热度推荐”:通过用户的阅读历史统计出用户阅读量最大的Top3分类,从这些分类里筛选点击量高且用户没读过的书推荐给用户。
实现代码大概也就四十行。先查阅读历史和书籍表关联,按分类统计阅读次数,排序取前三,再根据这三个分类查书,按点击量倒序取20本,排除用户已经在书架上的书籍。这个逻辑虽然朴素,但效果在演示时非常直观,跟用户最近读的书高度相关,评审基本都会认可。
更时髦一点的做法,是把用户分组做联合推荐。比如找到与当前用户阅读偏好相似的其他用户(也是按分类统计算余弦相似度),把那些用户读过且当前用户没读过的书推荐出来。这个实现复杂度高一些,但只要设计思路讲清楚,数据结构和算法逻辑都摆在代码里,作为“智能化”的亮点完全够格。
4.3 数据看板:让后台真正智能化
后台的数据看板是这个项目“数字化”定位最直接的体现。我的建议是做四个核心指标和一个排行榜页面。四个核心指标分别是:总用户数、今日新增用户、今日阅读人数、今日新增评论数。排行榜则是热门书籍TOP10、分类阅读占比、近7日活跃趋势。
前两个指标查user表的count和按日期过滤的count就能拿到。今日阅读人数用count(distinct user_id)从read_history表中统计。近7日趋势可以用一条SQL按日分组,类似这样:
SELECT DATE(create_time) AS day, COUNT(DISTINCT user_id) AS active_users FROM read_history WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) GROUP BY DATE(create_time) ORDER BY day;前端用ECharts折线图展示active_users的数组,柱状图展示分类占比,一个可视化大屏就完成了。这套看板代码量不大,但一打开后台,视觉效果和项目档次立刻就上去了,强烈建议保留。
5. 实操过程:从零搭建到本地跑通
5.1 环境准备与数据库初始化
先把基础环境准备齐:JDK8、Maven 3.6以上、MySQL 8.0、Redis。这四个装好后,用IDEA直接创建SpringBoot工程,勾选Web、MySQL驱动、MyBatis-Plus依赖。注意MyBatis-Plus要用最新的starter,老版本的包名和分页插件配置方式有区别。
数据库方面,先建一个novel_platform的库,字符集用utf8mb4,然后执行建表SQL。如果你是用Navicat,可以直接把项目里的sql文件拖进去执行。导入小说数据时,我会建议先导入书籍数据和分类数据,再导入章节数据,因为章节表有外键关联,顺序错了会报错。
5.2 application.yml配置
配置文件是项目跑起来的第一道关卡,里面有几处容易踩坑的地方,我直接给出一份完整可用的配置:
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/novel_platform?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 redis: host: localhost port: 6379 database: 0 servlet: multipart: max-file-size: 10MB max-request-size: 20MB mybatis-plus: mapper-locations: classpath*:mapper/*.xml configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl这里最重要的就是数据库URL里的characterEncoding=utf8和serverTimezone=Asia/Shanghai。不配置这两个参数,中文会乱码,时间会有8小时时差,这种问题排查起来很费劲,配置一次到位最省事。
5.3 测试数据导入
数据是项目演示的灵魂。这里提供三种方案:第一种是网上找现成的SQL文件直接导入,最省事但可能与表结构不完全匹配,需要手动调整;第二种是写一个简单的SpringBoot启动监听器,读取txt文本小说文件解析后插入数据库——这个方法代码量不大但很唬人,体现你的数据导入设计能力;第三种是后台章节管理页面手工添加,适合少量测试,不推荐作为主要方案。
我实操下来最推荐第二种。写一个命令行运行器,扫描指定目录下的文本文件,按文件名识别书名和作者,按文件内容按章分解章节标题和正文,批量插入数据库。几十本小说也就一两分钟跑完,演示效果非常充实。这个模块本身在答辩时还能作为“数据采集与预处理”能力来讲,一鱼两吃。
5.4 打包部署要点
项目在本地跑通后就需要打jar包做部署演示。在IDEA里点右侧Maven面板的clean再点package,生成target目录下的jar文件。这个阶段有两点经验值得分享:
第一点,打包时如果报“程序包不存在”之类的错误,多半是本地Maven仓库依赖没下载完整,先执行mvn clean,再执行mvn dependency:resolve拉一遍依赖。第二点,服务器上跑jar包时建议用如下命令,把日志输出到文件方便排查:
nohup java -jar novel-platform-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod > app.log 2>&1 &服务器上部署时要把yml里的数据库地址和密码改成生产环境的,同时确认数据库开启了远程访问权限。用局域网IP在手机上访问一下,能正常打开首页和阅读页面,部署就算成功了。
6. 常见问题与排查技巧实录
6.1 启动报错类问题
启动报错是出现频率最高的问题,我按实际经验把常见场景列成速查表:
| 问题现象 | 根本原因 | 解决方案 |
|---|---|---|
| 启动时提示端口占用 | 8080端口被其他进程占用 | 执行netstat -ano | findstr 8080查看PID,结束进程或改端口 |
| 启动后注册了数据源但卡住 | MySQL连接信息错误 | 检查yml中用户名密码及数据库名,用Navicat测试能否连通 |
| 提示时区错误 | 数据库连接串缺timezone | url末尾加serverTimezone=Asia/Shanghai |
| Redis连接失败 | Redis服务未启动或密码不对 | 先执行redis-cli ping确认,再检查配置 |
这几种问题里,端口占用最容易让人焦躁。遇到这种情况保持冷静,把配置文件里server.port改成8081再启动,马上就能验证是不是端口问题。如果是线上环境,还要检查防火墙有没有放行对应端口。
6.2 数据库与编码问题
中文乱码是毕设项目最常见的噩梦,而且表现五花八门:MySQL命令行查出来正常,但网页上显示问号;数据库表能查到数据但导出SQL再导入就乱了。根源基本都是字符集不统一。
我的建议是一套配置走到底:建库时指定utf8mb4,连接串里带characterEncoding=utf8,表结构里所有varchar字段也显式声明utf8mb4。utf8mb4比utf8多支持部分特殊字符(如emoji和生僻字),小说内容里可能出现各种字符,用utf8mb4最稳妥。如果已经导入的数据乱了,先把数据库导出为SQL,用文本编辑器批量替换字符集声明,再重新导入。
6.3 缓存一致性与性能问题
Redis缓存引入后,最常见的坑是“后台改了数据,前台没变化”。这个问题基本都出在缓存和数据库的一致性上。比如修改章节内容后只更新数据库忘记删除缓存、首页书籍列表缓存时间设得太久导致新上架的书半天看不到。针对这些问题,我的经验是每次做写操作,都在Service层顺手删相应缓存,并把这个动作写成习惯。缓存时间不要贪长,书籍列表15分钟、章节内容12小时就已经够用。
另一个性能大坑是列表页查最新章节时触发N+1查询。比如首页某本书要显示最新章节标题,如果写成循环里查章节表,书籍多时极其缓慢。正确做法是先用一条SQL查出所有需要展示的书籍ID,再按ID集合批量查章节表,最后在内存里做组装。MyBatis-Plus的listByIds配合循环构建关联Map,这个过程只要逻辑理清楚,代码量也不多。
6.4 答辩演示时的高频翻车点
最后说几个答辩现场容易翻车的细节,这些没经历过绝对想不到,每一个都是真实案例。
第一个是演示前没清理测试脏数据。项目里被反复改了多次状态的用户、或者一两本没封面的书,在演示时被评审翻到会显得很粗糙。我建议每个演示环境准备一套干净的演示数据脚本,演示前几分钟执行一次清库和导入。
第二个是网络环境切换导致数据库连接失败。答辩教室的WiFi换了网段,原来配置的localhost能访问,但如果数据库不在本机,IP变了就会连接失败。最好的办法就是答辩前把应用和数据库都放在本机,配置写localhost,不依赖外部网络。
第三个是上传图片失效。管理端上传的书籍封面存储在本地磁盘,打包部署后war包重启图片就丢了。正确做法是把上传目录配置成外部磁盘路径,在yml里用绝对路径配置,比如file.upload-dir=/data/upload,部署时创建对应文件夹。这样重启不会丢文件,演示也更稳定。
说实话,这类项目做到最后,真正拉开差距的往往不是技术栈多前沿,而是细节处理得是否完整。一个能讲清楚“为什么用Redis缓存章节内容”“为什么阅读记录表要冗余用户和书籍信息”“怎么通过阅读时长数据做推荐”的候选人,比一个简单堆砌功能的人评分高出一大截。把上面这些逻辑吃透,答辩时你就能从“做了个网站”升级到“设计了一个阅读服务系统”,这就是标题里“智能化”和“数字化”最实在的注脚。