feat(note): 新增笔记可见性与置顶功能

- 添加笔记仅对自己可见接口及实现逻辑
- 添加笔记置顶/取消置顶接口及实现逻辑
- 引入新的请求参数类 UpdateNoteVisibleOnlyMeReqVO 和 TopNoteReqVO
- 扩展响应码枚举以支持新功能的异常处理
- 使用通配符导入优化代码结构
- 更新服务层接口定义,增加对应方法声明
This commit is contained in:
Hanserwei
2025-10-10 18:57:24 +08:00
parent 5b19e715ce
commit cede5282e8
6 changed files with 128 additions and 18 deletions

View File

@@ -2,10 +2,7 @@ package com.hanserwei.hannote.note.biz.controller;
import com.hanserwei.framework.biz.operationlog.aspect.ApiOperationLog; import com.hanserwei.framework.biz.operationlog.aspect.ApiOperationLog;
import com.hanserwei.framework.common.response.Response; import com.hanserwei.framework.common.response.Response;
import com.hanserwei.hannote.note.biz.model.vo.FindNoteDetailReqVO; import com.hanserwei.hannote.note.biz.model.vo.*;
import com.hanserwei.hannote.note.biz.model.vo.FindNoteDetailRspVO;
import com.hanserwei.hannote.note.biz.model.vo.PublishNoteReqVO;
import com.hanserwei.hannote.note.biz.model.vo.UpdateNoteReqVO;
import com.hanserwei.hannote.note.biz.service.NoteService; import com.hanserwei.hannote.note.biz.service.NoteService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -41,4 +38,16 @@ public class NoteController {
return noteService.updateNote(updateNoteReqVO); return noteService.updateNote(updateNoteReqVO);
} }
@PostMapping(value = "/visible/onlyme")
@ApiOperationLog(description = "笔记仅对自己可见")
public Response<?> visibleOnlyMe(@Validated @RequestBody UpdateNoteVisibleOnlyMeReqVO updateNoteVisibleOnlyMeReqVO) {
return noteService.visibleOnlyMe(updateNoteVisibleOnlyMeReqVO);
}
@PostMapping(value = "/top")
@ApiOperationLog(description = "置顶/取消置顶笔记")
public Response<?> topNote(@Validated @RequestBody TopNoteReqVO topNoteReqVO) {
return noteService.topNote(topNoteReqVO);
}
} }

View File

