feat(service): 实现收藏夹、评论和消息相关功能

- 新增 CollectionService、CommentService 和 MessageService 接口实现类
- 实现了收藏夹、评论和消息相关的业务逻辑
- 更新了 NoteLikeService 和 NoteMapper 接口,增加了点赞和笔记相关方法
- 修改了 application.properties 文件,更新了数据库连接配置
This commit is contained in:
LingandRX 2025-05-11 22:02:12 +08:00
parent 4260b4e028
commit 9a8263b051
17 changed files with 1650 additions and 29 deletions

View File

@ -1,5 +1,6 @@
package com.example.copykamanotes.mapper; package com.example.copykamanotes.mapper;
import com.example.copykamanotes.model.entity.NoteLike;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@ -7,8 +8,45 @@ import java.util.List;
@Mapper @Mapper
public interface NoteLikeMapper { public interface NoteLikeMapper {
/**
* 插入一个点赞记录
*
* @param noteLike 要插入的点赞记录对象包含了用户ID和笔记ID等信息
* @return 返回影响的行数表示插入操作是否成功
*/
int insert(NoteLike noteLike);
/**
* 删除一个点赞记录对象
*
* @param noteLike 要删除的点赞记录通常包含用户ID和笔记ID以定位数据库中的记录
* @return 返回影响的行数表示删除操作是否成功
*/
int delete(NoteLike noteLike);
/**
* 根据用户ID和笔记ID列表查找用户点赞过笔记ID列表
* 此方法用于过滤给定的笔记ID列表仅返回该用户标记为点赞过笔记ID
*
* @param userId 用户ID用于标识用户
* @param noteIds 笔记ID列表待过滤的笔记ID集合
* @return 用户点赞过笔记ID列表
*/
List<Integer> findUserLikedNoteIds( List<Integer> findUserLikedNoteIds(
@Param("userId") Long userId, @Param("userId") Long userId,
@Param("noteIds") List<Integer> noteIds @Param("noteIds") List<Integer> noteIds
); );
/**
* 根据用户ID和笔记ID查找特定的笔记点赞记录
* 此方法用于验证用户是否点赞特定的笔记通过用户ID和笔记ID的组合来查询
*
* @param userId 用户ID用于标识用户
* @param noteId 笔记ID用于标识笔记
* @return 笔记点赞记录如果找到则返回否则返回null
*/
NoteLike findByUserIdAndNoteId(
@Param("userId") Long userId,
@Param("noteId") Integer noteId
);
} }

View File

@ -2,13 +2,207 @@ package com.example.copykamanotes.mapper;
import com.example.copykamanotes.model.dto.note.NoteQueryParams; import com.example.copykamanotes.model.dto.note.NoteQueryParams;
import com.example.copykamanotes.model.entity.Note; import com.example.copykamanotes.model.entity.Note;
import com.example.copykamanotes.model.vo.note.NoteHeatMapItem;
import com.example.copykamanotes.model.vo.note.NoteRankListItem;
import com.example.copykamanotes.model.vo.note.Top3Count;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
import java.util.Set;
@Mapper @Mapper
public interface NoteMapper { public interface NoteMapper {
int countNotes(NoteQueryParams noteQueryParams); /**
* 查询笔记的总数
*
* @param params 查询参数用于过滤笔记
* @return 笔记的总数量
*/
int countNotes(@Param("params") NoteQueryParams params);
List<Note> findByQueryParam(NoteQueryParams noteQueryParams, int pageSize, int offset); /**
* 根据笔记ID查询笔记
*
* @param noteId 笔记ID用于定位特定笔记
* @return 返回对应的笔记对象如果找不到则返回 null
*/
Note findById(@Param("noteId") Integer noteId);
/**
* 根据查询参数获取笔记列表
*
* @param params 查询参数用于过滤笔记
* @param offset 偏移量用于分页
* @param limit 每页大小用于分页
* @return 笔记列表返回符合查询条件的笔记
*/
List<Note> findByQueryParams(@Param("params") NoteQueryParams params,
@Param("offset") int offset,
@Param("limit") int limit);
/**
* 根据用户ID和问题ID查询笔记
*
* @param authorId 用户ID用于标识特定用户
* @param questionId 问题ID用于标识特定问题
* @return 返回匹配的笔记对象如果找不到匹配的笔记则返回 null
*/
Note findByAuthorIdAndQuestionId(@Param("authorId") Long authorId,
@Param("questionId") Integer questionId);
/**
* 根据用户ID查询笔记列表
* @param authorId 用户ID
* @return 用户创建的笔记列表
*/
List<Note> findByAuthorId(@Param("authorId") Long authorId);
/**
* 根据用户ID和问题ID列表过滤出用户已完成的问题ID列表
*
* @param authorId 用户ID用于标识特定用户
* @param questionIds 问题ID列表表示待查询的问题范围
* @return 用户已完成的问题ID列表如果用户未完成任何问题则返回空集合
*/
Set<Integer> filterFinishedQuestionIdsByUser(@Param("authorId") Long authorId,
@Param("questionIds") List<Integer> questionIds);
/**
* 插入一条新的笔记
*
* @param note 笔记对象包含要插入的笔记信息
* @return 插入成功
*/
int insert(Note note);
/**
* 更新笔记信息
*
* @param note 笔记对象包含要更新的笔记信息
* @return 更新成功记录数
*/
int update(Note note);
/**
* 点赞笔记
*
* @param noteId 笔记ID用于标识要点赞的笔记
* @return 点赞成功记录数
*/
int likeNote(@Param("noteId") Integer noteId);
/**
* 取消点赞笔记
*
* @param noteId 笔记ID用于标识要取消点赞的笔记
* @return 取消点赞成功记录数
*/
int unlikeNote(@Param("noteId") Integer noteId);
/**
* 收藏笔记
*
* @param noteId 笔记ID用于标识要收藏的笔记
* @return 收藏结果
*/
int collectNote(@Param("noteId") Integer noteId);
/**
* 取消收藏笔记
*
* @param noteId 笔记ID用于标识要取消收藏的笔记
* @return 取消收藏结果
*/
int unCollectNote(@Param("noteId") Integer noteId);
/**
* 根据笔记ID删除笔记
*
* @param noteId 笔记ID用于标识要删除的笔记
* @return 删除成功记录数
*/
int deleteById(@Param("noteId") Integer noteId);
/**
* 每日笔记提交数排行榜
*
* @return 排行榜数组
*/
List<NoteRankListItem> submitNoteRank();
/**
* 提交热力图
*
* @return 用户提交热力图信息
*/
List<NoteHeatMapItem> submitNoteHeatMap(@Param("authorId") Long authorId);
/**
* 用户提交 top3Count
*
* @return 用户提交 top3Count
*/
Top3Count submitNoteTop3Count(@Param("authorId") Long authorId);
/**
* 当日笔记数
*
* @return 当日笔记数
*/
int getTodayNoteCount();
/**
* 当日提交笔记人数
* @return 当日提交笔记人数
*/
int getTodaySubmitNoteUserCount();
/**
* 笔记总数
* @return 笔记总数
*/
int getTotalNoteCount();
/**
* 增加笔记评论数
*
* @param noteId 笔记ID
*/
void incrementCommentCount(@Param("noteId") Integer noteId);
/**
* 减少笔记评论数
*
* @param noteId 笔记ID
*/
void decrementCommentCount(@Param("noteId") Integer noteId);
/**
* 搜索笔记
*
* @param keyword 关键词
* @param limit 限制数量
* @param offset 偏移量
* @return 笔记列表
*/
List<Note> searchNotes(@Param("keyword") String keyword,
@Param("limit") int limit,
@Param("offset") int offset);
/**
* 根据标签搜索笔记
*
* @param keyword 关键词
* @param tag 标签
* @param limit 限制数量
* @param offset 偏移量
* @return 笔记列表
*/
List<Note> searchNotesByTag(@Param("keyword") String keyword,
@Param("tag") String tag,
@Param("limit") int limit,
@Param("offset") int offset);
} }

