refactor: 优化图片处理与数据库连接配置
This commit is contained in:
@@ -6,6 +6,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
@@ -14,10 +15,20 @@ public class AsyncConfig {
|
||||
@Bean("imageNameSyncExecutor")
|
||||
public Executor imageNameSyncExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(5);
|
||||
executor.setMaxPoolSize(10);
|
||||
// 核心线程数
|
||||
executor.setCorePoolSize(2);
|
||||
// 最大线程数
|
||||
executor.setMaxPoolSize(5);
|
||||
// 队列容量
|
||||
executor.setQueueCapacity(100);
|
||||
// 线程名前缀
|
||||
executor.setThreadNamePrefix("imageNameSync-");
|
||||
// 拒绝策略:由调用线程处理
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
// 等待所有任务完成后再关闭线程池
|
||||
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||
// 等待时间(秒)
|
||||
executor.setAwaitTerminationSeconds(60);
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
@@ -18,8 +18,7 @@ import java.util.Date;
|
||||
public class Grouping implements Serializable {
|
||||
@Schema(description = "分组id",implementation = Long.class)
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING) // 仅作用于此字段
|
||||
@TableField("id")
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||
private Long id;
|
||||
|
||||
@Schema(description ="上级id",implementation = Long.class)
|
||||
|
||||
@@ -16,8 +16,7 @@ import java.util.Date;
|
||||
public class Image {
|
||||
@Schema(description = "图片id",implementation = Long.class)
|
||||
@TableId(type = IdType.AUTO)
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING) // 仅作用于此字段
|
||||
@TableField("id")
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = " 外键,关联Markdown文件ID,标识图片所属文档",implementation = Long.class )
|
||||
|
||||
@@ -12,7 +12,6 @@ public class ImageName {
|
||||
@Schema(description = "图片名称id", implementation = Long.class)
|
||||
@TableId(type = IdType.AUTO)
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||
@TableField("id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "关联的Markdown文件ID", implementation = Long.class)
|
||||
|
||||
@@ -18,8 +18,7 @@ import java.util.Date;
|
||||
public class MarkdownFile implements Serializable {
|
||||
@Schema(description = "文本id",implementation = Long.class)
|
||||
@TableId(type = IdType.AUTO)
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING) // 仅作用于此字段
|
||||
@TableField("id")
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "分组表id",implementation = Long.class)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.test.bijihoudaun.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@@ -11,5 +12,6 @@ public class MarkdownFileVO extends MarkdownFile {
|
||||
private String groupingName;
|
||||
|
||||
@TableField(exist = false)
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||
private Long groupingId;
|
||||
}
|
||||
@@ -18,7 +18,6 @@ public class RegistrationCode implements Serializable {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
@Schema(description = "主键ID", name = "id")
|
||||
@TableField("id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "注册码", name = "code")
|
||||
|
||||
@@ -17,7 +17,6 @@ public class SystemSetting implements Serializable {
|
||||
|
||||
@TableId
|
||||
@Schema(description = "设置键", name = "settingKey")
|
||||
@TableField("`setting_key`")
|
||||
private String settingKey;
|
||||
|
||||
@Schema(description = "设置值", name = "settingValue")
|
||||
|
||||
@@ -16,8 +16,7 @@ import java.util.Date;
|
||||
public class User {
|
||||
@Schema(description = "用户id",implementation = Long.class)
|
||||
@TableId(type = IdType.AUTO)
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING) // 仅作用于此字段
|
||||
@TableField("id")
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户名",implementation = String.class)
|
||||
|
||||
@@ -15,7 +15,10 @@ import org.apache.ibatis.annotations.Update;
|
||||
@Mapper
|
||||
public interface MarkdownFileMapper extends BaseMapper<MarkdownFile> {
|
||||
|
||||
@Select("SELECT mf.id, mf.grouping_id as groupingId, mf.`title`, mf.file_name, mf.`content`, mf.created_at, mf.updated_at, mf.is_deleted, mf.deleted_at, mf.deleted_by, mf.is_private, g.`grouping` as groupingName " +
|
||||
/**
|
||||
* 查询最近更新的笔记列表(不包含content大字段,提高性能)
|
||||
*/
|
||||
@Select("SELECT mf.id, mf.grouping_id as groupingId, mf.`title`, mf.file_name, null as `content`, mf.created_at, mf.updated_at, mf.is_deleted, mf.deleted_at, mf.deleted_by, mf.is_private, g.`grouping` as groupingName " +
|
||||
"FROM `markdown_file` mf " +
|
||||
"LEFT JOIN `grouping` g ON mf.grouping_id = g.id " +
|
||||
"WHERE mf.is_deleted = 0 " +
|
||||
@@ -23,14 +26,20 @@ public interface MarkdownFileMapper extends BaseMapper<MarkdownFile> {
|
||||
"LIMIT #{limit}")
|
||||
List<MarkdownFileVO> selectRecentWithGrouping(@Param("limit") int limit);
|
||||
|
||||
@Select("SELECT mf.id, mf.grouping_id as groupingId, mf.`title`, mf.file_name, mf.`content`, mf.created_at, mf.updated_at, mf.is_deleted, mf.deleted_at, mf.deleted_by, mf.is_private, g.`grouping` as groupingName " +
|
||||
/**
|
||||
* 根据分类ID查询笔记列表(不包含content大字段,提高性能)
|
||||
*/
|
||||
@Select("SELECT mf.id, mf.grouping_id as groupingId, mf.`title`, mf.file_name, null as `content`, mf.created_at, mf.updated_at, mf.is_deleted, mf.deleted_at, mf.deleted_by, mf.is_private, g.`grouping` as groupingName " +
|
||||
"FROM `markdown_file` mf " +
|
||||
"LEFT JOIN `grouping` g ON mf.grouping_id = g.id " +
|
||||
"WHERE mf.grouping_id = #{groupingId} AND mf.is_deleted = 0 " +
|
||||
"ORDER BY mf.updated_at DESC")
|
||||
List<MarkdownFileVO> selectByGroupingIdWithGrouping(@Param("groupingId") String groupingId);
|
||||
|
||||
@Select("SELECT id, grouping_id, `title`, file_name, `content`, created_at, updated_at, is_deleted, deleted_at, deleted_by, is_private FROM `markdown_file` WHERE is_deleted = 1")
|
||||
/**
|
||||
* 查询已删除的笔记(不包含content大字段)
|
||||
*/
|
||||
@Select("SELECT id, grouping_id, `title`, file_name, null as `content`, created_at, updated_at, is_deleted, deleted_at, deleted_by, is_private FROM `markdown_file` WHERE is_deleted = 1")
|
||||
List<MarkdownFile> selectDeleted();
|
||||
|
||||
@Delete("DELETE FROM `markdown_file` WHERE id = #{id}")
|
||||
@@ -48,8 +57,15 @@ public interface MarkdownFileMapper extends BaseMapper<MarkdownFile> {
|
||||
* 获取所有笔记ID
|
||||
* @return 所有笔记ID列表
|
||||
*/
|
||||
@Select("SELECT id, grouping_id, `title`, file_name, `content`, created_at, updated_at, is_deleted, deleted_at, deleted_by, is_private FROM `markdown_file` WHERE is_deleted = 0")
|
||||
List<Integer> findAllIds();
|
||||
@Select("SELECT id FROM `markdown_file` WHERE is_deleted = 0")
|
||||
List<Long> findAllIds();
|
||||
|
||||
/**
|
||||
* 获取所有笔记的ID和content(用于图片清理任务)
|
||||
* @return 所有笔记的ID和content
|
||||
*/
|
||||
@Select("SELECT id, `content` FROM `markdown_file` WHERE is_deleted = 0")
|
||||
List<MarkdownFile> selectAllIdsAndContent();
|
||||
|
||||
@Select("SELECT mf.id, mf.grouping_id as groupingId, mf.`title`, mf.file_name, mf.`content`, mf.created_at, mf.updated_at, mf.is_deleted, mf.deleted_at, mf.deleted_by, mf.is_private, g.`grouping` as groupingName " +
|
||||
"FROM `markdown_file` mf " +
|
||||
|
||||
@@ -39,17 +39,15 @@ public class ImageCleanupService {
|
||||
*/
|
||||
@Transactional
|
||||
public int cleanupRedundantImages() {
|
||||
// 获取所有笔记ID
|
||||
List<Integer> allMarkdownIds = markdownFileMapper.findAllIds();
|
||||
// 批量获取所有笔记的ID和content(避免N+1查询)
|
||||
List<MarkdownFile> allMarkdownFiles = markdownFileMapper.selectAllIdsAndContent();
|
||||
|
||||
// 用于存储所有被引用的图片文件名
|
||||
Set<String> referencedImageNames = new HashSet<>();
|
||||
|
||||
// 遍历所有笔记,收集被引用的图片
|
||||
for (Integer markdownId : allMarkdownIds) {
|
||||
MarkdownFile markdownFile = markdownFileMapper.selectById(markdownId);
|
||||
for (MarkdownFile markdownFile : allMarkdownFiles) {
|
||||
String content = (markdownFile != null) ? markdownFile.getContent() : "";
|
||||
|
||||
List<String> imageNames = MarkdownImageExtractor.extractImageFilenames(content);
|
||||
referencedImageNames.addAll(imageNames);
|
||||
}
|
||||
|
||||
@@ -132,12 +132,25 @@ public class ImageServiceImpl
|
||||
if (CollUtil.isEmpty(urls)) {
|
||||
return false;
|
||||
}
|
||||
for (String url : urls) {
|
||||
Image image = imageMapper.selectOne(new QueryWrapper<Image>().eq("url", url));
|
||||
if (ObjectUtil.isNotNull(image)) {
|
||||
this.deleteImageByUrl(url);
|
||||
// 批量查询,避免N+1问题
|
||||
List<Image> images = imageMapper.selectList(new QueryWrapper<Image>().in("url", urls));
|
||||
if (CollUtil.isEmpty(images)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 批量删除文件和数据库记录
|
||||
for (Image image : images) {
|
||||
try {
|
||||
Path filePath = Paths.get(uploadDir, image.getStoredName());
|
||||
Files.deleteIfExists(filePath);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("删除图片文件失败: " + image.getStoredName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量删除数据库记录
|
||||
List<Long> ids = images.stream().map(Image::getId).toList();
|
||||
this.removeByIds(ids);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,8 +165,8 @@ public class MarkdownFileServiceImpl
|
||||
imageName.setMarkdownId(markdownId);
|
||||
return imageName;
|
||||
}).toList();
|
||||
// 批量插入新的文件名
|
||||
list.forEach(imageName -> imageNameMapper.insert(imageName));
|
||||
// 批量插入新的文件名(使用MyBatis Plus批量插入)
|
||||
imageNameMapper.insert(list);
|
||||
}
|
||||
} else {
|
||||
// 数据库中已有记录,需要对比处理
|
||||
|
||||
Reference in New Issue
Block a user