From 1491cfc330ac67112f39439e65e6150197f9f2a1 Mon Sep 17 00:00:00 2001 From: ikmkj <1@qq,com> Date: Thu, 31 Jul 2025 23:09:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(recycle-bin):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E5=9B=9E=E6=94=B6=E7=AB=99=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在数据库中添加逻辑删除字段和相关索引- 新增回收站相关实体类和接口 - 实现回收站列表查询、项目恢复、永久删除和清空回收站等功能 - 前端集成回收站接口,支持回收站页面操作 --- .../bijihoudaun/config/MybatisPlusConfig.java | 4 +- .../controller/TrashController.java | 47 +++++++++ .../com/test/bijihoudaun/entity/Grouping.java | 16 ++- .../test/bijihoudaun/entity/MarkdownFile.java | 14 ++- .../test/bijihoudaun/entity/TrashItemVo.java | 26 +++++ .../bijihoudaun/service/TrashService.java | 33 ++++++ .../service/impl/TrashServiceImpl.java | 99 ++++++++++++++++++ .../src/main/resources/application-dev.yml | 4 +- .../src/main/resources/application.yml | 5 + biji-qianduan/src/api/CommonApi.js | 4 +- biji-qianduan/src/components/TrashPage.vue | 4 +- mydatabase.db | Bin 49152 -> 53248 bytes sql/data.sql | 8 +- 13 files changed, 254 insertions(+), 10 deletions(-) create mode 100644 biji-houdaun/src/main/java/com/test/bijihoudaun/controller/TrashController.java create mode 100644 biji-houdaun/src/main/java/com/test/bijihoudaun/entity/TrashItemVo.java create mode 100644 biji-houdaun/src/main/java/com/test/bijihoudaun/service/TrashService.java create mode 100644 biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/TrashServiceImpl.java diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/config/MybatisPlusConfig.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/config/MybatisPlusConfig.java index 3e50436..a55a001 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/config/MybatisPlusConfig.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/config/MybatisPlusConfig.java @@ -3,6 +3,7 @@ package com.test.bijihoudaun.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -10,11 +11,12 @@ import org.springframework.context.annotation.Configuration; public class MybatisPlusConfig { /** - * 添加分页插件 + * 添加分页插件和逻辑删除插件 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.SQLITE)); return interceptor; } 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/Grouping.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/Grouping.java index eb464e7..f3260ff 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/Grouping.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/Grouping.java @@ -3,15 +3,19 @@ package com.test.bijihoudaun.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.io.Serializable; +import java.util.Date; + @Data @Schema(name = "分组实体") @TableName("grouping") -public class Grouping { +public class Grouping implements Serializable { @Schema(description = "分组id",implementation = Long.class) @TableId(type = IdType.ASSIGN_ID) @JsonFormat(shape = JsonFormat.Shape.STRING) // 仅作用于此字段 @@ -24,4 +28,14 @@ public class Grouping { @Schema(description = "分组名称",implementation = String.class) private String grouping; + + @Schema(description = "是否删除 0-未删除 1-已删除", implementation = Integer.class) + @TableLogic + private Integer isDeleted; + + @Schema(description = "删除时间", implementation = Date.class) + private Date deletedAt; + + @Schema(description = "删除人ID", implementation = Long.class) + private Long deletedBy; } diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/MarkdownFile.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/MarkdownFile.java index aa78aad..6c6cfd6 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/MarkdownFile.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/MarkdownFile.java @@ -3,17 +3,19 @@ package com.test.bijihoudaun.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.io.Serializable; import java.util.Date; @Data @Schema(name = "文本实体") @TableName("markdown_file") -public class MarkdownFile { +public class MarkdownFile implements Serializable { @Schema(description = "文本id",implementation = Long.class) @TableId(type = IdType.AUTO) @JsonFormat(shape = JsonFormat.Shape.STRING) // 仅作用于此字段 @@ -33,4 +35,14 @@ public class MarkdownFile { private Date createdAt; @Schema(description = "更新时间",implementation = Date.class) private Date updatedAt; + + @Schema(description = "是否删除 0-未删除 1-已删除", implementation = Integer.class) + @TableLogic + private Integer isDeleted; + + @Schema(description = "删除时间", implementation = Date.class) + private Date deletedAt; + + @Schema(description = "删除人ID", implementation = Long.class) + private Long deletedBy; } 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..4e8368e --- /dev/null +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/entity/TrashItemVo.java @@ -0,0 +1,26 @@ +package com.test.bijihoudaun.entity; + +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 name; + + @Schema(description = "项目类型(note 或 group)") + private String type; + + @Schema(description = "删除时间") + 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..1432fc1 --- /dev/null +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/TrashServiceImpl.java @@ -0,0 +1,99 @@ +package com.test.bijihoudaun.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +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() { + // 查询已删除的笔记 + List deletedNotes = markdownFileMapper.selectList(new QueryWrapper().eq("is_deleted", 1)) + .stream() + .map(file -> { + TrashItemVo vo = new TrashItemVo(); + vo.setId(String.valueOf(file.getId())); + vo.setName(file.getTitle()); + vo.setType("note"); + vo.setDeletedAt(file.getDeletedAt()); + vo.setDeletedBy(String.valueOf(file.getDeletedBy())); + return vo; + }) + .collect(Collectors.toList()); + + // 查询已删除的分组 + List deletedGroups = groupingMapper.selectList(new QueryWrapper().eq("is_deleted", 1)) + .stream() + .map(group -> { + TrashItemVo vo = new TrashItemVo(); + vo.setId(String.valueOf(group.getId())); + vo.setName(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)) { + MarkdownFile file = new MarkdownFile(); + file.setId(Long.parseLong(id)); + file.setIsDeleted(0); + file.setDeletedAt(null); + file.setDeletedBy(null); + markdownFileMapper.updateById(file); + } else if ("group".equals(type)) { + Grouping group = new Grouping(); + group.setId(Long.parseLong(id)); + group.setIsDeleted(0); + group.setDeletedAt(null); + group.setDeletedBy(null); + groupingMapper.updateById(group); + } + } + + @Override + @Transactional + public void permanentlyDeleteItem(String id, String type) { + if ("note".equals(type)) { + markdownFileMapper.deleteById(Long.parseLong(id)); + } else if ("group".equals(type)) { + // 删除分组时,也删除其下的所有笔记 + groupingMapper.deleteById(Long.parseLong(id)); + markdownFileMapper.delete(new QueryWrapper().eq("grouping_id", 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-houdaun/src/main/resources/application-dev.yml b/biji-houdaun/src/main/resources/application-dev.yml index 9f0f341..16c8b63 100644 --- a/biji-houdaun/src/main/resources/application-dev.yml +++ b/biji-houdaun/src/main/resources/application-dev.yml @@ -1,8 +1,8 @@ spring: datasource: driver-class-name: org.sqlite.JDBC -# url: jdbc:sqlite:C:\it\houtaigunli\biji\mydatabase.db - url: jdbc:sqlite:C:\KAIFA\2\mydatabase.db + url: jdbc:sqlite:C:\it\houtaigunli\biji\mydatabase.db +# url: jdbc:sqlite:C:\KAIFA\2\mydatabase.db jpa: hibernate: ddl-auto: none diff --git a/biji-houdaun/src/main/resources/application.yml b/biji-houdaun/src/main/resources/application.yml index e85a973..0d94671 100644 --- a/biji-houdaun/src/main/resources/application.yml +++ b/biji-houdaun/src/main/resources/application.yml @@ -29,6 +29,11 @@ mybatis-plus: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true + global-config: + db-config: + logic-delete-field: isDeleted # 全局逻辑删除的实体字段名 + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) # JWT 配置 jwt: diff --git a/biji-qianduan/src/api/CommonApi.js b/biji-qianduan/src/api/CommonApi.js index cf4d372..f4eaf91 100644 --- a/biji-qianduan/src/api/CommonApi.js +++ b/biji-qianduan/src/api/CommonApi.js @@ -120,10 +120,10 @@ export const MD5 = (data, file) => { export const getTrash = () => axiosApi.get('/api/trash'); // 恢复项目 -export const restoreTrashItem = (id) => axiosApi.post(`/api/trash/restore/${id}`); +export const restoreTrashItem = (id, type) => axiosApi.post(`/api/trash/restore/${type}/${id}`); // 彻底删除 -export const permanentlyDeleteItem = (id) => axiosApi.delete(`/api/trash/permanently/${id}`); +export const permanentlyDeleteItem = (id, type) => axiosApi.delete(`/api/trash/permanently/${type}/${id}`); // 清空回收站 export const cleanTrash = () => axiosApi.delete('/api/trash/clean'); diff --git a/biji-qianduan/src/components/TrashPage.vue b/biji-qianduan/src/components/TrashPage.vue index 268ed4c..c7d43d4 100644 --- a/biji-qianduan/src/components/TrashPage.vue +++ b/biji-qianduan/src/components/TrashPage.vue @@ -48,7 +48,7 @@ const fetchTrashItems = async () => { const handleRestore = async (item) => { try { - await restoreTrashItem(item.id); + await restoreTrashItem(item.id, item.type); ElMessage.success('恢复成功'); fetchTrashItems(); } catch (error) { @@ -63,7 +63,7 @@ const handleDeletePermanently = async (item) => { type: 'warning', }); try { - await permanentlyDeleteItem(item.id); + await permanentlyDeleteItem(item.id, item.type); ElMessage.success('已永久删除'); fetchTrashItems(); } catch (error) { diff --git a/mydatabase.db b/mydatabase.db index 2b7c05d2ffd0ea25e145be09b2a220b723bb69cb..99eb9391f92f44de0011766fb49c15d0eb75e782 100644 GIT binary patch delta 685 zcmZo@U~X8zJV9D;3IhWJFA#GBF*6VwPSi1$ox-3OoWRR|l!2X%gMoK7pD}+I_XS=< z&XsH&Y#bXKdsw-et;E^I)zul>j5lX;L@`e8=VS}dERIh}%}FguO;J+t^b2uycMVc- zadmSH^$Afh(Bb7$P*8%($0wF3DY!U>xQ2N8x}wS?Rl+s%a%pm{WPGx*@c|=OW33pw zxU4K=v+v|uu5J${D%myp7ndp%qx!~1WyZ}HxJ^VjnAoHk*y7lvHa7CHiCQ(SUvhZG zhn5Mz&|#EURcB@8U|^VRw9Ph6T+Hfhxrs&DDf#7j@oAYksVsX{Ol6n^7+8U_ zjQpR0BvAY3&5RRX@XPQ3?b-0S;&Y^NqXtSlJmQ8G#~C*DiZGeH}9|3y=?1F|GaCj6Pmw zDEImDiO;$kHYKn-urdLy;@vE;-~<1}0G3GtEIcd>It=_9`74<|GU!Znlo4+Q1`O0$ zK<~?|cBw+V->9&jNvswqH+|6?NYKcuZq$az@jDmti0U;hobh2_cgqAuZU!c4X=zA+ fdMPN2y250DLCq;C8OX}Y2@L9E`c{h>CkOxloBO~{ delta 663 zcmb7COK4L;6utBQ?1(KI)T*siv2ml-Z8zdhgtXWyrB<<_2(FUY7)d1L z`LyXm@PmJeX`OZ@8fYP#P{^hWL2w~WD|As%TzjKLH*TEa%!k9=x#ymnc*rLbd?DaY z1Ar6ZKyV1t+TRyU!@)B{x>^NUiGlulTOZ9j>iQuirp2{5bGyFCT580QeUS)B%gm6@ z5?R;wI)YHLk}tJn#3TEHL6QpDOwHMMH6I7eN|ssOaoFn=pdaW0^z%J1VI}t%-Kj^j zTOV@M1)=~K2n0mY&R06U>7%LggSqdu=>n)g%y2d=itN-}ne$vnqWE~TMa*|2p|Gn? z<76umD4_cNr!i8sa?GXE67bXpTBEDfrZ#i;-k|L}6+R+O3{Q@aT)B2Rc46$&sPKOG zkv7f+^fRJcJfO*XBR4MzT)i}{`^1X9O%T)`9G!6=>~|Dar6_d(`Cx&kLE;$d3w%h=J|K% znTrCLO5^^kwOcpW=ifO*0sLgG^z3W#4X+7!Q5rLu#%x~Wo9FfCRwJ9h=T5)cSj=sd z?*Yh}F#$YEG#Y(7B%ja;vRVs_6gKG{m!NP`2S~$G2eye