View File

@ -2,6 +2,9 @@ package com.example.copykamanotes.service;
import com.example.copykamanotes.model.base.ApiResponse; import com.example.copykamanotes.model.base.ApiResponse;
import com.example.copykamanotes.model.base.EmptyVO; import com.example.copykamanotes.model.base.EmptyVO;
import com.example.copykamanotes.model.base.PageVO;
import com.example.copykamanotes.model.dto.message.MessageQueryParams;
import com.example.copykamanotes.model.vo.message.MessageVO;
import com.example.copykamanotes.model.vo.message.UnreadCountByType; import com.example.copykamanotes.model.vo.message.UnreadCountByType;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -9,9 +12,60 @@ import java.util.List;
@Transactional @Transactional
public interface MessageService { public interface MessageService {
ApiResponse<EmptyVO> deleteMessage(Integer id); /**
* 创建消息
*
* @param receiverId 接收者ID
* @param senderId 发送者ID
* @param type 消息类型
* @param targetId 目标ID
* @param content 消息内容
* @return 创建的消息ID
*/
ApiResponse<Integer> createMessage(Long receiverId, Long senderId, String type, Integer targetId, String content);
ApiResponse<Integer> getUnreadMessageCount(); /**
* 获取消息列表
*
* @param params 查询参数
* @return 消息列表带分页信息
*/
ApiResponse<PageVO<MessageVO>> getMessages(MessageQueryParams params);
ApiResponse<List<UnreadCountByType>> getUnreadMessageCountByType(); /**
* 标记消息为已读
*
* @param messageId 消息ID
* @return 空响应
*/
ApiResponse<EmptyVO> markAsRead(Integer messageId);
/**
* 标记所有消息为已读
*
* @return 空响应
*/
ApiResponse<EmptyVO> markAllAsRead();
/**
* 删除消息
*
* @param messageId 消息ID
* @return 空响应
*/
ApiResponse<EmptyVO> deleteMessage(Integer messageId);
/**
* 获取未读消息数量
*
* @return 未读消息数量
*/
ApiResponse<Integer> getUnreadCount();
/**
* 获取各类型未读消息数量
*
* @return 各类型未读消息数量
*/
ApiResponse<List<UnreadCountByType>> getUnreadCountByType();
} }

View File

