1. 项目概述与背景
同人创作与分享平台是一个基于Java技术栈构建的Web应用系统,旨在为同人创作者提供作品发布、交流互动的专属空间。这个系统采用了当前主流的SpringBoot+SSM(Spring+SpringMVC+MyBatis)框架组合,配合MySQL数据库实现数据持久化,是一个典型的Java EE企业级应用。
在内容创作领域,同人作品一直拥有庞大的爱好者群体。传统论坛式的分享方式存在内容分散、互动形式单一等问题。而专门化的同人平台能够:
- 提供更聚焦的创作环境
- 实现作品分类管理
- 支持多种互动形式(评论、收藏、点赞等)
- 保障创作者权益
这个系统的技术选型考虑了以下因素:
- Java生态的成熟稳定,适合构建需要长期维护的内容平台
- SpringBoot的快速开发特性,能缩短项目迭代周期
- SSM框架在企业级应用中的广泛验证
- MySQL对中小型内容平台的数据支持能力
2. 技术架构详解
2.1 整体技术栈
系统采用经典的三层架构:
code复制表现层:Thymeleaf模板引擎 + Bootstrap前端框架
业务层:SpringBoot 2.7 + Spring 5.3 + SpringMVC
数据层:MyBatis 3.5 + MySQL 8.0
2.2 核心框架整合
2.2.1 SpringBoot配置要点
在application.yml中需要特别注意的配置项:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/fanwork_db?useSSL=false&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
thymeleaf:
cache: false # 开发时关闭缓存
mode: HTML
encoding: UTF-8
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true # 自动驼峰转换
2.2.2 SSM整合关键代码
- MyBatis配置类:
java复制@Configuration
@MapperScan("com.fanwork.mapper")
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setTypeAliasesPackage("com.fanwork.entity");
return sessionFactory.getObject();
}
}
- 事务管理配置:
java复制@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
2.3 数据库设计
2.3.1 主要实体关系
系统核心实体包括:
- 用户(User)
- 作品(Work)
- 评论(Comment)
- 收藏(Collection)
- 标签(Tag)
ER图关键部分:
code复制用户 --1:n--> 作品
用户 --1:n--> 评论
作品 --1:n--> 评论
用户 --m:n--> 作品 (通过收藏表)
作品 --m:n--> 标签 (通过作品标签关联表)
2.3.2 作品表结构设计
sql复制CREATE TABLE `work` (
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`content` text NOT NULL,
`cover_url` varchar(255) DEFAULT NULL,
`user_id` bigint NOT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`view_count` int DEFAULT '0',
`status` tinyint DEFAULT '1' COMMENT '1-正常 2-下架',
PRIMARY KEY (`id`),
KEY `idx_user` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
3. 核心功能实现
3.1 作品发布模块
3.1.1 富文本编辑器集成
采用WangEditor作为富文本编辑器,前端集成代码:
javascript复制const E = window.wangEditor
const editor = new E('#editor')
editor.config.uploadImgServer = '/api/upload/image'
editor.config.uploadFileName = 'file'
editor.create()
document.getElementById('submit-btn').addEventListener('click', () => {
const content = editor.txt.html()
// 提交逻辑...
})
后端图片上传接口:
java复制@PostMapping("/upload/image")
public Result uploadImage(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return Result.fail("请选择文件");
}
try {
String fileName = FileUtil.upload(file);
return Result.success("/uploads/" + fileName);
} catch (IOException e) {
log.error("文件上传失败", e);
return Result.fail("上传失败");
}
}
3.1.2 作品标签处理
作品发布时支持多标签选择,后端处理逻辑:
java复制public void createWork(Work work, List<Long> tagIds) {
workMapper.insert(work);
if (!CollectionUtils.isEmpty(tagIds)) {
tagIds.forEach(tagId -> {
WorkTag workTag = new WorkTag();
workTag.setWorkId(work.getId());
workTag.setTagId(tagId);
workTagMapper.insert(workTag);
});
}
}
3.2 互动功能实现
3.2.1 评论系统设计
采用多级评论结构:
java复制public class Comment {
private Long id;
private String content;
private Long userId;
private Long workId;
private Long parentId; // 父评论ID
private Long replyTo; // 回复的用户ID
private Date createTime;
// getters/setters...
}
评论列表查询SQL:
xml复制<select id="selectByWorkId" resultMap="CommentDTO">
SELECT c.*, u.username, u.avatar,
ru.username as reply_username
FROM comment c
LEFT JOIN user u ON c.user_id = u.id
LEFT JOIN user ru ON c.reply_to = ru.id
WHERE c.work_id = #{workId}
ORDER BY c.create_time DESC
</select>
3.2.2 收藏与点赞
使用Redis优化高频操作:
java复制@Service
public class LikeService {
private final RedisTemplate<String, String> redisTemplate;
public void likeWork(Long userId, Long workId) {
String key = "work:like:" + workId;
redisTemplate.opsForSet().add(key, userId.toString());
}
public void cancelLike(Long userId, Long workId) {
String key = "work:like:" + workId;
redisTemplate.opsForSet().remove(key, userId.toString());
}
public boolean isLiked(Long userId, Long workId) {
String key = "work:like:" + workId;
return redisTemplate.opsForSet().isMember(key, userId.toString());
}
}
4. 系统安全与性能优化
4.1 安全防护措施
4.1.1 XSS防护
使用Jsoup进行HTML内容过滤:
java复制public String filterHtml(String html) {
if (StringUtils.isEmpty(html)) {
return html;
}
Whitelist whitelist = Whitelist.basicWithImages()
.addAttributes("div", "class")
.addProtocols("img", "src", "http", "https", "data");
return Jsoup.clean(html, whitelist);
}
4.1.2 权限控制
基于Spring Security的权限注解:
java复制@PreAuthorize("hasRole('ADMIN') or #work.userId == authentication.principal.id")
@PostMapping("/work/{id}/delete")
public Result deleteWork(@PathVariable Long id, @ModelAttribute Work work) {
workService.deleteWork(id);
return Result.success();
}
4.2 性能优化实践
4.2.1 缓存策略
作品详情使用多级缓存:
java复制@Cacheable(value = "work", key = "#id", unless = "#result == null")
public WorkDTO getWorkDetail(Long id) {
Work work = workMapper.selectById(id);
if (work == null) {
return null;
}
WorkDTO dto = new WorkDTO();
BeanUtils.copyProperties(work, dto);
// 查询附加信息
dto.setTags(tagMapper.selectByWorkId(id));
dto.setLikeCount(redisTemplate.opsForSet().size("work:like:" + id));
return dto;
}
4.2.2 分页查询优化
使用MyBatis-Plus的分页插件:
java复制@GetMapping("/works")
public PageResult<WorkVO> getWorks(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size) {
Page<Work> pageInfo = new Page<>(page, size);
IPage<Work> workPage = workMapper.selectPage(pageInfo, null);
List<WorkVO> vos = workPage.getRecords().stream()
.map(this::convertToVO)
.collect(Collectors.toList());
return new PageResult<>(vos, workPage.getTotal());
}
5. 部署与运维
5.1 生产环境部署
推荐使用Docker Compose部署:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_DATABASE: fanwork_db
volumes:
- mysql_data:/var/lib/mysql
ports:
- "3306:3306"
redis:
image: redis:6
ports:
- "6379:6379"
volumes:
- redis_data:/data
app:
build: .
depends_on:
- mysql
- redis
ports:
- "8080:8080"
environment:
SPRING_PROFILES_ACTIVE: prod
DB_URL: jdbc:mysql://mysql:3306/fanwork_db
DB_USER: root
DB_PASSWORD: ${DB_PASSWORD}
REDIS_HOST: redis
volumes:
mysql_data:
redis_data:
5.2 监控与日志
集成Spring Boot Actuator进行健康监控:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
日志收集建议使用ELK栈:
xml复制<!-- logback-spring.xml -->
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>${LOGSTASH_HOST:-localhost}:5000</destination>
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
6. 项目扩展方向
- 内容推荐系统:基于用户行为数据实现个性化推荐
java复制public List<Work> recommendWorks(Long userId) {
// 1. 获取用户标签偏好
// 2. 查找相似用户
// 3. 混合推荐
}
- 创作激励体系:引入虚拟货币和打赏功能
sql复制CREATE TABLE `reward` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`from_user` BIGINT NOT NULL,
`to_user` BIGINT NOT NULL,
`work_id` BIGINT DEFAULT NULL,
`amount` DECIMAL(10,2) NOT NULL,
`message` VARCHAR(255) DEFAULT NULL,
`create_time` DATETIME NOT NULL,
PRIMARY KEY (`id`)
);
-
移动端适配:开发配套的React Native或Flutter应用
-
AI辅助创作:集成NLP模型提供写作建议
在实际开发中,我遇到的一个典型问题是MyBatis关联查询的N+1问题。解决方案是:
- 使用
<collection>标签进行嵌套结果映射 - 对于复杂关联,先查询主表再批量查询关联表
- 必要时使用MyBatis-Plus的
@TableField(exist=false)注解
另一个经验是:对于内容平台,一定要在早期设计好审核流程。我们最终实现的方案是:
- 自动过滤敏感词
- 重要操作二次确认
- 管理员后台审核队列
- 用户举报机制
