feat(note): 添加笔记更新功能

- 新增 /update 接口用于修改笔记内容
- 实现笔记类型校验,支持图文和视频笔记
- 校验图片数量不超过8张,视频链接不为空
- 更新笔记元数据并清除Redis和本地缓存
- 支持笔记内容的更新与K-V存储同步
- 新增笔记更新失败和话题不存在的异常码
- 添加 UpdateNoteReqVO 请求参数校验
This commit is contained in:
Hanserwei
2025-10-09 12:17:56 +08:00
parent 7508f176e8
commit 04196f8e3a
5 changed files with 150 additions and 0 deletions

View File

@@ -5,6 +5,7 @@ import com.hanserwei.framework.common.response.Response;
import com.hanserwei.hannote.note.biz.model.vo.FindNoteDetailReqVO;
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 jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@@ -34,4 +35,10 @@ public class NoteController {
return noteService.findNoteDetail(findNoteDetailReqVO);
}
@PostMapping(value = "/update")
@ApiOperationLog(description = "笔记修改")
public Response<?> updateNote(@Validated @RequestBody UpdateNoteReqVO updateNoteReqVO) {
return noteService.updateNote(updateNoteReqVO);
}
}

View File

@@ -17,6 +17,8 @@ public enum ResponseCodeEnum implements BaseExceptionInterface {
NOTE_PUBLISH_FAIL("NOTE-20001", "笔记发布失败"),
NOTE_NOT_FOUND("NOTE-20002", "笔记不存在"),
NOTE_PRIVATE("NOTE-20003", "作者已将该笔记设置为仅自己可见"),
NOTE_UPDATE_FAIL("NOTE-20004", "笔记更新失败"),
TOPIC_NOT_FOUND("NOTE-20005", "话题不存在")
;
// 异常码

View File

@@ -0,0 +1,32 @@
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;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UpdateNoteReqVO {
@NotNull(message = "笔记 ID 不能为空")
private Long id;
@NotNull(message = "笔记类型不能为空")
private Integer type;
private List<String> imgUris;
private String videoUri;
private String title;
private String content;
private Long topicId;
}

View File

@@ -6,6 +6,7 @@ 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.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> {
@@ -23,4 +24,11 @@ public interface NoteService extends IService<NoteDO> {
*/
Response<FindNoteDetailRspVO> findNoteDetail(FindNoteDetailReqVO findNoteDetailReqVO);
/**
* 笔记更新
* @param updateNoteReqVO 笔记更新请求
* @return 笔记更新结果
*/
Response<?> updateNote(UpdateNoteReqVO updateNoteReqVO);
}

View File

@@ -13,6 +13,7 @@ import com.hanserwei.framework.common.response.Response;
import com.hanserwei.framework.common.utils.JsonUtils;
import com.hanserwei.hannote.note.biz.constant.RedisKeyConstants;
import com.hanserwei.hannote.note.biz.domain.dataobject.NoteDO;
import com.hanserwei.hannote.note.biz.domain.dataobject.TopicDO;
import com.hanserwei.hannote.note.biz.domain.mapper.NoteDOMapper;
import com.hanserwei.hannote.note.biz.enums.NoteStatusEnum;
import com.hanserwei.hannote.note.biz.enums.NoteTypeEnum;
@@ -21,6 +22,7 @@ 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.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.KeyValueRpcService;
import com.hanserwei.hannote.note.biz.rpc.UserRpcService;
@@ -34,6 +36,7 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
@@ -289,6 +292,104 @@ public class NoteServiceImpl extends ServiceImpl<NoteDOMapper, NoteDO> implement
return Response.success(findNoteDetailRspVO);
}
@Override
@Transactional(rollbackFor = Exception.class)
public Response<?> updateNote(UpdateNoteReqVO updateNoteReqVO) {
// 笔记ID
Long noteId = updateNoteReqVO.getId();
// 笔记类型
Integer type = updateNoteReqVO.getType();
// 获取对应枚举类
NoteTypeEnum noteTypeEnum = NoteTypeEnum.valueOf(type);
// 判断笔记类型,如果非图文、视频笔记,则抛出异常
if (Objects.isNull(noteTypeEnum)){
throw new ApiException(ResponseCodeEnum.NOTE_TYPE_ERROR);
}
String imgUris = null;
String videoUri = null;
switch (noteTypeEnum) {
case IMAGE_TEXT -> {
List<String> imgUriList = updateNoteReqVO.getImgUris();
// 校验图片是否为空
Preconditions.checkArgument(CollUtil.isNotEmpty(imgUriList), "笔记图片不能为空");
// 校验图片数量
Preconditions.checkArgument(imgUriList.size() <= 8, "笔记图片不能多于 8 张");
imgUris = StringUtils.join(imgUriList, ",");
}
case VIDEO -> {
videoUri = updateNoteReqVO.getVideoUri();
// 校验视频链接是否为空
Preconditions.checkArgument(StringUtils.isNotBlank(videoUri), "笔记视频不能为空");
}
default -> {
// No operation needed, kept for clarity
}
}
// 话题
Long topicId = updateNoteReqVO.getTopicId();
String topicName = null;
if (Objects.nonNull(topicId)){
TopicDO topicDO = topicDOService.getById(topicId);
if (Objects.isNull(topicDO)){
throw new ApiException(ResponseCodeEnum.TOPIC_NOT_FOUND);
}
topicName = topicDO.getName();
// 判断提交的话题是否真实存在
if (StringUtils.isBlank(topicName)){
throw new ApiException(ResponseCodeEnum.TOPIC_NOT_FOUND);
}
}
// 更新笔记元数据表
String content = updateNoteReqVO.getContent();
NoteDO noteDO = NoteDO.builder()
.id(noteId)
.isContentEmpty(StringUtils.isBlank(content))
.imgUris(imgUris)
.title(updateNoteReqVO.getTitle())
.topicId(updateNoteReqVO.getTopicId())
.topicName(topicName)
.type(type)
.updateTime(LocalDateTime.now())
.videoUri(videoUri)
.build();
boolean updateResult = this.updateById(noteDO);
if (!updateResult){
throw new ApiException(ResponseCodeEnum.NOTE_UPDATE_FAIL);
}
// 删除Redis缓存
String noteDetailRedisKey = RedisKeyConstants.buildNoteDetailKey(noteId);
redisTemplate.delete(noteDetailRedisKey);
// 删除本地缓存
LOCAL_CACHE.invalidate(noteId);
// 笔记内容更新
// 查询笔记内容对应的UUID
NoteDO noteDO1 = this.getById(noteId);
String contentUuid = noteDO1.getContentUuid();
// 笔记内容是否更新成功
boolean isUpdateContentSuccess = false;
if (StringUtils.isNotBlank(contentUuid)){
// 若笔记内容为空则删除kv存储
isUpdateContentSuccess = keyValueRpcService.deleteNoteContent(contentUuid);
}else {
// 若将无内容的笔记,更新为了有内容的笔记,需要重新生成 UUID
contentUuid = StringUtils.isBlank(contentUuid) ? UUID.randomUUID().toString() : contentUuid;
// 调用 K-V 更新短文本
isUpdateContentSuccess = keyValueRpcService.saveNoteContent(contentUuid, content);
}
if (!isUpdateContentSuccess){
throw new ApiException(ResponseCodeEnum.NOTE_UPDATE_FAIL);
}
return Response.success();
}
/**
* 校验笔记的可见性
*