@ -0,0 +1,161 @@
package com.example.copykamanotes.service.impl;
import com.example.copykamanotes.annotation.NeedLogin;
import com.example.copykamanotes.mapper.CollectionMapper;
import com.example.copykamanotes.mapper.CollectionNoteMapper;
import com.example.copykamanotes.mapper.NoteMapper;
import com.example.copykamanotes.model.base.ApiResponse;
import com.example.copykamanotes.model.base.EmptyVO;
import com.example.copykamanotes.model.dto.collection.CollectionQueryParams;
import com.example.copykamanotes.model.dto.collection.CreateCollectionBody;
import com.example.copykamanotes.model.dto.collection.UpdateCollectionBody;
import com.example.copykamanotes.model.entity.Collection;
import com.example.copykamanotes.model.entity.CollectionNote;
import com.example.copykamanotes.model.vo.collection.CollectionVO;
import com.example.copykamanotes.model.vo.collection.CreateCollectionVO;
import com.example.copykamanotes.scope.RequestScopeData;
import com.example.copykamanotes.service.CollectionService;
import com.example.copykamanotes.utils.ApiResponseUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@Service
public class CollectionServiceImpl implements CollectionService {
@Autowired
private RequestScopeData requestScopeData;
@Autowired
private CollectionMapper collectionMapper;
@Autowired
private CollectionNoteMapper collectionNoteMapper;
@Autowired
private NoteMapper noteMapper;
@Override
public ApiResponse<List<CollectionVO>> getCollection(CollectionQueryParams queryParams) {
List<Collection> collections = collectionMapper.findByCreatorId(queryParams.getCreatorId());
List<Integer> collectionIds = collections.stream().map(Collection::getCollectionId).toList();
final Set<Integer> collectionNoteIdCollectionIds;
if (queryParams.getNoteId() != null) {
collectionNoteIdCollectionIds = collectionNoteMapper.filterCollectionIdsByNoteId(queryParams.getNoteId(), collectionIds);
} else {
collectionNoteIdCollectionIds = Collections.emptySet();
}
List<CollectionVO> collectionVOList = collections.stream().map(collection -> {
CollectionVO collectionVO = new CollectionVO();
BeanUtils.copyProperties(collection, collectionVO);
if (queryParams.getNoteId() == null) return collectionVO;
CollectionVO.NoteStatus noteStatus = new CollectionVO.NoteStatus();
noteStatus.setIsCollected(collectionNoteIdCollectionIds.contains(collection.getCollectionId()));
noteStatus.setNoteId(queryParams.getNoteId());
collectionVO.setNoteStatus(noteStatus);
return collectionVO;
}).toList();
return ApiResponseUtils.success("获取收藏夹列表成功", collectionVOList);
}
@Override
@NeedLogin
public ApiResponse<CreateCollectionVO> createCollection(CreateCollectionBody createCollectionBody) {
Long creatorId = requestScopeData.getUserId();
Collection collection = new Collection();
BeanUtils.copyProperties(createCollectionBody, collection);
collection.setCreatorId(creatorId);
try {
collectionMapper.insert(collection);
CreateCollectionVO createCollectionVO = new CreateCollectionVO();
createCollectionVO.setCollectionId(collection.getCollectionId());
return ApiResponseUtils.success("创建成功", createCollectionVO);
} catch (Exception e) {
return ApiResponseUtils.error("创建失败");
}
}
@Override
@NeedLogin
@Transactional
public ApiResponse<EmptyVO> deleteCollection(Integer id) {
Long creatorId = requestScopeData.getUserId();
Collection collection = collectionMapper.findByIdAndCreatorId(id, creatorId);
if (collection == null) {
return ApiResponseUtils.error("收藏夹不存在");
}
try {
collectionMapper.deleteById(id);
collectionNoteMapper.deleteByCollectionId(id);
return ApiResponseUtils.success("删除成功");
} catch (Exception e) {
return ApiResponseUtils.error("删除失败");
}
}
@Override
@NeedLogin
@Transactional
public ApiResponse<EmptyVO> batchModifyCollection(UpdateCollectionBody updateCollectionBody) {
Long userId = requestScopeData.getUserId();
Integer noteId = updateCollectionBody.getNoteId();
UpdateCollectionBody.UpdateItem[] collections = updateCollectionBody.getCollections();
for (UpdateCollectionBody.UpdateItem collection : collections) {
Integer collectionId = collection.getCollectionId();
String action = collection.getAction();
Collection collectionEntity = collectionMapper.findByIdAndCreatorId(collectionId, userId);
if (collectionEntity == null)
return ApiResponseUtils.error("收藏夹不存在");
if (action.equals("create")) {
try {
if (collectionMapper.countByCreatorIdAndNoteId(userId, noteId) == 0) {
noteMapper.collectNote(noteId);
}
CollectionNote collectionNote = new CollectionNote();
collectionNote.setCollectionId(collectionId);
collectionNote.setNoteId(noteId);
collectionNoteMapper.insert(collectionNote);
} catch (Exception e) {
return ApiResponseUtils.error("收藏失败");
}
}
if (action.equals("delete")) {
try {
collectionNoteMapper.deleteByCollectionIdAndNoteId(collectionId, noteId);
if (collectionMapper.countByCreatorIdAndNoteId(userId, noteId) == 0) {
noteMapper.unCollectNote(noteId);
}
} catch (Exception e) {
return ApiResponseUtils.error("取消收藏失败");
}
}
}
return ApiResponseUtils.success("操作成功");
}
}

View File

