diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/TrashController.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/TrashController.java new file mode 100644 index 0000000..5e53222 --- /dev/null +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/TrashController.java @@ -0,0 +1,47 @@ +package com.test.bijihoudaun.controller; + +import com.test.bijihoudaun.common.response.R; +import com.test.bijihoudaun.entity.TrashItemVo; +import com.test.bijihoudaun.service.TrashService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/trash") +@Tag(name = "回收站管理") +public class TrashController { + + @Autowired + private TrashService trashService; + + @GetMapping + @Operation(summary = "获取回收站列表") + public R> getTrashItems() { + return R.success(trashService.getTrashItems()); + } + + @PostMapping("/restore/{type}/{id}") + @Operation(summary = "恢复项目") + public R restoreItem(@PathVariable String type, @PathVariable String id) { + trashService.restoreItem(id, type); + return R.success(); + } + + @DeleteMapping("/permanently/{type}/{id}") + @Operation(summary = "永久删除项目") + public R permanentlyDeleteItem(@PathVariable String type, @PathVariable String id) { + trashService.permanentlyDeleteItem(id, type); + return R.success(); + } + + @DeleteMapping("/clean") + @Operation(summary = "清空回收站") + public R cleanTrash() { + trashService.cleanTrash(); + return R.success(); + } +} \ No newline at end of file diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/TrashItemVo.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/TrashItemVo.java new file mode 100644 index 0000000..425f408 --- /dev/null +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/TrashItemVo.java @@ -0,0 +1,28 @@ +package com.test.bijihoudaun.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; + +@Data +@Schema(name = "回收站项目视图对象") +public class TrashItemVo { + + @Schema(description = "项目ID") + private String id; + + @Schema(description = "项目名称(笔记标题或分组名称)") + private String title; + + @Schema(description = "项目类型(note 或 group)") + private String type; + + @Schema(description = "删除时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date deletedAt; + + @Schema(description = "删除者ID") + private String deletedBy; +} \ No newline at end of file diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/service/TrashService.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/TrashService.java new file mode 100644 index 0000000..8fc004c --- /dev/null +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/TrashService.java @@ -0,0 +1,33 @@ +package com.test.bijihoudaun.service; + +import com.test.bijihoudaun.entity.TrashItemVo; + +import java.util.List; + +public interface TrashService { + + /** + * 获取回收站中的所有项目 + * @return 回收站项目列表 + */ + List getTrashItems(); + + /** + * 恢复指定的回收站项目 + * @param id 项目ID + * @param type 项目类型 ("note" 或 "group") + */ + void restoreItem(String id, String type); + + /** + * 永久删除指定的回收站项目 + * @param id 项目ID + * @param type 项目类型 ("note" 或 "group") + */ + void permanentlyDeleteItem(String id, String type); + + /** + * 清空回收站 + */ + void cleanTrash(); +} \ No newline at end of file diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/TrashServiceImpl.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/TrashServiceImpl.java new file mode 100644 index 0000000..2abb930 --- /dev/null +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/TrashServiceImpl.java @@ -0,0 +1,90 @@ +package com.test.bijihoudaun.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.test.bijihoudaun.entity.Grouping; +import com.test.bijihoudaun.entity.MarkdownFile; +import com.test.bijihoudaun.entity.TrashItemVo; +import com.test.bijihoudaun.mapper.GroupingMapper; +import com.test.bijihoudaun.mapper.MarkdownFileMapper; +import com.test.bijihoudaun.service.TrashService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Service +public class TrashServiceImpl implements TrashService { + + @Autowired + private MarkdownFileMapper markdownFileMapper; + + @Autowired + private GroupingMapper groupingMapper; + + @Override + public List getTrashItems() { + // 调用自定义的Mapper方法查询已删除的笔记,绕过全局逻辑删除过滤器 + List deletedNotes = markdownFileMapper.selectDeleted() + .stream() + .map(file -> { + TrashItemVo vo = new TrashItemVo(); + vo.setId(String.valueOf(file.getId())); + vo.setTitle(file.getTitle()); + vo.setType("note"); + vo.setDeletedAt(file.getDeletedAt()); + vo.setDeletedBy(String.valueOf(file.getDeletedBy())); + return vo; + }) + .collect(Collectors.toList()); + + // 调用自定义的Mapper方法查询已删除的分组 + List deletedGroups = groupingMapper.selectDeleted() + .stream() + .map(group -> { + TrashItemVo vo = new TrashItemVo(); + vo.setId(String.valueOf(group.getId())); + vo.setTitle(group.getGrouping()); + vo.setType("group"); + vo.setDeletedAt(group.getDeletedAt()); + vo.setDeletedBy(String.valueOf(group.getDeletedBy())); + return vo; + }) + .collect(Collectors.toList()); + + // 合并并返回 + return Stream.concat(deletedNotes.stream(), deletedGroups.stream()).collect(Collectors.toList()); + } + + @Override + @Transactional + public void restoreItem(String id, String type) { + if ("note".equals(type)) { + markdownFileMapper.restoreById(Long.parseLong(id)); + } else if ("group".equals(type)) { + groupingMapper.restoreById(Long.parseLong(id)); + } + } + + @Override + @Transactional + public void permanentlyDeleteItem(String id, String type) { + if ("note".equals(type)) { + markdownFileMapper.physicalDeleteById(Long.parseLong(id)); + } else if ("group".equals(type)) { + // 永久删除分组时,也永久删除其下的所有笔记 + groupingMapper.physicalDeleteById(Long.parseLong(id)); + markdownFileMapper.physicalDeleteByGroupingId(Long.parseLong(id)); + } + } + + @Override + @Transactional + public void cleanTrash() { + markdownFileMapper.delete(new QueryWrapper().eq("is_deleted", 1)); + groupingMapper.delete(new QueryWrapper().eq("is_deleted", 1)); + } +} \ No newline at end of file diff --git a/biji-qianduan/src/components/TrashPage.vue b/biji-qianduan/src/components/TrashPage.vue new file mode 100644 index 0000000..c7d43d4 --- /dev/null +++ b/biji-qianduan/src/components/TrashPage.vue @@ -0,0 +1,104 @@ + + + + + \ No newline at end of file diff --git a/回收站功能设计.md b/回收站功能设计.md new file mode 100644 index 0000000..65a66e8 --- /dev/null +++ b/回收站功能设计.md @@ -0,0 +1,22 @@ +# 回收站功能设计方案 + +## 功能需求 +- 实现笔记和分类的软删除功能 +- 提供30天数据保留期 +- 支持恢复和永久删除操作 + +## 前端修改 +1. 侧边栏添加回收站入口 +2. 删除操作改为"移至回收站" +3. 新建TrashPage.vue组件 + +## 后端修改 +```sql +ALTER TABLE markdown ADD COLUMN is_deleted BOOLEAN DEFAULT false; +ALTER TABLE markdown ADD COLUMN deleted_at TIMESTAMP; +``` + +## API接口 +- GET /api/trash - 获取回收站内容 +- POST /api/trash/restore - 恢复项目 +- DELETE /api/trash/clean - 清空回收站 \ No newline at end of file