@@ -18,7 +18,9 @@ public enum ResponseCodeEnum implements BaseExceptionInterface {
NOTE_NOT_FOUND("NOTE-20002", "笔记不存在"), NOTE_NOT_FOUND("NOTE-20002", "笔记不存在"),
NOTE_PRIVATE("NOTE-20003", "作者已将该笔记设置为仅自己可见"), NOTE_PRIVATE("NOTE-20003", "作者已将该笔记设置为仅自己可见"),
NOTE_UPDATE_FAIL("NOTE-20004", "笔记更新失败"), NOTE_UPDATE_FAIL("NOTE-20004", "笔记更新失败"),
TOPIC_NOT_FOUND("NOTE-20005", "话题不存在") TOPIC_NOT_FOUND("NOTE-20005", "话题不存在"),
NOTE_CANT_VISIBLE_ONLY_ME("NOTE-20006", "此笔记无法修改为仅自己可见"),
NOTE_CANT_OPERATE("NOTE-20007", "您无法操作该笔记"),
; ;
// 异常码 // 异常码

View File

@@ -0,0 +1,20 @@
package com.hanserwei.hannote.note.biz.model.vo;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class TopNoteReqVO {
@NotNull(message = "笔记 ID 不能为空")
private Long id;
@NotNull(message = "置顶状态不能为空")
private Boolean isTop;
}

View File

@@ -0,0 +1,18 @@
package com.hanserwei.hannote.note.biz.model.vo;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UpdateNoteVisibleOnlyMeReqVO {
@NotNull(message = "笔记 ID 不能为空")
private Long id;
}

View File

@@ -3,10 +3,7 @@ package com.hanserwei.hannote.note.biz.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.hanserwei.framework.common.response.Response; import com.hanserwei.framework.common.response.Response;
import com.hanserwei.hannote.note.biz.domain.dataobject.NoteDO; import com.hanserwei.hannote.note.biz.domain.dataobject.NoteDO;
import com.hanserwei.hannote.note.biz.model.vo.FindNoteDetailReqVO; import com.hanserwei.hannote.note.biz.model.vo.*;
import com.hanserwei.hannote.note.biz.model.vo.FindNoteDetailRspVO;
import com.hanserwei.hannote.note.biz.model.vo.PublishNoteReqVO;
import com.hanserwei.hannote.note.biz.model.vo.UpdateNoteReqVO;
public interface NoteService extends IService<NoteDO> { public interface NoteService extends IService<NoteDO> {
@@ -31,4 +28,18 @@ public interface NoteService extends IService<NoteDO> {
*/ */
Response<?> updateNote(UpdateNoteReqVO updateNoteReqVO); Response<?> updateNote(UpdateNoteReqVO updateNoteReqVO);
/**
* 笔记仅对自己可见
* @param updateNoteVisibleOnlyMeReqVO 笔记仅对自己可见请求
* @return 笔记仅对自己可见结果
*/
Response<?> visibleOnlyMe(UpdateNoteVisibleOnlyMeReqVO updateNoteVisibleOnlyMeReqVO);
/**
* 笔记置顶 / 取消置顶
* @param topNoteReqVO 笔记置顶 / 取消置顶请求
* @return 笔记置顶 / 取消置顶结果
*/
Response<?> topNote(TopNoteReqVO topNoteReqVO);
} }

View File

@@ -20,10 +20,7 @@ import com.hanserwei.hannote.note.biz.enums.NoteStatusEnum;
import com.hanserwei.hannote.note.biz.enums.NoteTypeEnum; import com.hanserwei.hannote.note.biz.enums.NoteTypeEnum;
import com.hanserwei.hannote.note.biz.enums.NoteVisibleEnum; import com.hanserwei.hannote.note.biz.enums.NoteVisibleEnum;
import com.hanserwei.hannote.note.biz.enums.ResponseCodeEnum; import com.hanserwei.hannote.note.biz.enums.ResponseCodeEnum;
import com.hanserwei.hannote.note.biz.model.vo.FindNoteDetailReqVO; import com.hanserwei.hannote.note.biz.model.vo.*;
import com.hanserwei.hannote.note.biz.model.vo.FindNoteDetailRspVO;
import com.hanserwei.hannote.note.biz.model.vo.PublishNoteReqVO;
import com.hanserwei.hannote.note.biz.model.vo.UpdateNoteReqVO;
import com.hanserwei.hannote.note.biz.rpc.DistributedIdGeneratorRpcService; import com.hanserwei.hannote.note.biz.rpc.DistributedIdGeneratorRpcService;
import com.hanserwei.hannote.note.biz.rpc.KeyValueRpcService; import com.hanserwei.hannote.note.biz.rpc.KeyValueRpcService;
import com.hanserwei.hannote.note.biz.rpc.UserRpcService; import com.hanserwei.hannote.note.biz.rpc.UserRpcService;
@@ -312,7 +309,7 @@ public class NoteServiceImpl extends ServiceImpl<NoteDOMapper, NoteDO> implement
NoteTypeEnum noteTypeEnum = NoteTypeEnum.valueOf(type); NoteTypeEnum noteTypeEnum = NoteTypeEnum.valueOf(type);
// 判断笔记类型,如果非图文、视频笔记,则抛出异常 // 判断笔记类型,如果非图文、视频笔记,则抛出异常
if (Objects.isNull(noteTypeEnum)){ if (Objects.isNull(noteTypeEnum)) {
throw new ApiException(ResponseCodeEnum.NOTE_TYPE_ERROR); throw new ApiException(ResponseCodeEnum.NOTE_TYPE_ERROR);
} }
String imgUris = null; String imgUris = null;
@@ -340,14 +337,14 @@ public class NoteServiceImpl extends ServiceImpl<NoteDOMapper, NoteDO> implement
// 话题 // 话题
Long topicId = updateNoteReqVO.getTopicId(); Long topicId = updateNoteReqVO.getTopicId();
String topicName = null; String topicName = null;
if (Objects.nonNull(topicId)){ if (Objects.nonNull(topicId)) {
TopicDO topicDO = topicDOService.getById(topicId); TopicDO topicDO = topicDOService.getById(topicId);
if (Objects.isNull(topicDO)){ if (Objects.isNull(topicDO)) {
throw new ApiException(ResponseCodeEnum.TOPIC_NOT_FOUND); throw new ApiException(ResponseCodeEnum.TOPIC_NOT_FOUND);
} }
topicName = topicDO.getName(); topicName = topicDO.getName();
// 判断提交的话题是否真实存在 // 判断提交的话题是否真实存在
if (StringUtils.isBlank(topicName)){ if (StringUtils.isBlank(topicName)) {
throw new ApiException(ResponseCodeEnum.TOPIC_NOT_FOUND); throw new ApiException(ResponseCodeEnum.TOPIC_NOT_FOUND);
} }
} }
@@ -370,7 +367,7 @@ public class NoteServiceImpl extends ServiceImpl<NoteDOMapper, NoteDO> implement
.videoUri(videoUri) .videoUri(videoUri)
.build(); .build();
boolean updateResult = this.updateById(noteDO); boolean updateResult = this.updateById(noteDO);
if (!updateResult){ if (!updateResult) {
throw new ApiException(ResponseCodeEnum.NOTE_UPDATE_FAIL); throw new ApiException(ResponseCodeEnum.NOTE_UPDATE_FAIL);
} }
@@ -431,6 +428,59 @@ public class NoteServiceImpl extends ServiceImpl<NoteDOMapper, NoteDO> implement
return Response.success(); return Response.success();
} }
@Override
public Response<?> visibleOnlyMe(UpdateNoteVisibleOnlyMeReqVO updateNoteVisibleOnlyMeReqVO) {
// 笔记 ID
Long noteId = updateNoteVisibleOnlyMeReqVO.getId();
// 构建更新的实体类
NoteDO noteDO = NoteDO.builder()
.id(noteId)
.visible(NoteVisibleEnum.PRIVATE.getCode())
.updateTime(LocalDateTime.now())
.build();
// 仅仅更新status为1的数据
boolean updateResult = this.update(noteDO, new LambdaQueryWrapper<>(NoteDO.class).eq(NoteDO::getStatus, NoteStatusEnum.NORMAL.getCode()));
if (!updateResult) {
throw new ApiException(ResponseCodeEnum.NOTE_UPDATE_FAIL);
}
// 删除Redis缓存
String noteDetailRedisKey = RedisKeyConstants.buildNoteDetailKey(noteId);
redisTemplate.delete(noteDetailRedisKey);
// 同步广播模式 MQ将所有实例中的本地缓存都删除掉
rocketMQTemplate.syncSend(MQConstants.TOPIC_DELETE_NOTE_LOCAL_CACHE, noteId);
log.info("====> MQ笔记更新本地缓存发送成功...");
return Response.success();
}
@Override
public Response<?> topNote(TopNoteReqVO topNoteReqVO) {
Long noteId = topNoteReqVO.getId();
boolean isTop = topNoteReqVO.getIsTop();
//当前用户ID
Long currUserId = LoginUserContextHolder.getUserId();
NoteDO noteDO = NoteDO.builder()
.id(noteId)
.isTop(isTop)
.updateTime(LocalDateTime.now())
.creatorId(currUserId) // 只有笔记所有者,才能置顶/取消置顶笔记
.build();
boolean isUpdated = this.update(noteDO, new LambdaQueryWrapper<>(NoteDO.class).eq(NoteDO::getId, noteId).eq(NoteDO::getCreatorId, currUserId));
if (!isUpdated) {
throw new ApiException(ResponseCodeEnum.NOTE_UPDATE_FAIL);
}
// 删除Redis缓存
String noteDetailRedisKey = RedisKeyConstants.buildNoteDetailKey(noteId);
redisTemplate.delete(noteDetailRedisKey);
// 同步广播模式 MQ将所有实例中的本地缓存都删除掉
rocketMQTemplate.syncSend(MQConstants.TOPIC_DELETE_NOTE_LOCAL_CACHE, noteId);
log.info("====> 笔记置顶更新,本地缓存发送成功...");
return Response.success();
}
/** /**
* 校验笔记的可见性 * 校验笔记的可见性
* *