@ -0,0 +1,235 @@
package com.example.copykamanotes.service.impl;
import com.example.copykamanotes.annotation.NeedLogin;
import com.example.copykamanotes.mapper.CommentLikeMapper;
import com.example.copykamanotes.mapper.CommentMapper;
import com.example.copykamanotes.mapper.NoteMapper;
import com.example.copykamanotes.mapper.UserMapper;
import com.example.copykamanotes.model.base.ApiResponse;
import com.example.copykamanotes.model.base.EmptyVO;
import com.example.copykamanotes.model.dto.comment.CommentQueryParams;
import com.example.copykamanotes.model.dto.comment.CreateCommentRequest;
import com.example.copykamanotes.model.dto.comment.UpdateCommentRequest;
import com.example.copykamanotes.model.entity.Comment;
import com.example.copykamanotes.model.entity.Note;
import com.example.copykamanotes.model.entity.User;
import com.example.copykamanotes.model.vo.comment.CommentVO;
import com.example.copykamanotes.model.vo.note.NoteVO;
import com.example.copykamanotes.model.vo.user.UserActionVO;
import com.example.copykamanotes.scope.RequestScopeData;
import com.example.copykamanotes.service.CommentService;
import com.example.copykamanotes.service.MessageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class CommentServiceImpl implements CommentService {
private final CommentMapper commentMapper;
private final NoteMapper noteMapper;
private final UserMapper userMapper;
private final CommentLikeMapper commentLikeMapper;
private final MessageService messageService;
private final RequestScopeData requestScopeData;
@Override
@NeedLogin
@Transactional
public ApiResponse<Integer> createComment(CreateCommentRequest createCommentRequest) {
log.info("开始创建评论:request={}", createCommentRequest);
try {
Long userId = requestScopeData.getUserId();
Note note = noteMapper.findById(createCommentRequest.getNoteId());
if (note == null) {
log.error("笔记不存在 noteId={}", createCommentRequest.getNoteId());
return ApiResponse.error(HttpStatus.BAD_REQUEST.value(), "笔记不存在");
}
Comment comment = new Comment();
comment.setNoteId(createCommentRequest.getNoteId());
comment.setContent(createCommentRequest.getContent());
comment.setAuthorId(userId);
comment.setParentId(createCommentRequest.getParentId());
comment.setLikeCount(0);
comment.setReplyCount(0);
comment.setCreatedAt(LocalDateTime.now());
comment.setUpdatedAt(LocalDateTime.now());
commentMapper.insert(comment);
log.info("评论创建结果: commentId={}", comment.getCommentId());
noteMapper.incrementCommentCount(createCommentRequest.getNoteId());
if (createCommentRequest.getParentId() != null) {
commentMapper.incrementReplyCount(comment.getParentId());
}
ApiResponse<Integer> messageResponse = messageService.createMessage(
note.getAuthorId(),
userId,
"COMMENT",
comment.getCommentId(),
"评论了你的笔记"
);
log.info("发送消息结果: {}", messageResponse);
return ApiResponse.success(comment.getCommentId());
} catch (Exception e) {
return ApiResponse.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "创建评论失败" + e.getMessage());
}
}
@Override
@NeedLogin
@Transactional
public ApiResponse<EmptyVO> updateComment(Integer id, UpdateCommentRequest updateCommentRequest) {
long userId = requestScopeData.getUserId();
Comment comment = commentMapper.findById(id);
if (comment == null) {
return ApiResponse.error(HttpStatus.BAD_REQUEST.value(), "评论不存在");
}
if (!comment.getAuthorId().equals(userId)) {
return ApiResponse.error(HttpStatus.BAD_REQUEST.value(), "无操作权限");
}
try {
comment.setContent(updateCommentRequest.getContent());
comment.setUpdatedAt(LocalDateTime.now());
commentMapper.update(comment);
return ApiResponse.success(new EmptyVO());
} catch (Exception e) {
log.error("更新评论失败: {}", e.getMessage());
return ApiResponse.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "更新评论失败");
}
}
@Override
@NeedLogin
@Transactional
public ApiResponse<EmptyVO> deleteComment(Integer id) {
long userId= requestScopeData.getUserId();
Comment comment = commentMapper.findById(id);
if (comment == null) {
return ApiResponse.error(HttpStatus.NOT_FOUND.value(), "评论不存在");
}
// 检查权限
if (!comment.getAuthorId().equals(userId)) {
return ApiResponse.error(HttpStatus.FORBIDDEN.value(), "无权删除该评论");
}
try {
commentMapper.deleteById(id);
return ApiResponse.success(new EmptyVO());
} catch (Exception e) {
log.error("删除评论失败: {}", e.getMessage());
return ApiResponse.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "删除评论失败");
}
}
@Override
public ApiResponse<List<CommentVO>> getComments(CommentQueryParams params) {
try {
List<Comment> comments = commentMapper.findByQueryParam(params, params.getPageSize(), (params.getPage() - 1) * params.getPageSize());
if (comments == null || comments.isEmpty()) {
return ApiResponse.success(List.of());
}
List<CommentVO> commentVOs = comments.stream().map(comment -> {
CommentVO commentVO = new CommentVO();
commentVO.setCommentId(comment.getCommentId());
commentVO.setContent(comment.getContent());
commentVO.setLikeCount(comment.getLikeCount());
commentVO.setCreatedAt(LocalDateTime.now());
commentVO.setUpdatedAt(LocalDateTime.now());
User author = userMapper.findById(comment.getAuthorId());
if (author != null) {
CommentVO.SimpleAuthorVO simpleAuthorVO = new CommentVO.SimpleAuthorVO();
simpleAuthorVO.setUserId(author.getUserId());
simpleAuthorVO.setUsername(author.getUsername());
simpleAuthorVO.setAvatarUrl(author.getAvatarUrl());
commentVO.setAuthor(simpleAuthorVO);
}
Long currentUserId = requestScopeData.getUserId();
if (currentUserId != null) {
UserActionVO userActionsVO = new UserActionVO();
userActionsVO.setIsLiked(commentLikeMapper.checkIsLiked(currentUserId, comment.getCommentId()));
commentVO.setUserActions(userActionsVO);
}
return commentVO;
}).toList();
return ApiResponse.success(commentVOs);
} catch (Exception e) {
log.error("获取评论失败: {}", e.getMessage());
return ApiResponse.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "获取评论失败");
}
}
@Override
@NeedLogin
@Transactional
public ApiResponse<EmptyVO> likeComment(Integer commentId) {
Long userId= requestScopeData.getUserId();
Comment comment = commentMapper.findById(commentId);
if (comment == null) {
return ApiResponse.error(HttpStatus.NOT_FOUND.value(), "评论不存在");
}
try {
commentMapper.incrementLikeCount(commentId);
messageService.createMessage(
comment.getAuthorId(), // 接收者是评论作者
userId, // 发送者是点赞用户
"LIKE", // 消息类型是点赞
commentId, // 目标ID是评论ID
"点赞了你的评论" // 消息
);
return ApiResponse.success(new EmptyVO());
} catch (Exception e) {
log.error("点赞评论失败: {}", e.getMessage());
return ApiResponse.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "点赞评论失败");
}
}
@Override
@NeedLogin
@Transactional
public ApiResponse<EmptyVO> unlikeComment(Integer commentId) {
Long userId= requestScopeData.getUserId();
Comment comment = commentMapper.findById(commentId);
if (comment == null) {
return ApiResponse.error(HttpStatus.NOT_FOUND.value(), "评论不存在");
}
try {
commentMapper.decrementLikeCount(commentId);
return ApiResponse.success(new EmptyVO());
} catch (Exception e) {
log.error("取消点赞评论失败: {}", e.getMessage());
return ApiResponse.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "取消点赞评论失败");
}
}
}

View File

@ -0,0 +1,50 @@
package com.example.copykamanotes.service.impl;
import com.example.copykamanotes.model.base.ApiResponse;
import com.example.copykamanotes.model.base.EmptyVO;
import com.example.copykamanotes.model.base.PageVO;
import com.example.copykamanotes.model.dto.message.MessageQueryParams;
import com.example.copykamanotes.model.vo.message.MessageVO;
import com.example.copykamanotes.model.vo.message.UnreadCountByType;
import com.example.copykamanotes.service.MessageService;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class MessageServiceImpl implements MessageService {
@Override
public ApiResponse<Integer> createMessage(Long receiverId, Long senderId, String type, Integer targetId, String content) {
return null;
}
@Override
public ApiResponse<EmptyVO> markAsRead(Integer messageId) {
return null;
}
@Override
public ApiResponse<EmptyVO> markAllAsRead() {
return null;
}
@Override
public ApiResponse<EmptyVO> deleteMessage(Integer messageId) {
return null;
}
@Override
public ApiResponse<Integer> getUnreadCount() {
return null;
}
@Override
public ApiResponse<List<UnreadCountByType>> getUnreadCountByType() {
return null;
}
@Override
public ApiResponse<PageVO<MessageVO>> getMessages(MessageQueryParams params) {
return null;
}
}

View File

