diff --git a/han-note-kv/han-note-kv-api/src/main/java/com/hanserwei/hannote/kv/api/KeyValueFeignApi.java b/han-note-kv/han-note-kv-api/src/main/java/com/hanserwei/hannote/kv/api/KeyValueFeignApi.java index d37a23d..6222690 100644 --- a/han-note-kv/han-note-kv-api/src/main/java/com/hanserwei/hannote/kv/api/KeyValueFeignApi.java +++ b/han-note-kv/han-note-kv-api/src/main/java/com/hanserwei/hannote/kv/api/KeyValueFeignApi.java @@ -4,6 +4,8 @@ import com.hanserwei.framework.common.response.Response; import com.hanserwei.hannote.kv.constant.ApiConstants; import com.hanserwei.hannote.kv.dto.req.AddNoteContentReqDTO; import com.hanserwei.hannote.kv.dto.req.DeleteNoteContentReqDTO; +import com.hanserwei.hannote.kv.dto.req.FindNoteContentReqDTO; +import com.hanserwei.hannote.kv.dto.resp.FindNoteContentRspDTO; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -17,7 +19,7 @@ public interface KeyValueFeignApi { Response addNoteContent(@RequestBody AddNoteContentReqDTO addNoteContentReqDTO); @PostMapping(value = PREFIX + "/note/content/find") - Response findNoteContent(@RequestBody AddNoteContentReqDTO addNoteContentReqDTO); + Response findNoteContent(@RequestBody FindNoteContentReqDTO findNoteContentReqDTO); @PostMapping(value = PREFIX + "/note/content/delete") Response deleteNoteContent(@RequestBody DeleteNoteContentReqDTO deleteNoteContentReqDTO); diff --git a/han-note-note/han-note-note-biz/pom.xml b/han-note-note/han-note-note-biz/pom.xml index 8fa4e54..605257b 100644 --- a/han-note-note/han-note-note-biz/pom.xml +++ b/han-note-note/han-note-note-biz/pom.xml @@ -100,6 +100,18 @@ com.hanserwei han-note-distributed-id-generator-api + + + com.hanserwei + han-note-user-api + + + + + com.github.ben-manes.caffeine + caffeine + + diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/config/ThreadPoolConfig.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/config/ThreadPoolConfig.java new file mode 100644 index 0000000..4f090c5 --- /dev/null +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/config/ThreadPoolConfig.java @@ -0,0 +1,37 @@ +package com.hanserwei.hannote.note.biz.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ThreadPoolExecutor; + +@Configuration +public class ThreadPoolConfig { + + @Bean(name = "noteTaskExecutor") + public ThreadPoolTaskExecutor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + // 核心线程数 + executor.setCorePoolSize(10); + // 最大线程数 + executor.setMaxPoolSize(50); + // 队列容量 + executor.setQueueCapacity(200); + // 线程活跃时间(秒) + executor.setKeepAliveSeconds(30); + // 线程名前缀 + executor.setThreadNamePrefix("NoteExecutor-"); + + // 拒绝策略:由调用线程处理(一般为主线程) + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + + // 等待所有任务结束后再关闭线程池 + executor.setWaitForTasksToCompleteOnShutdown(true); + // 设置等待时间,如果超过这个时间还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是被没有完成的任务阻塞 + executor.setAwaitTerminationSeconds(60); + + executor.initialize(); + return executor; + } +} \ No newline at end of file diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/constant/RedisKeyConstants.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/constant/RedisKeyConstants.java new file mode 100644 index 0000000..73458d9 --- /dev/null +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/constant/RedisKeyConstants.java @@ -0,0 +1,20 @@ +package com.hanserwei.hannote.note.biz.constant; + +public class RedisKeyConstants { + + /** + * 笔记详情 KEY 前缀 + */ + public static final String NOTE_DETAIL_KEY = "note:detail:"; + + + /** + * 构建完整的笔记详情 KEY + * @param noteId 笔记ID + * @return 笔记详情 KEY + */ + public static String buildNoteDetailKey(Long noteId) { + return NOTE_DETAIL_KEY + noteId; + } + +} \ No newline at end of file diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/controller/NoteController.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/controller/NoteController.java index 4376781..a07e86f 100644 --- a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/controller/NoteController.java +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/controller/NoteController.java @@ -2,6 +2,8 @@ package com.hanserwei.hannote.note.biz.controller; import com.hanserwei.framework.biz.operationlog.aspect.ApiOperationLog; 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.service.NoteService; import jakarta.annotation.Resource; @@ -26,4 +28,10 @@ public class NoteController { return noteService.publishNote(publishNoteReqVO); } + @PostMapping(value = "/detail") + @ApiOperationLog(description = "笔记详情") + public Response findNoteDetail(@Validated @RequestBody FindNoteDetailReqVO findNoteDetailReqVO) { + return noteService.findNoteDetail(findNoteDetailReqVO); + } + } diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/domain/dataobject/TopicDO.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/domain/dataobject/TopicDO.java index d35cdec..14bee96 100644 --- a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/domain/dataobject/TopicDO.java +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/domain/dataobject/TopicDO.java @@ -1,9 +1,6 @@ package com.hanserwei.hannote.note.biz.domain.dataobject; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/enums/ResponseCodeEnum.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/enums/ResponseCodeEnum.java index df34cdb..53fe6e0 100644 --- a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/enums/ResponseCodeEnum.java +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/enums/ResponseCodeEnum.java @@ -15,6 +15,9 @@ public enum ResponseCodeEnum implements BaseExceptionInterface { // ----------- 业务异常状态码 ----------- NOTE_TYPE_ERROR("NOTE-20000", "未知的笔记类型"), NOTE_PUBLISH_FAIL("NOTE-20001", "笔记发布失败"), + NOTE_NOT_FOUND("NOTE-20002", "笔记不存在"), + NOTE_PRIVATE("NOTE-20003", "作者已将该笔记设置为仅自己可见"), + ; ; // 异常码 diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/model/vo/FindNoteDetailReqVO.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/model/vo/FindNoteDetailReqVO.java new file mode 100644 index 0000000..ff28823 --- /dev/null +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/model/vo/FindNoteDetailReqVO.java @@ -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 FindNoteDetailReqVO { + + @NotNull(message = "笔记 ID 不能为空") + private Long id; + +} \ No newline at end of file diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/model/vo/FindNoteDetailRspVO.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/model/vo/FindNoteDetailRspVO.java new file mode 100644 index 0000000..c8a3012 --- /dev/null +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/model/vo/FindNoteDetailRspVO.java @@ -0,0 +1,49 @@ +package com.hanserwei.hannote.note.biz.model.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindNoteDetailRspVO { + + private Long id; + + private Integer type; + + private String title; + + private String content; + + private List imgUris; + + private Long topicId; + + private String topicName; + + private Long creatorId; + + private String creatorName; + + private String avatar; + + private String videoUri; + + /** + * 编辑时间 + */ + private LocalDateTime updateTime; + + /** + * 是否可见 + */ + private Integer visible; + +} diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/rpc/KeyValueRpcService.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/rpc/KeyValueRpcService.java index 460b008..f94a90a 100644 --- a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/rpc/KeyValueRpcService.java +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/rpc/KeyValueRpcService.java @@ -4,6 +4,8 @@ import com.hanserwei.framework.common.response.Response; import com.hanserwei.hannote.kv.api.KeyValueFeignApi; import com.hanserwei.hannote.kv.dto.req.AddNoteContentReqDTO; import com.hanserwei.hannote.kv.dto.req.DeleteNoteContentReqDTO; +import com.hanserwei.hannote.kv.dto.req.FindNoteContentReqDTO; +import com.hanserwei.hannote.kv.dto.resp.FindNoteContentRspDTO; import jakarta.annotation.Resource; import org.springframework.stereotype.Component; @@ -47,4 +49,23 @@ public class KeyValueRpcService { return Objects.nonNull(response) && response.isSuccess(); } + /** + * 查询笔记内容 + * + * @param uuid 笔记UUID + * @return 笔记内容 + */ + public String findNoteContent(String uuid) { + FindNoteContentReqDTO findNoteContentReqDTO = new FindNoteContentReqDTO(); + findNoteContentReqDTO.setUuid(uuid); + + Response response = keyValueFeignApi.findNoteContent(findNoteContentReqDTO); + + if (Objects.isNull(response) || !response.isSuccess() || Objects.isNull(response.getData())) { + return null; + } + + return response.getData().getContent(); + } + } \ No newline at end of file diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/rpc/UserRpcService.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/rpc/UserRpcService.java new file mode 100644 index 0000000..a125ff1 --- /dev/null +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/rpc/UserRpcService.java @@ -0,0 +1,37 @@ +package com.hanserwei.hannote.note.biz.rpc; + +import com.hanserwei.framework.common.response.Response; +import com.hanserwei.hannote.user.api.UserFeignApi; +import com.hanserwei.hannote.user.dto.req.FindUserByIdReqDTO; +import com.hanserwei.hannote.user.dto.resp.FindUserByIdRspDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.Objects; + +@Component +public class UserRpcService { + @Resource + private UserFeignApi userFeignApi; + + /** + * 查询用户信息 + * + * @param userId 用户ID + * @return 用户信息 + */ + public FindUserByIdRspDTO findById(Long userId) { + FindUserByIdReqDTO findUserByIdReqDTO = new FindUserByIdReqDTO(); + findUserByIdReqDTO.setId(userId); + + Response response = userFeignApi.findById(findUserByIdReqDTO); + + if (Objects.isNull(response) || !response.isSuccess()) { + return null; + } + + return response.getData(); + } + +} + diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/NoteService.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/NoteService.java index af596cc..83c59d4 100644 --- a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/NoteService.java +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/NoteService.java @@ -3,6 +3,8 @@ package com.hanserwei.hannote.note.biz.service; import com.baomidou.mybatisplus.extension.service.IService; import com.hanserwei.framework.common.response.Response; 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; public interface NoteService extends IService { @@ -14,4 +16,11 @@ public interface NoteService extends IService { */ Response publishNote(PublishNoteReqVO publishNoteReqVO); + /** + * 笔记详情 + * @param findNoteDetailReqVO 笔记详情请求 + * @return 笔记详情结果 + */ + Response findNoteDetail(FindNoteDetailReqVO findNoteDetailReqVO); + } \ No newline at end of file diff --git a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/impl/NoteServiceImpl.java b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/impl/NoteServiceImpl.java index bea94ad..dd8eaf9 100644 --- a/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/impl/NoteServiceImpl.java +++ b/han-note-note/han-note-note-biz/src/main/java/com/hanserwei/hannote/note/biz/service/impl/NoteServiceImpl.java @@ -1,31 +1,45 @@ package com.hanserwei.hannote.note.biz.service.impl; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.RandomUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import com.google.common.base.Preconditions; import com.hanserwei.framework.biz.context.holder.LoginUserContextHolder; import com.hanserwei.framework.common.exception.ApiException; 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.mapper.NoteDOMapper; import com.hanserwei.hannote.note.biz.enums.NoteStatusEnum; import com.hanserwei.hannote.note.biz.enums.NoteTypeEnum; import com.hanserwei.hannote.note.biz.enums.NoteVisibleEnum; 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.rpc.DistributedIdGeneratorRpcService; import com.hanserwei.hannote.note.biz.rpc.KeyValueRpcService; +import com.hanserwei.hannote.note.biz.rpc.UserRpcService; import com.hanserwei.hannote.note.biz.service.NoteService; import com.hanserwei.hannote.note.biz.service.TopicDOService; +import com.hanserwei.hannote.user.dto.resp.FindUserByIdRspDTO; import jakarta.annotation.Resource; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; 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 java.time.LocalDateTime; import java.util.List; import java.util.Objects; import java.util.UUID; +import java.util.concurrent.TimeUnit; @Slf4j @Service @@ -36,6 +50,22 @@ public class NoteServiceImpl extends ServiceImpl implement private KeyValueRpcService keyValueRpcService; @Resource private TopicDOService topicDOService; + @Resource + private UserRpcService userRpcService; + @Resource(name = "noteTaskExecutor") + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + @Resource + private RedisTemplate redisTemplate; + + /** + * 笔记详情本地缓存 + */ + @SuppressWarnings("NullableProblems") + private static final Cache LOCAL_CACHE = Caffeine.newBuilder() + .initialCapacity(10000) // 设置初始容量为 10000 个条目 + .maximumSize(10000) // 设置缓存的最大容量为 10000 个条目 + .expireAfterWrite(1, TimeUnit.HOURS) // 设置缓存条目在写入后 1 小时过期 + .build(); @Override public Response publishNote(PublishNoteReqVO publishNoteReqVO) { @@ -127,9 +157,141 @@ public class NoteServiceImpl extends ServiceImpl implement log.error("保存笔记失败!", e); // RPC:调用KV服务,删除笔记内容 if (StringUtils.isNotBlank(contentUuid)) { - keyValueRpcService.deleteNoteContent(contentUuid); + boolean res = keyValueRpcService.deleteNoteContent(contentUuid); + if (!res) { + log.error("删除笔记内容失败!"); + } } } return Response.success(); } + + @Override + public Response findNoteDetail(FindNoteDetailReqVO findNoteDetailReqVO) { + // 查询笔记ID + Long noteId = findNoteDetailReqVO.getId(); + + // 当前登录用户 + Long userId = LoginUserContextHolder.getUserId(); + + // 先从本地缓存中查询 + String findNoteDetailRspVOStrLocalCache = LOCAL_CACHE.getIfPresent(noteId); + if (StringUtils.isNotBlank(findNoteDetailRspVOStrLocalCache)) { + FindNoteDetailRspVO findNoteDetailRspVO = JsonUtils.parseObject(findNoteDetailRspVOStrLocalCache, FindNoteDetailRspVO.class); + log.info("==> 笔记详情命中了本地缓存;{}", findNoteDetailRspVOStrLocalCache); + // 可见性校验 + checkNoteVisibleFromVO(userId, findNoteDetailRspVO); + return Response.success(findNoteDetailRspVO); + } + + // 从Redis缓存中获取 + String noteDetailRedisKey = RedisKeyConstants.buildNoteDetailKey(noteId); + String noteDetailJson = redisTemplate.opsForValue().get(noteDetailRedisKey); + // 若缓存中有该笔记的数据,则直接返回 + if (StringUtils.isNotBlank(noteDetailJson)) { + FindNoteDetailRspVO findNoteDetailRspVO = JsonUtils.parseObject(noteDetailJson, FindNoteDetailRspVO.class); + // 异步线程中将用户信息存入本地缓存 + threadPoolTaskExecutor.submit(() -> { + // 写入本地缓存 + LOCAL_CACHE.put(noteId, + Objects.isNull(findNoteDetailRspVO) ? "null" : JsonUtils.toJsonString(findNoteDetailRspVO)); + }); + // 可见性校验 + checkNoteVisibleFromVO(userId, findNoteDetailRspVO); + + return Response.success(findNoteDetailRspVO); + } + + // 查询笔记 + NoteDO noteDO = this.getOne(new LambdaQueryWrapper<>(NoteDO.class) + .eq(NoteDO::getId, noteId) + .eq(NoteDO::getStatus, 1)); + + // 若笔记不存在,则抛异常 + if (Objects.isNull(noteDO)){ + threadPoolTaskExecutor.execute(() -> { + // 防止缓存穿透,将空数据存入 Redis 缓存 (过期时间不宜设置过长) + // 保底1分钟 + 随机秒数 + long expireSeconds = 60 + RandomUtil.randomInt(60); + redisTemplate.opsForValue().set(noteDetailRedisKey, "null", expireSeconds, TimeUnit.SECONDS); + }); + throw new ApiException(ResponseCodeEnum.NOTE_NOT_FOUND); + } + + // 可见性校验 + Integer visible = noteDO.getVisible(); + checkNoteVisible(visible, userId, noteDO.getCreatorId()); + + // RPC调用用户服务,获取用户信息 + Long creatorId = noteDO.getCreatorId(); + FindUserByIdRspDTO findUserByIdRspDTO = userRpcService.findById(creatorId); + + // RPC: 调用 K-V 存储服务获取内容 + String content = null; + if (Objects.equals(noteDO.getIsContentEmpty(), Boolean.FALSE)) { + content = keyValueRpcService.findNoteContent(noteDO.getContentUuid()); + } + + // 笔记类型 + Integer noteType = noteDO.getType(); + // 图文笔记图片链接(字符串) + String imgUrisStr = noteDO.getImgUris(); + // 图文笔记图片链接(集合) + List imgUris = null; + // 如果查询的是图文笔记,需要将图片链接的逗号分隔开,转换成集合 + if (Objects.equals(noteType, NoteTypeEnum.IMAGE_TEXT.getCode()) + && StringUtils.isNotBlank(imgUrisStr)) { + imgUris = List.of(imgUrisStr.split(",")); + } + + // 构建返参 VO 实体类 + FindNoteDetailRspVO findNoteDetailRspVO = FindNoteDetailRspVO.builder() + .id(noteDO.getId()) + .type(noteDO.getType()) + .title(noteDO.getTitle()) + .content(content) + .imgUris(imgUris) + .topicId(noteDO.getTopicId()) + .topicName(noteDO.getTopicName()) + .creatorId(noteDO.getCreatorId()) + .creatorName(findUserByIdRspDTO.getNickName()) + .avatar(findUserByIdRspDTO.getAvatar()) + .videoUri(noteDO.getVideoUri()) + .updateTime(noteDO.getUpdateTime()) + .visible(noteDO.getVisible()) + .build(); + // 异步线程中将笔记详情存入 Redis + threadPoolTaskExecutor.submit(() -> { + String noteDetailJson1 = JsonUtils.toJsonString(findNoteDetailRspVO); + // 过期时间(保底1天 + 随机秒数,将缓存过期时间打散,防止同一时间大量缓存失效,导致数据库压力太大) + long expireSeconds = 60*60*24 + RandomUtil.randomInt(60*60*24); + redisTemplate.opsForValue().set(noteDetailRedisKey, noteDetailJson1, expireSeconds, TimeUnit.SECONDS); + }); + return Response.success(findNoteDetailRspVO); + } + + /** + * 校验笔记的可见性 + * @param visible 是否可见 + * @param userId 当前用户 ID + * @param creatorId 笔记创建者 + */ + private void checkNoteVisible(Integer visible, Long userId, Long creatorId) { + if (Objects.equals(visible, NoteVisibleEnum.PRIVATE.getCode()) + && !Objects.equals(userId, creatorId)) { // 仅自己可见, 并且访问用户为笔记创建者才能访问,非本人则抛出异常 + throw new ApiException(ResponseCodeEnum.NOTE_PRIVATE); + } + } + + /** + * 校验笔记的可见性(针对 VO 实体类) + * @param userId 当前用户 ID + * @param findNoteDetailRspVO 笔记详情VO类 + */ + private void checkNoteVisibleFromVO(Long userId, FindNoteDetailRspVO findNoteDetailRspVO) { + if (Objects.nonNull(findNoteDetailRspVO)) { + Integer visible = findNoteDetailRspVO.getVisible(); + checkNoteVisible(visible, userId, findNoteDetailRspVO.getCreatorId()); + } + } } diff --git a/han-note-user/han-note-user-api/src/main/java/com/hanserwei/hannote/user/api/UserFeignApi.java b/han-note-user/han-note-user-api/src/main/java/com/hanserwei/hannote/user/api/UserFeignApi.java index 29e0d77..a81f968 100644 --- a/han-note-user/han-note-user-api/src/main/java/com/hanserwei/hannote/user/api/UserFeignApi.java +++ b/han-note-user/han-note-user-api/src/main/java/com/hanserwei/hannote/user/api/UserFeignApi.java @@ -3,9 +3,11 @@ package com.hanserwei.hannote.user.api; import com.hanserwei.framework.common.response.Response; import com.hanserwei.hannote.user.constant.ApiConstants; import com.hanserwei.hannote.user.dto.req.FindUserByEmailReqDTO; +import com.hanserwei.hannote.user.dto.req.FindUserByIdReqDTO; import com.hanserwei.hannote.user.dto.req.RegisterUserReqDTO; import com.hanserwei.hannote.user.dto.req.UpdateUserPasswordReqDTO; import com.hanserwei.hannote.user.dto.resp.FindUserByEmailRspDTO; +import com.hanserwei.hannote.user.dto.resp.FindUserByIdRspDTO; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -42,4 +44,12 @@ public interface UserFeignApi { @PostMapping(value = PREFIX + "/password/update") Response updatePassword(@RequestBody UpdateUserPasswordReqDTO updateUserPasswordReqDTO); + /** + * 根据用户 ID 查询用户信息 + * + * @param findUserByIdReqDTO 查询信息请求 + * @return 响应 + */ + @PostMapping(value = PREFIX + "/findById") + Response findById(@RequestBody FindUserByIdReqDTO findUserByIdReqDTO); } \ No newline at end of file