1. 项目背景与核心价值
作为一个长期混迹于网文圈的老书虫,我深知一个好用的在线阅读平台对读者的重要性。去年我用Spring Boot搭建了一个小说网站,从技术选型到功能实现踩了不少坑,今天就把这个项目的完整实现思路和关键代码分享出来。
这个平台的核心价值在于解决了三个痛点:
- 对读者:整合碎片化的网文资源,提供统一的阅读体验,支持个性化书签和阅读记录
- 对作者:建立作品展示渠道,通过阅读数据分析读者偏好
- 对平台:采用模块化架构,便于后续扩展付费章节、打赏等功能
技术选型心得:Spring Boot的starter机制让依赖管理变得极其简单,相比传统SSM框架节省了至少30%的配置时间
2. 技术架构设计
2.1 整体架构方案
采用经典的三层架构:
- 表现层:Thymeleaf模板引擎 + Bootstrap5
- 业务层:Spring Boot 2.7 + Spring Security
- 数据层:MySQL 8.0 + MyBatis-Plus
mermaid复制graph TD
A[浏览器] --> B[Nginx]
B --> C[Spring Boot应用]
C --> D[MySQL]
C --> E[Redis缓存]
2.2 数据库设计关键点
主要表结构设计:
- 用户表(user):采用盐值加密存储密码
- 小说表(novel):包含作品状态字段(连载/完结)
- 章节表(chapter):建立双向链表结构实现上一章/下一章快速跳转
- 阅读记录表(reading_log):记录用户最近阅读位置
sql复制CREATE TABLE `chapter` (
`id` bigint NOT NULL AUTO_INCREMENT,
`novel_id` bigint NOT NULL,
`prev_id` bigint DEFAULT NULL COMMENT '上一章ID',
`next_id` bigint DEFAULT NULL COMMENT '下一章ID',
`title` varchar(100) NOT NULL,
`content` longtext NOT NULL,
`word_count` int DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_novel` (`novel_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 核心功能实现
3.1 用户认证模块
采用Spring Security + JWT实现无状态认证,关键配置类:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/novel/latest").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
密码加密采用BCrypt算法:
java复制public class PasswordUtils {
private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
public static String encode(String rawPassword) {
return encoder.encode(rawPassword);
}
public static boolean matches(String rawPassword, String encodedPassword) {
return encoder.matches(rawPassword, encodedPassword);
}
}
3.2 小说阅读功能
章节内容采用分段加载技术,避免大章节内存溢出:
java复制@GetMapping("/chapter/{id}")
public ResponseEntity<ChapterVO> getChapter(
@PathVariable Long id,
@RequestParam(defaultValue = "0") int segment) {
Chapter chapter = chapterService.getById(id);
String content = chapter.getContent();
// 按每2000字符分段
int segmentSize = 2000;
int totalSegments = (content.length() + segmentSize - 1) / segmentSize;
ChapterVO vo = new ChapterVO();
vo.setId(chapter.getId());
vo.setTitle(chapter.getTitle());
vo.setContentSegment(content.substring(
segment * segmentSize,
Math.min((segment + 1) * segmentSize, content.length())));
vo.setCurrentSegment(segment);
vo.setTotalSegments(totalSegments);
return ResponseEntity.ok(vo);
}
4. 性能优化实践
4.1 缓存策略
采用多级缓存架构:
- 热点数据:Redis缓存最近更新的50部小说信息
- 章节内容:使用Caffeine本地缓存最近访问的1000个章节
- 排行榜数据:定时任务每小时更新一次缓存
java复制@Configuration
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(2, TimeUnit.HOURS)
.maximumSize(1000));
return cacheManager;
}
}
4.2 数据库优化
针对小说搜索功能,采用Elasticsearch构建全文检索:
java复制public interface NovelRepository extends ElasticsearchRepository<NovelEsEntity, Long> {
@Query("{\"multi_match\": {\"query\": \"?0\", \"fields\": [\"title^3\", \"author^2\", \"description\"]}}")
Page<NovelEsEntity> search(String keyword, Pageable pageable);
}
5. 踩坑经验分享
- 章节更新并发问题:
- 现象:作者同时上传多章时出现章节顺序错乱
- 解决方案:采用数据库事务+乐观锁控制更新顺序
java复制@Transactional
public void batchUpdateChapters(Long novelId, List<Chapter> chapters) {
Chapter lastChapter = getLastChapter(novelId);
for (Chapter chapter : chapters) {
chapter.setPrevId(lastChapter.getId());
chapterMapper.insert(chapter);
lastChapter.setNextId(chapter.getId());
chapterMapper.updateById(lastChapter);
lastChapter = chapter;
}
}
- 敏感内容过滤:
- 使用DFA算法实现关键词过滤
- 建立审核流程,新上传章节需人工审核
java复制public class SensitiveFilter {
private static final SensitiveWordFilter filter = new SensitiveWordFilter();
public static String filter(String text) {
return filter.replace(text, '*');
}
}
6. 扩展功能实现
6.1 阅读进度同步
采用WebSocket实现多设备阅读进度同步:
java复制@ServerEndpoint("/ws/reading/{token}")
@Component
public class ReadingProgressEndpoint {
@OnMessage
public void onMessage(Session session, String message) {
ReadingProgress progress = JSON.parseObject(message, ReadingProgress.class);
progressService.saveProgress(progress);
// 广播给用户的其他设备
sessions.forEach(s -> {
if (s.isOpen() && !s.equals(session)) {
s.getAsyncRemote().sendText(message);
}
});
}
}
6.2 智能推荐系统
基于用户行为实现协同过滤推荐:
java复制public List<Novel> recommendNovels(Long userId) {
// 1. 获取用户最近阅读记录
List<ReadingHistory> histories = historyService.getRecentHistories(userId);
// 2. 找出相似用户
List<Long> similarUsers = userService.findSimilarUsers(userId);
// 3. 合并推荐结果
return novelService.getRecommendedNovels(
histories.stream().map(h -> h.getNovelId()).collect(Collectors.toList()),
similarUsers
);
}
7. 部署实践
7.1 容器化部署
Docker Compose编排文件示例:
yaml复制version: '3'
services:
app:
image: novel-app:1.0
ports:
- "8080:8080"
depends_on:
- mysql
- redis
environment:
- SPRING_PROFILES_ACTIVE=prod
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=novel
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:6.2
ports:
- "6379:6379"
volumes:
mysql_data:
7.2 性能监控
集成Prometheus + Grafana监控关键指标:
java复制@Configuration
public class MetricsConfig {
@Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags(
"application", "novel-web",
"region", System.getenv("REGION")
);
}
}
8. 项目演进方向
- 移动端适配:开发React Native跨平台应用
- 内容生态建设:接入第三方内容API扩展书库
- 商业化探索:会员订阅+广告系统的集成
- AI应用:基于NLP实现智能书评生成
经验之谈:初期版本功能不必追求大而全,但架构设计一定要留有扩展空间。我们第一个版本只实现了核心阅读功能,但后续每个扩展模块都能平滑接入