@ -0,0 +1,89 @@
package com.example.copykamanotes.service.impl;
import com.example.copykamanotes.mapper.NoteCommentMapper;
import com.example.copykamanotes.mapper.NoteMapper;
import com.example.copykamanotes.model.base.ApiResponse;
import com.example.copykamanotes.model.base.EmptyVO;
import com.example.copykamanotes.model.entity.Note;
import com.example.copykamanotes.model.entity.NoteComment;
import com.example.copykamanotes.scope.RequestScopeData;
import com.example.copykamanotes.service.MessageService;
import com.example.copykamanotes.service.NoteCommentService;
import com.example.copykamanotes.utils.ApiResponseUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
@Service
@RequiredArgsConstructor
public class NoteCommentServiceImpl implements NoteCommentService {
private final NoteCommentMapper noteCommentMapper;
private final NoteMapper noteMapper;
private final RequestScopeData requestScopeData;
private final MessageService messageService;
@Override
public ApiResponse<EmptyVO> createComment(Integer noteId, String content) {
Long userId = requestScopeData.getUserId();
Note note = noteMapper.findById(noteId);
if (note == null) {
return ApiResponseUtils.error("笔记不存在");
}
try {
NoteComment noteComment = new NoteComment();
noteComment.setNoteId(noteId);
noteComment.setUserId(userId);
noteComment.setContent(content);
noteComment.setCreatedAt(new Date());
noteComment.setIsDeleted(false);
noteCommentMapper.insert(noteComment);
messageService.createMessage(
note.getAuthorId(),
userId,
"note",
noteId,
"评论了你的笔记"
);
return ApiResponseUtils.success("创建评论成功");
} catch (Exception e) {
return ApiResponseUtils.error("创建评论失败");
}
}
@Override
public ApiResponse<EmptyVO> deleteComment(Integer commentId) {
Long userId = requestScopeData.getUserId();
NoteComment noteComment = noteCommentMapper.findById(commentId);
if (noteComment == null || !noteComment.getUserId().equals(userId)) {
return ApiResponseUtils.error("没有权限");
}
try {
noteComment.setIsDeleted(true);
noteComment.setUpdatedAt(new Date());
noteCommentMapper.update(noteComment);
return ApiResponseUtils.success("删除成功");
} catch (Exception e) {
return ApiResponseUtils.error("删除失败");
}
}
@Override
public ApiResponse<List<NoteComment>> getComments(Integer noteId) {
try{
List<NoteComment> comments = noteCommentMapper.findByNoteId(noteId);
return ApiResponseUtils.success("获取评论成功", comments);
} catch (Exception e) {
return ApiResponseUtils.error("获取评论失败");
}
}
}

View File

