作为一个长期混迹文学论坛的老用户,我深知传统文学社区存在的痛点:创作工具简陋、互动形式单一、内容推荐机械。去年我和团队决定用现代技术栈重构这类平台,于是诞生了这个基于SpringBoot+Vue3的文学创作社交系统。
这个系统的核心目标是解决三个问题:
我们选择的技术组合并非偶然。SpringBoot的后端稳定性、Vue3的前端响应速度、MyBatis的灵活SQL控制,加上Redis和Elasticsearch这对性能黄金搭档,共同支撑起了这个日均PV超10万的文学社区。
采用前后端分离架构时,我们特别设计了API网关层来处理跨域和鉴权问题。前端Vue3项目通过axios与后端交互时,所有请求都会先经过这个网关层:
java复制@Configuration
public class GatewayConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST")
.allowCredentials(true)
.maxAge(3600);
}
}
重要提示:生产环境务必配置具体的allowedOrigins而非通配符,我们初期就因为这个疏忽导致过CSRF攻击。
JWT的实现我们选择了jjwt库,但在token刷新机制上做了特殊处理。不同于常见的双token方案,我们采用单token+滑动过期策略:
java复制public String refreshToken(String oldToken) {
Claims claims = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(oldToken)
.getBody();
// 仅当token剩余有效期小于1小时才刷新
if (claims.getExpiration().getTime() - System.currentTimeMillis() > 3600000) {
return oldToken;
}
return Jwts.builder()
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + 3600000))
.signWith(SignatureAlgorithm.HS512, secretKey)
.compact();
}
这种设计显著降低了Redis的存储压力,在用户量突破5万时依然保持毫秒级响应。
用户表的设计我们经历了三次迭代。最终版本增加了salt字段用于增强密码安全性,并将status字段细化为多种状态:
sql复制CREATE TABLE `user_info` (
`user_id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`password_hash` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`salt` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`avatar_url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`register_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_login_time` datetime DEFAULT NULL,
`status` tinyint NOT NULL DEFAULT '1' COMMENT '0-禁用 1-正常 2-未激活 3-临时封禁',
PRIMARY KEY (`user_id`),
UNIQUE KEY `idx_username` (`username`),
UNIQUE KEY `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
文学作品表最关键的优化是在category字段上添加了全文索引,并配合Elasticsearch实现分级检索:
sql复制ALTER TABLE `literary_work` ADD FULLTEXT INDEX `ft_idx_title_content` (`title`, `content`);
我们测试发现,当作品量超过10万条时,纯MySQL的LIKE查询响应时间超过2秒,而引入Elasticsearch后相同查询仅需200ms左右。具体实现方案:
java复制@Repository
public interface WorkSearchRepository extends ElasticsearchRepository<WorkDocument, Long> {
@Query("{\"multi_match\": {\"query\": \"?0\", \"fields\": [\"title^2\", \"content\"]}}")
Page<WorkDocument> findByKeyword(String keyword, Pageable pageable);
}
经过对比测试,我们最终放弃了主流的wangEditor,选择了定制化的Tiptap编辑器。关键配置如下:
javascript复制const editor = new Editor({
extensions: [
StarterKit,
Placeholder.configure({
placeholder: '开始你的文学创作...',
}),
// 自定义诗歌排版扩展
PoetryExtension,
],
editorProps: {
attributes: {
class: 'prose prose-sm sm:prose lg:prose-lg xl:prose-xl mx-auto focus:outline-none',
},
},
})
这个选择让我们实现了独特的诗歌排版功能,成为吸引诗人的关键卖点。
评论功能采用WebSocket实现实时推送,但加入了智能节流机制防止刷屏:
java复制@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic")
.setTaskScheduler(taskScheduler())
.setHeartbeatValue(new long[] {10000, 10000});
config.setApplicationDestinationPrefixes("/app");
}
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(2);
scheduler.setThreadNamePrefix("ws-");
return scheduler;
}
}
我们的生产环境采用Docker Swarm而非Kubernetes,对于中小型应用更为轻量。关键docker-compose配置:
yaml复制version: '3.8'
services:
app:
image: xabo-backend:${TAG:-latest}
deploy:
replicas: 3
environment:
- SPRING_PROFILES_ACTIVE=prod
- REDIS_HOST=redis
depends_on:
- redis
- mysql
redis:
image: redis:6-alpine
command: redis-server --save 60 1 --loglevel warning
volumes:
- redis_data:/data
volumes:
redis_data:
我们使用Prometheus+Grafana监控体系,特别针对文学社区定制了内容热度指标:
yaml复制# prometheus.yml 片段
scrape_configs:
- job_name: 'spring'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['app:8080']
relabel_configs:
- source_labels: [__address__]
target_label: instance
regex: '(.*):\d+'
replacement: '$1'
上线首日遭遇的连接池耗尽问题,最终通过以下方案解决:
properties复制spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.leak-detection-threshold=5000
xml复制<settings>
<setting name="defaultStatementTimeout" value="30"/>
</settings>
用户长时间使用后页面卡顿,经排查发现是富文本编辑器实例未正确销毁。解决方案:
javascript复制onBeforeUnmount(() => {
if (editor) {
editor.destroy()
editor = null
}
})
目前我们正在开发作者协作功能,允许多个作者共同创作一部作品。技术难点在于实现实时协同编辑,我们评估了Operational Transformation和CRDT两种方案后,最终选择使用Y.js库来实现:
javascript复制const ydoc = new Y.Doc()
const provider = new WebsocketProvider(
'wss://your-websocket-endpoint',
'your-room-name',
ydoc
)
const yXmlFragment = ydoc.getXmlFragment('prosemirror')
const editorView = new EditorView(document.querySelector('#editor'), {
state: EditorState.create({
schema,
plugins: [
...setup({
yXmlFragment,
// 其他配置
})
]
})
})
这个文学社区项目让我深刻体会到,技术选型必须服务于产品特性。比如我们为诗歌排版专门开发的CSS模块:
css复制.poetry-line {
text-align: center;
margin: 0.5em 0;
line-height: 2;
position: relative;
}
.poetry-line::before {
content: "❦";
position: absolute;
left: -1.5em;
opacity: 0.3;
}
这些细节处的用心,才是留住创作者的关键。在性能优化方面,我们最近引入了Workbox实现PWA缓存策略,使二次访问加载时间从3秒降至800毫秒。技术永远只是手段,打造有温度的创作社区才是我们的终极目标。