@ -1,22 +1,35 @@
package com.example.copykamanotes.service.impl; package com.example.copykamanotes.service.impl;
import com.example.copykamanotes.annotation.NeedLogin;
import com.example.copykamanotes.mapper.MessageMapper;
import com.example.copykamanotes.mapper.NoteLikeMapper; import com.example.copykamanotes.mapper.NoteLikeMapper;
import com.example.copykamanotes.mapper.NoteMapper;
import com.example.copykamanotes.model.base.ApiResponse; import com.example.copykamanotes.model.base.ApiResponse;
import com.example.copykamanotes.model.base.EmptyVO; import com.example.copykamanotes.model.base.EmptyVO;
import com.example.copykamanotes.model.entity.Note;
import com.example.copykamanotes.model.entity.NoteLike;
import com.example.copykamanotes.scope.RequestScopeData;
import com.example.copykamanotes.service.MessageService;
import com.example.copykamanotes.service.NoteLikeService; import com.example.copykamanotes.service.NoteLikeService;
import com.example.copykamanotes.utils.ApiResponseUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@Service @Service
@RequiredArgsConstructor
public class NoteLikeServiceImpl implements NoteLikeService { public class NoteLikeServiceImpl implements NoteLikeService {
@Autowired private final NoteLikeMapper noteLikeMapper;
private NoteLikeMapper noteLikeMapper; private final NoteMapper noteMapper;
private final RequestScopeData requestScopeData;
private final MessageService messageService;
@Override @Override
public Set<Integer> findUserLikedNoteIds(Long userId, List<Integer> noteIds) { public Set<Integer> findUserLikedNoteIds(Long userId, List<Integer> noteIds) {
@ -25,12 +38,58 @@ public class NoteLikeServiceImpl implements NoteLikeService {
} }
@Override @Override
@NeedLogin
@Transactional
public ApiResponse<EmptyVO> likeNote(Integer noteId) { public ApiResponse<EmptyVO> likeNote(Integer noteId) {
return null; Long userId = requestScopeData.getUserId();
Note note = noteMapper.findById(noteId);
if (note == null) {
return ApiResponseUtils.error("笔记不存在");
}
try {
NoteLike noteLike = new NoteLike();
noteLike.setNoteId(noteId);
noteLike.setUserId(userId);
noteLike.setCreatedAt(new Date());
noteMapper.likeNote(noteId);
messageService.createMessage(
note.getAuthorId(),
userId,
"like",
noteId,
"你点赞了笔记:" + note.getContent()
);
return ApiResponseUtils.success("点赞成功");
} catch (Exception e) {
return ApiResponseUtils.error("点赞失败");
}
} }
@Override @Override
@NeedLogin
@Transactional
public ApiResponse<EmptyVO> unlikeNote(Integer noteId) { public ApiResponse<EmptyVO> unlikeNote(Integer noteId) {
return null; Long userId = requestScopeData.getUserId();
Note note = noteMapper.findById(noteId);
if (note == null) {
return ApiResponseUtils.error("笔记不存在");
}
try {
NoteLike noteLike = noteLikeMapper.findByUserIdAndNoteId(userId, noteId);
if (noteLike != null) {
noteLikeMapper.delete(noteLike);
noteMapper.unlikeNote(noteId);
}
return ApiResponseUtils.success("取消点赞成功");
} catch (Exception e) {
return ApiResponseUtils.error("取消点赞失败");
}
} }
} }

View File

@ -1,6 +1,8 @@
package com.example.copykamanotes.service.impl; package com.example.copykamanotes.service.impl;
import com.example.copykamanotes.annotation.NeedLogin;
import com.example.copykamanotes.mapper.NoteMapper; import com.example.copykamanotes.mapper.NoteMapper;
import com.example.copykamanotes.mapper.QuestionMapper;
import com.example.copykamanotes.model.base.ApiResponse; import com.example.copykamanotes.model.base.ApiResponse;
import com.example.copykamanotes.model.base.EmptyVO; import com.example.copykamanotes.model.base.EmptyVO;
import com.example.copykamanotes.model.base.Pagination; import com.example.copykamanotes.model.base.Pagination;
@ -8,23 +10,21 @@ import com.example.copykamanotes.model.dto.note.*;
import com.example.copykamanotes.model.entity.Note; import com.example.copykamanotes.model.entity.Note;
import com.example.copykamanotes.model.entity.Question; import com.example.copykamanotes.model.entity.Question;
import com.example.copykamanotes.model.entity.User; import com.example.copykamanotes.model.entity.User;
import com.example.copykamanotes.model.vo.category.CategoryVO;
import com.example.copykamanotes.model.vo.note.*; import com.example.copykamanotes.model.vo.note.*;
import com.example.copykamanotes.scope.RequestScopeData; import com.example.copykamanotes.scope.RequestScopeData;
import com.example.copykamanotes.service.NoteLikeService; import com.example.copykamanotes.service.*;
import com.example.copykamanotes.service.NoteService;
import com.example.copykamanotes.service.QuestionService;
import com.example.copykamanotes.service.UserService;
import com.example.copykamanotes.utils.ApiResponseUtils; import com.example.copykamanotes.utils.ApiResponseUtils;
import com.example.copykamanotes.utils.MarkdownUtils; import com.example.copykamanotes.utils.MarkdownUtils;
import com.example.copykamanotes.utils.PaginationUtils; import com.example.copykamanotes.utils.PaginationUtils;
import com.google.protobuf.Api;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.relational.core.sql.In;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Collections; import java.util.*;
import java.util.List; import java.util.stream.Collectors;
import java.util.Map;
import java.util.Set;
@Service @Service
public class NoteServiceImpl implements NoteService { public class NoteServiceImpl implements NoteService {
@ -43,6 +43,10 @@ public class NoteServiceImpl implements NoteService {
@Autowired @Autowired
private RequestScopeData requestScopeData; private RequestScopeData requestScopeData;
@Autowired
private CategoryService categoryService;
@Autowired
private QuestionMapper questionMapper;
@Override @Override
public ApiResponse<List<NoteVO>> getNotes(NoteQueryParams noteQueryParams) { public ApiResponse<List<NoteVO>> getNotes(NoteQueryParams noteQueryParams) {
@ -52,10 +56,9 @@ public class NoteServiceImpl implements NoteService {
Pagination pagination = new Pagination(noteQueryParams.getPage(), noteQueryParams.getPageSize(), total); Pagination pagination = new Pagination(noteQueryParams.getPage(), noteQueryParams.getPageSize(), total);
List<Note> notes = noteMapper.findByQueryParam(noteQueryParams, noteQueryParams.getPageSize(), offset); List<Note> notes = noteMapper.findByQueryParams(noteQueryParams, noteQueryParams.getPageSize(), offset);
List<Integer> questionIds = notes.stream().map(Note::getQuestionId).distinct().toList(); List<Integer> questionIds = notes.stream().map(Note::getQuestionId).distinct().toList();
List<Long> authorIds = notes.stream().map(Note::getAuthorId).distinct().toList(); List<Long> authorIds = notes.stream().map(Note::getAuthorId).distinct().toList();
List<Integer> noteIds = notes.stream().map(Note::getNoteId).toList(); List<Integer> noteIds = notes.stream().map(Note::getNoteId).toList();
@ -121,37 +124,164 @@ public class NoteServiceImpl implements NoteService {
} }
@Override @Override
@NeedLogin
public ApiResponse<CreateNoteVO> createNote(CreateNoteRequest createNoteRequest) { public ApiResponse<CreateNoteVO> createNote(CreateNoteRequest createNoteRequest) {
return null; Long userId = requestScopeData.getUserId();
Integer questionId = createNoteRequest.getQuestionId();
Question question = questionService.findById(questionId);
if (question == null) {
return ApiResponseUtils.error("问题不存在");
}
Note note = new Note();
BeanUtils.copyProperties(createNoteRequest, note);
note.setAuthorId(userId);
try {
noteMapper.insert(note);
CreateNoteVO createNoteVO = new CreateNoteVO();
createNoteVO.setNoteId(note.getNoteId());
return ApiResponseUtils.success("创建笔记成功", createNoteVO);
} catch (Exception e) {
return ApiResponseUtils.error("创建笔记失败");
}
} }
@Override @Override
@NeedLogin
public ApiResponse<EmptyVO> updateNote(Integer noteId, UpdateNoteRequest updateNoteRequest) { public ApiResponse<EmptyVO> updateNote(Integer noteId, UpdateNoteRequest updateNoteRequest) {
return null; Long userId = requestScopeData.getUserId();
Note note = noteMapper.findById(noteId);
if (note == null) {
return ApiResponseUtils.error("笔记不存在");
}
if (!Objects.equals(userId, note.getAuthorId())) {
return ApiResponseUtils.error("无权限");
}
try {
note.setContent(updateNoteRequest.getContent());
noteMapper.update(note);
return ApiResponseUtils.success("更新笔记成功");
} catch (Exception e) {
return ApiResponseUtils.error("更新笔记失败");
}
} }
@Override @Override
@NeedLogin
public ApiResponse<EmptyVO> deleteNote(Integer noteId) { public ApiResponse<EmptyVO> deleteNote(Integer noteId) {
return null; Long userId = requestScopeData.getUserId();
Note note = noteMapper.findById(noteId);
if (note == null) {
return ApiResponseUtils.error("笔记不存在");
}
if (!Objects.equals(userId, note.getAuthorId())) {
return ApiResponseUtils.error("无权限");
}
try {
noteMapper.deleteById(noteId);
return ApiResponseUtils.success("删除笔记成功");
} catch (Exception e) {
return ApiResponseUtils.error("删除笔记失败");
}
} }
@Override @Override
public ApiResponse<DownloadNoteVO> downloadNote(Integer noteId) { public ApiResponse<DownloadNoteVO> downloadNote(Integer noteId) {
return null; Long userId = requestScopeData.getUserId();
List<Note> userNotes = noteMapper.findByAuthorId(userId);
Map<Integer, Note> questionNoteMap = userNotes.stream()
.collect(Collectors.toMap(Note::getNoteId, note -> note));
if (userNotes.isEmpty()) {
return ApiResponseUtils.error("无笔记");
}
List<CategoryVO> categoryTree = categoryService.buildCategoryTree();
StringBuilder markdownContent = new StringBuilder();
List<Integer> questionIds = userNotes.stream()
.map(Note::getQuestionId)
.toList();
List<Question> questions = questionMapper.findByIdBatch(questionIds);
for (CategoryVO categoryVO : categoryTree) {
boolean hasTopLevelToc = false;
if (categoryVO.getChildren().isEmpty()) {
continue;
}
for (CategoryVO.ChildrenCategoryVO childrenCategoryVO : categoryVO.getChildren()) {
boolean hasSubLevelToc = false;
Integer categoryId = childrenCategoryVO.getCategoryId();
List<Question> categoryQuestionsList = questions.stream()
.filter(question -> Objects.equals(question.getCategoryId(), categoryId))
.toList();
if (categoryQuestionsList.isEmpty()) {
continue;
}
for (Question question : categoryQuestionsList) {
if (!hasTopLevelToc) {
markdownContent.append("# ").append(categoryVO.getName()).append("\n\n");
hasTopLevelToc = true;
}
if (!hasSubLevelToc) {
markdownContent.append("## ").append(childrenCategoryVO.getName()).append("\n\n");
hasSubLevelToc = true;
}
markdownContent.append("### [")
.append(question.getTitle())
.append("](")
.append(question.getQuestionId())
.append(")\n\n");
Note note = questionNoteMap.get(question.getQuestionId());
markdownContent.append(note.getContent()).append("\n\n");
}
}
}
DownloadNoteVO downloadNoteVO = new DownloadNoteVO();
downloadNoteVO.setMarkdown(markdownContent.toString());
return ApiResponseUtils.success("生产笔记成功", downloadNoteVO);
} }
@Override @Override
public ApiResponse<List<NoteRankListItem>> submitNoteRank() { public ApiResponse<List<NoteRankListItem>> submitNoteRank() {
return null; return ApiResponseUtils.success("获取笔记排行榜成功", noteMapper.submitNoteRank());
} }
@Override @Override
public ApiResponse<List<NoteHeatMapItem>> submitNoteHeatMap() { public ApiResponse<List<NoteHeatMapItem>> submitNoteHeatMap() {
return null; Long userId = requestScopeData.getUserId();
return ApiResponseUtils.success("获取笔记热力图成功", noteMapper.submitNoteHeatMap(userId));
} }
@Override @Override
public ApiResponse<Top3Count> submitNoteTop3Count() { public ApiResponse<Top3Count> submitNoteTop3Count() {
return null; Long userId = requestScopeData.getUserId();
Top3Count top3Count = noteMapper.submitNoteTop3Count(userId);
return ApiResponseUtils.success("获取笔记top3成功", top3Count);
} }
} }

View File

@ -0,0 +1,39 @@
package com.example.copykamanotes.service.impl;
import com.example.copykamanotes.mapper.StatisticMapper;
import com.example.copykamanotes.model.base.ApiResponse;
import com.example.copykamanotes.model.base.Pagination;
import com.example.copykamanotes.model.dto.statistic.StatisticQueryParam;
import com.example.copykamanotes.model.entity.Statistic;
import com.example.copykamanotes.service.StatisticService;
import com.example.copykamanotes.utils.ApiResponseUtils;
import com.example.copykamanotes.utils.PaginationUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
public class StatisticServiceImpl implements StatisticService {
private final StatisticMapper statisticMapper;
@Override
public ApiResponse<List<Statistic>> getStatistics(StatisticQueryParam queryParam) {
Integer page = queryParam.getPage();
Integer pageSize = queryParam.getPageSize();
int offset = PaginationUtils.calculateOffset(page, pageSize);
int total = statisticMapper.countStatistic();
Pagination pagination = new Pagination(page, pageSize, total);
try {
List<Statistic> statistics = statisticMapper.findByPage(pageSize, offset);
return ApiResponseUtils.success("获取统计数据成功", statistics, pagination);
} catch (Exception e) {
return ApiResponseUtils.error("获取统计数据失败");
}
}
}

View File

@ -1,6 +1,7 @@
spring.application.name=copyKamanotes spring.application.name=copyKamanotes
spring.datasource.url=jdbc:mysql://localhost:3306/kamanote_tech #spring.datasource.url=jdbc:mysql://localhost:3306/kamanote_tech
spring.datasource.url=jdbc:mysql://122.152.201.90:9912/note
spring.datasource.username=root spring.datasource.username=root
spring.datasource.password=0andrx spring.datasource.password=0andrx
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
@ -8,9 +9,10 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
server.port= 19090 server.port= 19090
# Redis # Redis
spring.data.redis.host=localhost spring.data.redis.host=122.152.201.90
spring.data.redis.port=6379 spring.data.redis.port=6379
spring.data.redis.database=0 spring.data.redis.database=0
spring.data.redis.password=0andrx
spring.data.redis.timeout=3000 spring.data.redis.timeout=3000
#Mybatis #Mybatis

View File

@ -0,0 +1,11 @@
CREATE TABLE note_comment (
id INT PRIMARY KEY AUTO_INCREMENT,
note_id INT NOT NULL,
user_id BIGINT NOT NULL,
content TEXT NOT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
FOREIGN KEY (note_id) REFERENCES note(id),
FOREIGN KEY (user_id) REFERENCES user(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

View File

@ -0,0 +1,33 @@
-- 评论表
CREATE TABLE IF NOT EXISTS `comment` (
`comment_id` INT NOT NULL AUTO_INCREMENT COMMENT '评论ID',
`note_id` INT UNSIGNED NOT NULL COMMENT '笔记ID',
`author_id` BIGINT UNSIGNED NOT NULL COMMENT '作者ID',
`parent_id` INT DEFAULT NULL COMMENT '父评论ID',
`content` TEXT NOT NULL COMMENT '评论内容',
`like_count` INT NOT NULL DEFAULT 0 COMMENT '点赞数',
`reply_count` INT NOT NULL DEFAULT 0 COMMENT '回复数',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`comment_id`),
KEY `idx_note_id` (`note_id`),
KEY `idx_author_id` (`author_id`),
KEY `idx_parent_id` (`parent_id`),
KEY `idx_created_at` (`created_at`),
CONSTRAINT `fk_comment_note` FOREIGN KEY (`note_id`) REFERENCES `note` (`note_id`) ON DELETE CASCADE,
CONSTRAINT `fk_comment_author` FOREIGN KEY (`author_id`) REFERENCES `user` (`user_id`) ON DELETE CASCADE,
CONSTRAINT `fk_comment_parent` FOREIGN KEY (`parent_id`) REFERENCES `comment` (`comment_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='评论表';
-- 评论点赞表
CREATE TABLE IF NOT EXISTS `comment_like` (
`comment_like_id` INT NOT NULL AUTO_INCREMENT COMMENT '评论点赞ID',
`comment_id` INT NOT NULL COMMENT '评论ID',
`user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`comment_like_id`),
UNIQUE KEY `uk_comment_user` (`comment_id`, `user_id`),
KEY `idx_user_id` (`user_id`),
CONSTRAINT `fk_comment_like_comment` FOREIGN KEY (`comment_id`) REFERENCES `comment` (`comment_id`) ON DELETE CASCADE,
CONSTRAINT `fk_comment_like_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='评论点赞表';

View File

@ -0,0 +1,18 @@
-- 消息表
CREATE TABLE IF NOT EXISTS `message` (
`message_id` INT NOT NULL AUTO_INCREMENT COMMENT '消息ID',
`receiver_id` BIGINT UNSIGNED NOT NULL COMMENT '接收者ID',
`sender_id` BIGINT UNSIGNED NOT NULL COMMENT '发送者ID',
`type` VARCHAR(20) NOT NULL COMMENT '消息类型: COMMENT-评论, LIKE-点赞',
`target_id` INT NOT NULL COMMENT '目标ID(评论ID或笔记ID)',
`content` TEXT NOT NULL COMMENT '消息内容',
`is_read` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否已读: 0-未读, 1-已读',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`message_id`),
KEY `idx_receiver_id` (`receiver_id`),
KEY `idx_sender_id` (`sender_id`),
KEY `idx_created_at` (`created_at`),
CONSTRAINT `fk_message_receiver` FOREIGN KEY (`receiver_id`) REFERENCES `user` (`user_id`) ON DELETE CASCADE,
CONSTRAINT `fk_message_sender` FOREIGN KEY (`sender_id`) REFERENCES `user` (`user_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='消息表';

View File

@ -0,0 +1,70 @@
-- 用户表添加邮箱相关字段(如果不存在)
SET @dbname = DATABASE();
SET @tablename = "user";
SET @columnname = "email";
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (COLUMN_NAME = @columnname)
) > 0,
"SELECT 1",
"ALTER TABLE user ADD COLUMN email VARCHAR(100) COMMENT '用户邮箱'"
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- 添加email_verified列如果不存在
SET @columnname = "email_verified";
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (COLUMN_NAME = @columnname)
) > 0,
"SELECT 1",
"ALTER TABLE user ADD COLUMN email_verified BOOLEAN DEFAULT FALSE COMMENT '邮箱是否验证'"
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- 添加邮箱唯一索引(如果不存在)
SET @indexname = "idx_email";
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.STATISTICS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (INDEX_NAME = @indexname)
) > 0,
"SELECT 1",
"ALTER TABLE user ADD UNIQUE INDEX idx_email (email)"
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- 创建邮箱验证码表(如果不存在)
CREATE TABLE IF NOT EXISTS email_verify_code (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
email VARCHAR(100) NOT NULL COMMENT '邮箱地址',
code VARCHAR(6) NOT NULL COMMENT '验证码',
type VARCHAR(20) NOT NULL COMMENT '验证码类型REGISTER-注册RESET_PASSWORD-重置密码',
expired_at TIMESTAMP NOT NULL COMMENT '过期时间',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
used BOOLEAN NOT NULL DEFAULT FALSE COMMENT '是否已使用',
PRIMARY KEY (id),
INDEX idx_email (email),
INDEX idx_code (code),
INDEX idx_expired_at (expired_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='邮箱验证码表';

View File

@ -0,0 +1,114 @@
-- 为笔记表添加搜索向量字段和索引
ALTER TABLE note
ADD COLUMN search_vector TEXT GENERATED ALWAYS AS
(CONCAT_WS(' ', title, content)) STORED;
-- 添加全文索引(如果不存在)
SET @dbname = DATABASE();
SET @tablename = "note";
SET @indexname = "idx_note_search";
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.STATISTICS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (INDEX_NAME = @indexname)
) > 0,
"SELECT 1",
"ALTER TABLE note ADD FULLTEXT INDEX idx_note_search(search_vector)"
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- 添加普通索引(如果不存在)
SET @indexname = "idx_created_at";
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.STATISTICS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (INDEX_NAME = @indexname)
) > 0,
"SELECT 1",
"ALTER TABLE note ADD INDEX idx_created_at(created_at)"
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
SET @indexname = "idx_user_id";
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.STATISTICS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (INDEX_NAME = @indexname)
) > 0,
"SELECT 1",
"ALTER TABLE note ADD INDEX idx_user_id(user_id)"
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- 为标签表添加索引(如果不存在)
SET @tablename = "tag";
SET @indexname = "idx_name";
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.STATISTICS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (INDEX_NAME = @indexname)
) > 0,
"SELECT 1",
"ALTER TABLE tag ADD INDEX idx_name(name)"
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
SET @indexname = "idx_user_id";
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.STATISTICS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (INDEX_NAME = @indexname)
) > 0,
"SELECT 1",
"ALTER TABLE tag ADD INDEX idx_user_id(user_id)"
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;
-- 为用户表添加组合索引(如果不存在)
SET @tablename = "user";
SET @indexname = "idx_search";
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.STATISTICS
WHERE
(TABLE_SCHEMA = @dbname)
AND (TABLE_NAME = @tablename)
AND (INDEX_NAME = @indexname)
) > 0,
"SELECT 1",
"ALTER TABLE user ADD INDEX idx_search(username, account, email)"
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;

File diff suppressed because one or more lines are too long