diff --git a/.idea/compiler.xml b/.idea/compiler.xml index fa317b3..10e6a08 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,8 @@ + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index d46a6c7..22e83c6 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -4,12 +4,12 @@ - + \ No newline at end of file diff --git a/.idea/validation.xml b/.idea/validation.xml new file mode 100644 index 0000000..a7b9942 --- /dev/null +++ b/.idea/validation.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/ocean-collection-service/src/main/java/com/oriole/ocean/controller/UserCollectionController.java b/ocean-collection-service/src/main/java/com/oriole/ocean/controller/UserCollectionController.java index 700d2a9..253341a 100644 --- a/ocean-collection-service/src/main/java/com/oriole/ocean/controller/UserCollectionController.java +++ b/ocean-collection-service/src/main/java/com/oriole/ocean/controller/UserCollectionController.java @@ -16,6 +16,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; import org.apache.dubbo.config.annotation.DubboReference; +import org.apache.ibatis.annotations.Param; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.query.Query; import org.springframework.web.bind.annotation.RequestMapping; @@ -77,16 +78,20 @@ public MsgEntity deleteCollection(@AuthUser AuthUserEntity authUser, // 需要一并清空用户的收藏情况记录 for (Integer itemID : collectionEntity.getItems()) { // 遍历被删除的收藏夹中的文件对应用户行为记录 UserBehaviorEntity userBehaviorEntityQuery = new UserBehaviorEntity(itemID, mainType, username, BehaviorType.DO_COLLECTION); - JSONArray collectionList = (JSONArray) userBehaviorService.findBehaviorRecord(userBehaviorEntityQuery).getExtraInfo(IN_COLLECTION); - collectionList.remove(collectionID); - if (collectionList.isEmpty()) {//所有的收藏已经被全部删除了 - // 移除收藏量统计 - userCollectionService.collectionStatisticsChange(itemID, "-1", mainType); - // 移除用户行为记录 - userBehaviorService.deleteBehaviorRecord(userBehaviorEntityQuery); - } else { // 还有收藏记录存在,更新记录 - userBehaviorService.updateBehaviorRecordExtraInfo(userBehaviorEntityQuery, IN_COLLECTION, collectionList); + Object extraInfo = userBehaviorService.findBehaviorRecord(userBehaviorEntityQuery).getExtraInfo(IN_COLLECTION); + if (extraInfo instanceof List) { + List collectionList = (List) extraInfo; + collectionList.removeIf(id -> id.equals(collectionID)); + + if (collectionList.isEmpty()) {//所有的收藏已经被全部删除了 + // 移除文件收藏量统计 + userCollectionService.collectionStatisticsChange(itemID, "-1", mainType); + // 移除用户行为记录 + userBehaviorService.deleteBehaviorRecord(userBehaviorEntityQuery); + } else { // 还有收藏记录存在,更新记录 + userBehaviorService.updateBehaviorRecordExtraInfo(userBehaviorEntityQuery, IN_COLLECTION, collectionList); + } } } return new MsgEntity<>(SUCCESS); @@ -148,11 +153,10 @@ public MsgEntity changeCollectionItem(@AuthUser AuthUserEn @RequestMapping(value = "/deleteCollectionItem", method = RequestMethod.GET) public MsgEntity deleteCollectedItem(@AuthUser AuthUserEntity authUser, - @RequestParam(required = false) String username, @RequestParam String collectionID, @RequestParam Integer itemID, @RequestParam MainType mainType) { - username = authUser.getAllowOperationUsername(username); + String username = authUser.getUsername(); Integer removedItemID = userCollectionService.deleteOneCollectionItem(username, collectionID, itemID, mainType); if (removedItemID == null) { @@ -160,16 +164,20 @@ public MsgEntity deleteCollectedItem(@AuthUser AuthUserEntity authUser, } // 进行到了此处说明肯定有删除发生 UserBehaviorEntity userBehaviorEntityQuery = new UserBehaviorEntity(itemID, mainType, username, BehaviorType.DO_COLLECTION); - JSONArray collectionList = (JSONArray) userBehaviorService.findBehaviorRecord(userBehaviorEntityQuery).getExtraInfo(IN_COLLECTION); - collectionList.remove(collectionID); - if (collectionList.isEmpty()) {//所有的收藏已经被全部删除了 - // 移除文件收藏量统计 - userCollectionService.collectionStatisticsChange(itemID, "-1", mainType); - // 移除用户行为记录 - userBehaviorService.deleteBehaviorRecord(userBehaviorEntityQuery); - } else { // 还有收藏记录存在,更新记录 - userBehaviorService.updateBehaviorRecordExtraInfo(userBehaviorEntityQuery, IN_COLLECTION, collectionList); + Object extraInfo = userBehaviorService.findBehaviorRecord(userBehaviorEntityQuery).getExtraInfo(IN_COLLECTION); + if (extraInfo instanceof List) { + List collectionList = (List) extraInfo; + collectionList.removeIf(id -> id.equals(collectionID)); + + if (collectionList.isEmpty()) {//所有的收藏已经被全部删除了 + // 移除文件收藏量统计 + userCollectionService.collectionStatisticsChange(itemID, "-1", mainType); + // 移除用户行为记录 + userBehaviorService.deleteBehaviorRecord(userBehaviorEntityQuery); + } else { // 还有收藏记录存在,更新记录 + userBehaviorService.updateBehaviorRecordExtraInfo(userBehaviorEntityQuery, IN_COLLECTION, collectionList); + } } return new MsgEntity<>(SUCCESS); } @@ -182,6 +190,7 @@ public MsgEntity> getCollectionItemList(@AuthUser AuthUserEntity a username = authUser.getAllowOperationUsername(username); UserCollectionEntity.CollectionEntity collectionEntity = userCollectionService.getCollectionByUsernameAndCollectionID(username, collectionID, mainType); + if (collectionEntity == null) return new MsgEntity<>("SUCCESS", "2", null); return new MsgEntity<>("SUCCESS", "1", collectionEntity.getItems()); } } diff --git a/ocean-collection-service/src/main/java/com/oriole/ocean/service/UserCollectionServiceImpl.java b/ocean-collection-service/src/main/java/com/oriole/ocean/service/UserCollectionServiceImpl.java index 24f35b2..a54e3dc 100644 --- a/ocean-collection-service/src/main/java/com/oriole/ocean/service/UserCollectionServiceImpl.java +++ b/ocean-collection-service/src/main/java/com/oriole/ocean/service/UserCollectionServiceImpl.java @@ -68,6 +68,9 @@ public UserCollectionEntity.CollectionEntity getCollectionByUsernameAndCollectio AggregationResults results = mongoTemplate.aggregate(aggregation, getCollectionName(mainType), UserCollectionEntity.CollectionEntity.class); + + System.out.println(results.getMappedResults()); + if (results.getMappedResults().isEmpty()) return null; return results.getMappedResults().get(0); } diff --git a/ocean-comment-service/src/main/java/com/oriole/ocean/controller/UserCommentController.java b/ocean-comment-service/src/main/java/com/oriole/ocean/controller/UserCommentController.java index d8ad741..b0e53c5 100644 --- a/ocean-comment-service/src/main/java/com/oriole/ocean/controller/UserCommentController.java +++ b/ocean-comment-service/src/main/java/com/oriole/ocean/controller/UserCommentController.java @@ -23,11 +23,13 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import javax.xml.stream.events.Comment; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; +import static com.oriole.ocean.common.enumerate.BehaviorType.DO_COMMENT_LIKE; import static com.oriole.ocean.common.enumerate.ResultCode.SUCCESS; import static com.oriole.ocean.common.enumerate.ResultCode.UNAUTHORIZED_OPERATION; @@ -115,16 +117,12 @@ public MsgEntity addComment(@AuthUser AuthUserEntity authUser, String cid = RandomStringUtils.randomAlphanumeric(8).toUpperCase(); AbstractComment returnEntity; //构建用户消息事件 - NotifyEntity notifyEntity = new NotifyEntity(NotifyType.REMIND,authUser.getUsername()); - notifyEntity.setTargetIDAndType(String.valueOf(bindID),mainType); - notifyEntity.setContent(commentContent); if (!isReply) { CommentEntity commentEntity = new CommentEntity(cid, authUser.getUsername(), commentContent); commentService.addComment(bindID, mainType, commentEntity); + notifyService.addNotifyByComment(bindID, mainType, commentEntity); commentEntity.setBuildDate(handleTime(commentEntity.getBuildDate())); returnEntity = commentEntity; - notifyEntity.setAction(NotifyAction.NEW_COMMENT);// 新评论事件不面向任何其他评论 - } else { CommentReplyEntity fileCommentReplyEntity = new CommentReplyEntity( replyInCommentID + "_" + cid, authUser.getUsername(), @@ -132,14 +130,13 @@ public MsgEntity addComment(@AuthUser AuthUserEntity authUser, replyToCommentReplyID, commentContent); commentService.addCommentReply(bindID, mainType, replyInCommentID, fileCommentReplyEntity); - fileCommentReplyEntity.setBuildDate(handleTime(fileCommentReplyEntity.getBuildDate())); - returnEntity = fileCommentReplyEntity; - notifyEntity.setAction(NotifyAction.NEW_REPLY); if(!replyToCommentReplyID.isEmpty()){ - notifyEntity.setCommentID(replyToCommentReplyID); + notifyService.addNotifyByReply(bindID, mainType, replyToCommentReplyID, fileCommentReplyEntity); }else { - notifyEntity.setCommentID(replyInCommentID); + notifyService.addNotifyByReply(bindID, mainType, replyInCommentID, fileCommentReplyEntity); } + fileCommentReplyEntity.setBuildDate(handleTime(fileCommentReplyEntity.getBuildDate())); + returnEntity = fileCommentReplyEntity; } // 增加用户消息订阅事件:用户需要订阅自己发布的评论或回复的动态 List notifyActionList = new ArrayList<>(); @@ -148,9 +145,6 @@ public MsgEntity addComment(@AuthUser AuthUserEntity authUser, notifySubscriptionService.setNotifySubscription(authUser.getUsername(), notifyActionList, returnEntity.getId(), NotifySubscriptionTargetType.COMMENT); - // 产生用户消息事件 - notifyService.addNotify(notifyEntity); - if (!isReply) { // 只有评论才计入文章的评论量统计 commentService.commentStatisticsChange(bindID, "+1", mainType); @@ -172,6 +166,7 @@ public MsgEntity getComment(@RequestParam Integer bindID, } List commentEntities = commentService.getComments(bindID, mainType, commentCount, replyCount, pageNum); //处理评论 + //commentEntities.removeIf( commentEntity -> commentEntity.getId() == null); for (CommentEntity comment : commentEntities) { //处理评论日期信息 comment.setBuildDate(handleTime(comment.getBuildDate())); @@ -184,6 +179,24 @@ public MsgEntity getComment(@RequestParam Integer bindID, return new MsgEntity<>("SUCCESS", "1", commentsListEntity); } + @RequestMapping(value = "/getCommentById", method = RequestMethod.GET) + public MsgEntity getCommentById( + @RequestParam Integer bindID, + @RequestParam MainType mainType, + @RequestParam String commentID) { + CommentEntity commentEntity = commentService.getCommentByCommentID(bindID, mainType, commentID); + return new MsgEntity<>("SUCCESS", "1", commentEntity); + } + + @RequestMapping(value = "/getCommentReplyByCommentReplyID", method = RequestMethod.GET) + public MsgEntity getCommentReplyByCommentReplyID( + @RequestParam Integer bindID, + @RequestParam MainType mainType, + @RequestParam String commentReplyID) { + CommentReplyEntity commentReplyEntity = commentService.getCommentReplyByCommentReplyID(bindID, mainType, commentReplyID); + return new MsgEntity<>("SUCCESS", "1", commentReplyEntity); + } + @RequestMapping(value = "/getCommentReply", method = RequestMethod.GET) public MsgEntity getCommentReply( @RequestParam Integer bindID, @@ -217,6 +230,7 @@ public MsgEntity evaluateComment(@AuthUser AuthUserEntity authUser, @RequestParam Boolean isLike) { // 添加用户行为 UserBehaviorEntity userBehaviorEntity = new UserBehaviorEntity(bindID, mainType, authUser.getUsername(), null); + userBehaviorEntity.setExtraInfo(BehaviorExtraInfo.COMMENT_ID, commentID); List evaluates = userBehaviorService.checkAndGetUserEvaluateBehavior(userBehaviorEntity, isCancel, isLike); userBehaviorService.setUserEvaluateBehavior(userBehaviorEntity, evaluates); @@ -225,6 +239,18 @@ public MsgEntity evaluateComment(@AuthUser AuthUserEntity authUser, return new MsgEntity<>(SUCCESS); } + @RequestMapping(value = "/findEvaluateRecord", method = RequestMethod.GET) + public MsgEntity findEvaluateRecord(@AuthUser AuthUserEntity authUser, + @RequestParam Integer bindID, + @RequestParam MainType mainType, + @RequestParam String commentID) { + UserBehaviorEntity userBehaviorEntity = new UserBehaviorEntity(bindID, mainType, authUser.getUsername(), DO_COMMENT_LIKE); + userBehaviorEntity.setExtraInfo(BehaviorExtraInfo.COMMENT_ID, commentID); + + boolean ifExists = (userBehaviorService.findBehaviorRecord(userBehaviorEntity) != null); + return new MsgEntity<>("SUCCESS", "1", ifExists); + } + public String handleTime(String buildDate) { long timeDifference = new Date().getTime() - Long.parseLong(buildDate); diff --git a/ocean-comment-service/src/main/java/com/oriole/ocean/service/CommentServiceImpl.java b/ocean-comment-service/src/main/java/com/oriole/ocean/service/CommentServiceImpl.java index af84916..32f1808 100644 --- a/ocean-comment-service/src/main/java/com/oriole/ocean/service/CommentServiceImpl.java +++ b/ocean-comment-service/src/main/java/com/oriole/ocean/service/CommentServiceImpl.java @@ -37,7 +37,7 @@ public class CommentServiceImpl { private FileExtraService fileExtraService; @DubboReference - private FileService fileService ; + private FileService fileService; public void commentStatisticsChange(Integer itemID, String addNumber, MainType mainType) { switch (mainType){ @@ -76,7 +76,7 @@ public boolean isCanAddComment(Integer bindID, String username, MainType mainTyp } private String getCollectionName(MainType mainType){ - return "comments_" + mainType.toString(); + return mainType.toString().toLowerCase() + "_comments"; } public CommentsListEntity getCommentsListEntity(Integer bindID, MainType mainType) { @@ -91,7 +91,9 @@ public CommentsListEntity getCommentsListEntity(Integer bindID, MainType mainTyp public List getComments(Integer bindID, MainType mainType, Integer commentCount, Integer queryReplyCount, Integer pageNum) { List operations = new ArrayList<>(); operations.add(Aggregation.match(Criteria.where("bindID").is(bindID))); - operations.add(Aggregation.unwind("comments")); + operations.add(Aggregation.match(Criteria.where("comments").exists(true))); + operations.add(Aggregation.match(Criteria.where("comments").not().size(0))); + operations.add(Aggregation.unwind("comments", true)); operations.add(Aggregation.sort(Sort.by(new Sort.Order(Sort.Direction.DESC, "comments.hotValue"))) .and(Sort.by(new Sort.Order(Sort.Direction.DESC, "comments.likeNumber"))) .and(Sort.by(new Sort.Order(Sort.Direction.ASC, "comments.dislikeNumber")))); diff --git a/ocean-common/pom.xml b/ocean-common/pom.xml index e23ede0..a7800c0 100644 --- a/ocean-common/pom.xml +++ b/ocean-common/pom.xml @@ -31,7 +31,7 @@ com.baomidou mybatis-plus - 3.5.4.1 + 3.3.2 diff --git a/ocean-common/src/main/java/com/oriole/ocean/common/po/mongo/FavorEntity.java b/ocean-common/src/main/java/com/oriole/ocean/common/po/mongo/FavorEntity.java new file mode 100644 index 0000000..d288abb --- /dev/null +++ b/ocean-common/src/main/java/com/oriole/ocean/common/po/mongo/FavorEntity.java @@ -0,0 +1,17 @@ +package com.oriole.ocean.common.po.mongo; + +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +import java.util.Map; + +@Data +@Document(collection = "note_favor") +public class FavorEntity implements java.io.Serializable { + @Id + private String id; + + private String username; + private String noteId; +} diff --git a/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NoteCommentEntity.java b/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NoteCommentEntity.java new file mode 100644 index 0000000..64f7be9 --- /dev/null +++ b/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NoteCommentEntity.java @@ -0,0 +1,53 @@ +package com.oriole.ocean.common.po.mysql; + +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; + +import java.util.Date; + +@Data +public class NoteCommentEntity implements java.io.Serializable { + @TableField("id") + private String id; + + @TableField("note_id") + private String noteId; + + @TableField("content") + private String content; + + @TableField("build_username") + private String buildUsername; + + @TableField("like_num") + private Integer likeNum; + + @TableField("build_date") + private Date buildDate; + + @TableField("is_deleted") + private Byte isDeleted; + + @TableField("reply_id") + private String replyId; + + @TableField("reply_username") + private String replyUsername; + + /** + * Whether the current user has liked this comment. + * This field is not mapped to the database, only used for data transfer + */ + @TableField(exist = false) + private Boolean isLikedByCurrentUser; + + public NoteCommentEntity( + String noteId, String buildUsername, + String content, String replyId, String replyUsername) { + this.noteId = noteId; + this.content = content; + this.buildUsername = buildUsername; + this.replyId = replyId; + this.replyUsername = replyUsername; + } +} diff --git a/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NoteCommentLikeEntity.java b/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NoteCommentLikeEntity.java new file mode 100644 index 0000000..52e51f2 --- /dev/null +++ b/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NoteCommentLikeEntity.java @@ -0,0 +1,48 @@ +package com.oriole.ocean.common.po.mysql; + +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 lombok.Data; + +import java.util.Date; + +/** + * Note like record entity + * Records the like relationship between users and notes + */ +@Data +@TableName("`note_like`") +public class NoteCommentLikeEntity implements java.io.Serializable { + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + @TableField("username") + private String username; + + @TableField("comment_id") + private String commentId; + + @TableField("like_time") + private Date likeTime; + + /** + * Default constructor + */ + public NoteCommentLikeEntity() { + this.likeTime = new Date(); + } + + /** + * Constructor with username and commentId + * @param username The username who likes the comment + * @param commentId The ID of the liked comment + */ + public NoteCommentLikeEntity(String username, String commentId) { + this.username = username; + this.commentId = commentId; + this.likeTime = new Date(); + } +} diff --git a/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NoteEntity.java b/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NoteEntity.java index d98556d..8305385 100644 --- a/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NoteEntity.java +++ b/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NoteEntity.java @@ -1,7 +1,9 @@ package com.oriole.ocean.common.po.mysql; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.IdType; import lombok.Data; import java.time.DateTimeException; @@ -10,19 +12,46 @@ @Data @TableName("`note`") public class NoteEntity implements java.io.Serializable { - @TableId("note_id") - private Integer noteID; + @TableId(value = "note_id", type = IdType.ASSIGN_ID) + private String id; - private Integer noteType; + @TableField("build_username") + private String buildUsername; + + @TableField("content") private String content; - private Integer likeNum; - private Integer commentNum; - private Integer readNum; - private Date refreshDate; + + @TableField("tag") + private String tag; + + @TableField("build_date") private Date buildDate; - private String buildUsername; + + @TableField("refresh_date") + private Date refreshDate; + + @TableField("is_deleted") + private Byte isDeleted; + + @TableField("is_anon") private Byte isAnon; - private Byte isApproved; + + @TableField("is_allow_comment") private Byte isAllowComment; + @TableField("like_num") + private Integer likeNum; + + @TableField("comment_num") + private Integer commentNum; + + @TableField("read_num") + private Integer readNum; + + /** + * Whether the current user has liked this note + * This field is not mapped to database, only used for data transfer + */ + @TableField(exist = false) + private Boolean isLikedByCurrentUser; } diff --git a/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NoteLikeEntity.java b/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NoteLikeEntity.java new file mode 100644 index 0000000..3621733 --- /dev/null +++ b/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NoteLikeEntity.java @@ -0,0 +1,48 @@ +package com.oriole.ocean.common.po.mysql; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.IdType; +import lombok.Data; + +import java.util.Date; + +/** + * Note like record entity + * Records the like relationship between users and notes + */ +@Data +@TableName("`note_like`") +public class NoteLikeEntity implements java.io.Serializable { + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + @TableField("username") + private String username; + + @TableField("note_id") + private String noteId; + + @TableField("like_time") + private Date likeTime; + + /** + * Default constructor + */ + public NoteLikeEntity() { + this.likeTime = new Date(); + } + + /** + * Constructor with username and noteId + * @param username The username who likes the note + * @param noteId The ID of the liked note + */ + public NoteLikeEntity(String username, String noteId) { + this.username = username; + this.noteId = noteId; + this.likeTime = new Date(); + } +} diff --git a/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NotifyEntity.java b/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NotifyEntity.java index 3560168..3eb7dac 100644 --- a/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NotifyEntity.java +++ b/ocean-common/src/main/java/com/oriole/ocean/common/po/mysql/NotifyEntity.java @@ -24,7 +24,7 @@ public class NotifyEntity implements java.io.Serializable { private NotifyType type; @TableField(value = "target_id") - private String targetID; + private String targetId; private MainType targetType; @TableField(value = "comment_id") @@ -50,8 +50,8 @@ public NotifyEntity(NotifyType type, String buildUsername) { this.buildDate = new Date(); } - public void setTargetIDAndType(String targetID,MainType targetType) { - this.targetID = targetID; + public void setTargetIdAndType(String targetId, MainType targetType) { + this.targetId = targetId; this.targetType = targetType; } } diff --git a/ocean-common/src/main/java/com/oriole/ocean/common/service/NoteCommentService.java b/ocean-common/src/main/java/com/oriole/ocean/common/service/NoteCommentService.java new file mode 100644 index 0000000..02014f5 --- /dev/null +++ b/ocean-common/src/main/java/com/oriole/ocean/common/service/NoteCommentService.java @@ -0,0 +1,85 @@ +package com.oriole.ocean.common.service; + +import com.oriole.ocean.common.po.mongo.FavorEntity; +import com.oriole.ocean.common.po.mysql.NoteCommentEntity; +import com.oriole.ocean.common.po.mysql.NoteCommentLikeEntity; +import com.oriole.ocean.common.po.mysql.NoteEntity; +import com.oriole.ocean.common.po.mysql.NoteLikeEntity; + +import java.util.List; + +/** + * Note comment service interface + * Provides business logic for note comment operations + */ +public interface NoteCommentService { + + /** + * Check if user is the creator of a comment + * @param commentId Comment ID + * @param username Username to check + * @return true if user is creator, false otherwise + */ + boolean isCommentCreator(String commentId, String username); + + /** + * Delete a note comment + * @param commentId Comment ID + */ + void deleteNoteComment(String commentId); + + /** + * Get note comments by note ID (for anonymous users) + * @param noteId Note ID + * @return List of note comments + */ + List getNoteCommentsByNoteId(String noteId); + + /** + * Get note comments by note ID with like status (for authenticated users) + * @param noteId Note ID + * @param username Current user's username + * @return List of note comments with like status + */ + List getNoteCommentsByNoteIdWithLikeStatus(String noteId, String username); + + /** + * Get note comments by comment ID + * @param commentId comment ID + * @return note comment + */ + NoteCommentEntity getNoteComment(String commentId); + + /** + * Create a new note comment + * @param noteCommentEntity Note comment entity to create + * @return Created note comment entity + */ + NoteCommentEntity createNoteComment(NoteCommentEntity noteCommentEntity); + + + /** + * Like or unlike a comment + * @param username Username who likes/unlikes + * @param commentId Comment ID to like/unlike + * @param isLike true to like, false to unlike + * @return NoteCommentLikeEntity if liked, null if unliked + */ + NoteCommentLikeEntity likeNoteComment(String username, String commentId, boolean isLike); + + /** + * Check if a user has liked a specific comment + * @param username Username to check + * @param commentId Comment ID to check + * @return true if liked, false otherwise + */ + boolean hasUserLikedNoteComment(String username, String commentId); + + /** + * Get like record for a user and comment + * @param username Username + * @param commentId Comment ID + * @return NoteCommentLikeEntity if exists, null otherwise + */ + NoteCommentLikeEntity getNoteCommentLike(String username, String commentId); +} diff --git a/ocean-common/src/main/java/com/oriole/ocean/common/service/NoteService.java b/ocean-common/src/main/java/com/oriole/ocean/common/service/NoteService.java new file mode 100644 index 0000000..ab53ff3 --- /dev/null +++ b/ocean-common/src/main/java/com/oriole/ocean/common/service/NoteService.java @@ -0,0 +1,158 @@ +package com.oriole.ocean.common.service; + +import com.oriole.ocean.common.po.mongo.FavorEntity; +import com.oriole.ocean.common.po.mysql.NoteCommentEntity; +import com.oriole.ocean.common.po.mysql.NoteEntity; +import com.oriole.ocean.common.po.mysql.NoteLikeEntity; +import java.util.List; + +/** + * Note service interface + * Provides business logic for note operations + */ +public interface NoteService { + + void changeNoteCommentNum(String noteId, int value); + + /** + * Check if user is the creator of a note + * @param noteId Note ID + * @param username Username to check + * @return true if user is creator, false otherwise + */ + boolean isNoteCreator(String noteId, String username); + + void readNote(String noteId); + + /** + * Delete a note (soft delete) + * @param noteID Note ID to delete + */ + void deleteNote(String noteID); + + /** + * Favorite/unfavorite a note + * @param username Username + * @param isFavor Whether to favor or unfavor + * @param noteID Note ID + * @param id Existing favor record ID (for unfavor operation) + * @return Favor entity or null + */ + FavorEntity favoriteNote(String username, boolean isFavor, String noteID, String id); + + /** + * Get note by ID + * @param noteID Note ID + * @return Note entity + */ + NoteEntity getNoteById(String noteID); + + /** + * Get note by ID with like status for current user + * @param noteID Note ID + * @param username Current user's username + * @return Note entity with like status + */ + NoteEntity getNoteByIdWithLikeStatus(String noteID, String username); + + /** + * Get user behavior (favorites) for a specific note + * @param username Username + * @param noteId Note ID + * @return Favor entity or null + */ + FavorEntity getBehaviourByUsernameAndNoteId(String username, String noteId); + + /** + * Get user behaviors (favorites) list + * @param username Username + * @param pageNo Page number + * @param pageSize Page size + * @return List of favor entities + */ + List getBehaviourByUsername(String username, int pageNo, int pageSize); + + /** + * Get latest notes + * @return List of latest notes + */ + List getLatestNotes(); + + /** + * Get latest notes with like status for current user + * @param username Current user's username + * @return List of latest notes with like status + */ + List getLatestNotesWithLikeStatus(String username); + + /** + * Get notes by creator name + * @param username Current user's name + * @return list of notes created by that user + */ + List getNotesByUsernameWithLikeStatus(String username); + + /** + * Get notes by keywords + * @param searchString Search keywords + * @return List of matching notes + */ + List getNotesByKeywords(String searchString); + + /** + * Get notes by keywords with like status for current user + * @param searchString Search keywords + * @param username Current user's username + * @return List of matching notes with like status + */ + List getNotesByKeywordsWithLikeStatus(String searchString, String username); + + /** + * Get notes by tag + * @param tag Note tag + * @return List of notes with specified tag + */ + List getNotesByTag(String tag); + + /** + * Get notes by tag with like status for current user + * @param tag Note tag + * @param username Current user's username + * @return List of notes with specified tag and like status + */ + List getNotesByTagWithLikeStatus(String tag, String username); + + /** + * Create a new note + * @param noteEntity Note entity to create + * @return Created note entity + */ + NoteEntity createNote(NoteEntity noteEntity); + + // === Like functionality === + + /** + * Like or unlike a note + * @param username Username who likes/unlikes + * @param noteId Note ID to like/unlike + * @param isLike true to like, false to unlike + * @return NoteLikeEntity if liked, null if unliked + */ + NoteLikeEntity likeNote(String username, String noteId, boolean isLike); + + /** + * Check if a user has liked a specific note + * @param username Username to check + * @param noteId Note ID to check + * @return true if liked, false otherwise + */ + boolean hasUserLikedNote(String username, String noteId); + + /** + * Get like record for a user and note + * @param username Username + * @param noteId Note ID + * @return NoteLikeEntity if exists, null otherwise + */ + NoteLikeEntity getNoteLike(String username, String noteId); +} diff --git a/ocean-common/src/main/java/com/oriole/ocean/common/service/NotifyService.java b/ocean-common/src/main/java/com/oriole/ocean/common/service/NotifyService.java index 8a35f08..b144975 100644 --- a/ocean-common/src/main/java/com/oriole/ocean/common/service/NotifyService.java +++ b/ocean-common/src/main/java/com/oriole/ocean/common/service/NotifyService.java @@ -1,7 +1,16 @@ package com.oriole.ocean.common.service; +import com.oriole.ocean.common.enumerate.MainType; +import com.oriole.ocean.common.po.mongo.UserBehaviorEntity; +import com.oriole.ocean.common.po.mongo.comment.CommentEntity; +import com.oriole.ocean.common.po.mongo.comment.CommentReplyEntity; import com.oriole.ocean.common.po.mysql.NotifyEntity; public interface NotifyService { void addNotify(NotifyEntity notifyEntity); + void addNotifyByComment(Integer bindID, MainType mainType, + CommentEntity fileCommentEntity); + void addNotifyByReply(Integer bindID, MainType mainType, + String replyInCommentID, CommentReplyEntity fileCommentReplyEntity); + void addNotifyByBehaviorRecord(UserBehaviorEntity userBehaviorEntity); } diff --git a/ocean-common/src/main/java/com/oriole/ocean/common/vo/OceanExceptionHandler.java b/ocean-common/src/main/java/com/oriole/ocean/common/vo/OceanExceptionHandler.java index 86fc9d4..f0c2bc9 100644 --- a/ocean-common/src/main/java/com/oriole/ocean/common/vo/OceanExceptionHandler.java +++ b/ocean-common/src/main/java/com/oriole/ocean/common/vo/OceanExceptionHandler.java @@ -7,7 +7,7 @@ import java.util.Arrays; import java.util.Date; -@Slf4j + @Slf4j public class OceanExceptionHandler { @Data diff --git a/ocean-docs-file-service/pom.xml b/ocean-docs-file-service/pom.xml index 2ee87f3..e7aa5fe 100644 --- a/ocean-docs-file-service/pom.xml +++ b/ocean-docs-file-service/pom.xml @@ -148,5 +148,16 @@ org.apache.commons commons-lang3 + + + org.jodconverter + jodconverter-local + 4.4.8 + + + org.jodconverter + jodconverter-core + 4.4.8 + diff --git a/ocean-docs-file-service/src/main/java/com/oriole/ocean/utils/OpenOfficeUtils.java b/ocean-docs-file-service/src/main/java/com/oriole/ocean/utils/OpenOfficeUtils.java index 3d35aa4..9fe1022 100644 --- a/ocean-docs-file-service/src/main/java/com/oriole/ocean/utils/OpenOfficeUtils.java +++ b/ocean-docs-file-service/src/main/java/com/oriole/ocean/utils/OpenOfficeUtils.java @@ -1,9 +1,9 @@ package com.oriole.ocean.utils; -import org.jodconverter.OfficeDocumentConverter; -import org.jodconverter.office.DefaultOfficeManagerBuilder; -import org.jodconverter.office.OfficeException; -import org.jodconverter.office.OfficeManager; +import org.jodconverter.local.LocalConverter; +import org.jodconverter.local.office.LocalOfficeManager; +import org.jodconverter.core.office.OfficeException; +import org.jodconverter.core.office.OfficeManager; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -33,13 +33,14 @@ public OpenOfficeUtils(Boolean useOpenOffice) { * @throws OfficeException */ private OfficeManager getOfficeManager() throws OfficeException { - DefaultOfficeManagerBuilder builder = new DefaultOfficeManagerBuilder(); + LocalOfficeManager.Builder builder = LocalOfficeManager.builder(); //此处填写OpenOffice安装路径 if (useOpenOffice) { - builder.setOfficeHome(OPENOFFICE_PATH); + builder.officeHome(OPENOFFICE_PATH); } else { - builder.setOfficeHome(LIBREOFFICE_PATH); + builder.officeHome(LIBREOFFICE_PATH); } + builder.install(); OfficeManager officeManager = builder.build(); //officeManager提供了开启OpenOffice的API服务 officeManager.start(); @@ -57,8 +58,8 @@ public Boolean openOfficeExperience(String path, String fileName, String suffix, //设置转换后的文件存储路径,文件名 //使用OfficeDocumentConverter类转换文件,其实核心就这一句 - OfficeDocumentConverter converter = new OfficeDocumentConverter(manage); - converter.convert(inputTempFile, outputTempFile); + LocalConverter converter = LocalConverter.make(manage); + converter.convert(inputTempFile).to(outputTempFile).execute(); return true; } catch (Exception e) { e.printStackTrace(); diff --git a/ocean-docs-service/src/main/java/com/oriole/ocean/controller/DocInfoController.java b/ocean-docs-service/src/main/java/com/oriole/ocean/controller/DocInfoController.java index cc17eec..fe63f52 100644 --- a/ocean-docs-service/src/main/java/com/oriole/ocean/controller/DocInfoController.java +++ b/ocean-docs-service/src/main/java/com/oriole/ocean/controller/DocInfoController.java @@ -15,18 +15,25 @@ import com.oriole.ocean.common.vo.MsgEntity; import com.oriole.ocean.service.FileCheckServiceImpl; import com.oriole.ocean.service.FileServiceImpl; +import com.oriole.ocean.model.FileInfo; +import com.oriole.ocean.service.DocInfoService; import org.apache.dubbo.config.annotation.DubboReference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; +import java.util.Map; import static com.oriole.ocean.common.enumerate.ResultCode.*; +/** + * Controller for handling document information related requests. + */ @RestController //@Slf4j @RequestMapping("/docInfoService") @@ -41,6 +48,9 @@ public class DocInfoController { @Autowired FileCheckServiceImpl fileCheckService; + @Autowired + private DocInfoService docInfoService; + @RequestMapping(value = "/getFileList", method = RequestMethod.GET) public MsgEntity> getFileList(@AuthUser AuthUserEntity authUser, @RequestParam(required = false) String username, @@ -185,4 +195,24 @@ public MsgEntity submitFolderInfo(@AuthUser String username, @RequestPar fileService.saveOrUpdateFileInfo(new FileEntity(title, abstractContent, username, folderID)); return new MsgEntity<>(SUCCESS); } + + /** + * Retrieves a paginated list of files owned by the current user. + * + * @param authUser Current authenticated user + * @param isFolder Whether to fetch folders (false for files) + * @param pageNum Page number for pagination + * @param pageSize Number of items per page + * @return MsgEntity containing the list of files and pagination info + */ + @GetMapping("/getMyFileList") + public MsgEntity> getMyFileList( + @AuthUser AuthUserEntity authUser, + @RequestParam boolean isFolder, + @RequestParam int pageNum, + @RequestParam int pageSize) { + + Map result = docInfoService.getMyFileList(authUser.getUsername(), isFolder, pageNum, pageSize); + return new MsgEntity<>("SUCCESS", "1", result); + } } diff --git a/ocean-docs-service/src/main/java/com/oriole/ocean/dao/FileInfoDao.java b/ocean-docs-service/src/main/java/com/oriole/ocean/dao/FileInfoDao.java new file mode 100644 index 0000000..3af9179 --- /dev/null +++ b/ocean-docs-service/src/main/java/com/oriole/ocean/dao/FileInfoDao.java @@ -0,0 +1,49 @@ +package com.oriole.ocean.dao; + +import com.oriole.ocean.model.FileInfo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * Data Access Object for file information operations. + */ +@Mapper +public interface FileInfoDao { + + /** + * Counts the total number of files owned by the current user. + * + * @param username Username of the current user + * @param isFolder Whether to count folders (false for files) + * @return Total count of files + */ + @Select("SELECT COUNT(*) FROM file f " + + "WHERE f.upload_username = #{username} " + + "AND ((#{isFolder} = true AND f.file_id IN (SELECT folder_id FROM file WHERE folder_id IS NOT NULL)) " + + "OR (#{isFolder} = false AND f.file_id NOT IN (SELECT folder_id FROM file WHERE folder_id IS NOT NULL)))") + int countMyFiles(@Param("username") String username, @Param("isFolder") boolean isFolder); + + /** + * Retrieves a paginated list of files owned by the current user. + * + * @param username Username of the current user + * @param isFolder Whether to fetch folders (false for files) + * @param offset Offset for pagination + * @param limit Number of items to fetch + * @return List of file information + */ + @Select("SELECT f.*, fe.* FROM file f " + + "LEFT JOIN file_extra fe ON f.file_id = fe.file_id " + + "WHERE f.upload_username = #{username} " + + "AND ((#{isFolder} = true AND f.file_id IN (SELECT folder_id FROM file WHERE folder_id IS NOT NULL)) " + + "OR (#{isFolder} = false AND f.file_id NOT IN (SELECT folder_id FROM file WHERE folder_id IS NOT NULL))) " + + "ORDER BY f.upload_date DESC " + + "LIMIT #{offset}, #{limit}") + List getMyFiles(@Param("username") String username, + @Param("isFolder") boolean isFolder, + @Param("offset") int offset, + @Param("limit") int limit); +} \ No newline at end of file diff --git a/ocean-docs-service/src/main/java/com/oriole/ocean/model/FileExtraEntity.java b/ocean-docs-service/src/main/java/com/oriole/ocean/model/FileExtraEntity.java new file mode 100644 index 0000000..430988c --- /dev/null +++ b/ocean-docs-service/src/main/java/com/oriole/ocean/model/FileExtraEntity.java @@ -0,0 +1,19 @@ +package com.oriole.ocean.model; + +import lombok.Data; + +/** + * Represents additional statistics and metadata for a file. + */ +@Data +public class FileExtraEntity { + private Integer readNum; + private Double score; + private Integer ratersNum; + private Boolean isVipIncome; + private Integer downloadNum; + private Integer likeNum; + private Integer collectionNum; + private Integer commentNum; + private Integer isProCert; +} \ No newline at end of file diff --git a/ocean-docs-service/src/main/java/com/oriole/ocean/model/FileInfo.java b/ocean-docs-service/src/main/java/com/oriole/ocean/model/FileInfo.java new file mode 100644 index 0000000..f146e56 --- /dev/null +++ b/ocean-docs-service/src/main/java/com/oriole/ocean/model/FileInfo.java @@ -0,0 +1,35 @@ +package com.oriole.ocean.model; + +import lombok.Data; +import java.util.List; + +/** + * Represents a file's information in the system. + */ +@Data +public class FileInfo { + private Integer fileId; + private String title; + private String abstractContent; + private String fileType; + private String previewPictureObjectName; + private String uploadDate; + private Integer paymentMethod; + private Integer paymentAmount; + private List tagNames; + private FileExtraEntity fileExtraEntity; + + // Additional fields from file table + private Integer size; + private Integer folderId; + private String previewPdfObjectName; + private String uploadUsername; + private String realObjectName; + private Double hideScore; + private Boolean isApproved; + private Boolean isAllowAnon; + private Boolean isAllowVipfree; + private Boolean isAllowComment; + private String indexString; + private Integer typeId; +} \ No newline at end of file diff --git a/ocean-docs-service/src/main/java/com/oriole/ocean/service/DocInfoService.java b/ocean-docs-service/src/main/java/com/oriole/ocean/service/DocInfoService.java new file mode 100644 index 0000000..e0e0cb0 --- /dev/null +++ b/ocean-docs-service/src/main/java/com/oriole/ocean/service/DocInfoService.java @@ -0,0 +1,19 @@ +package com.oriole.ocean.service; + +import java.util.Map; + +/** + * Service interface for document information operations. + */ +public interface DocInfoService { + /** + * Retrieves a paginated list of files owned by the current user. + * + * @param username Username of the current user + * @param isFolder Whether to fetch folders (false for files) + * @param pageNum Page number for pagination + * @param pageSize Number of items per page + * @return Map containing the list of files and pagination info + */ + Map getMyFileList(String username, boolean isFolder, int pageNum, int pageSize); +} \ No newline at end of file diff --git a/ocean-docs-service/src/main/java/com/oriole/ocean/service/impl/DocInfoServiceImpl.java b/ocean-docs-service/src/main/java/com/oriole/ocean/service/impl/DocInfoServiceImpl.java new file mode 100644 index 0000000..9b8a1ab --- /dev/null +++ b/ocean-docs-service/src/main/java/com/oriole/ocean/service/impl/DocInfoServiceImpl.java @@ -0,0 +1,38 @@ +package com.oriole.ocean.service.impl; + +import com.oriole.ocean.dao.FileInfoDao; +import com.oriole.ocean.model.FileInfo; +import com.oriole.ocean.service.DocInfoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Implementation of the document information service. + */ +@Service +public class DocInfoServiceImpl implements DocInfoService { + + @Autowired + private FileInfoDao fileInfoDao; + + @Override + public Map getMyFileList(String username, boolean isFolder, int pageNum, int pageSize) { + // Calculate offset for pagination + int offset = (pageNum - 1) * pageSize; + + // Get total count and list of files + int totalCount = fileInfoDao.countMyFiles(username, isFolder); + List files = fileInfoDao.getMyFiles(username, isFolder, offset, pageSize); + + // Prepare response + Map result = new HashMap<>(); + result.put("list", files); + result.put("isLastPage", (offset + pageSize) >= totalCount); + + return result; + } +} \ No newline at end of file diff --git a/ocean-gateway/src/main/java/com/oriole/ocean/gateway/config/WebSecurityConfig.java b/ocean-gateway/src/main/java/com/oriole/ocean/gateway/config/WebSecurityConfig.java index 0363479..271a84e 100644 --- a/ocean-gateway/src/main/java/com/oriole/ocean/gateway/config/WebSecurityConfig.java +++ b/ocean-gateway/src/main/java/com/oriole/ocean/gateway/config/WebSecurityConfig.java @@ -37,7 +37,7 @@ public class WebSecurityConfig { "/userAuth/login", //login接口是可以匿名访问的 "/userAuth/**Login", //login接口是可以匿名访问的 "/thirdPartLogin/**", //thirdPartLogin三方登录接口是可以匿名访问的 - "/userInfoService/reg", //reg接口是可以匿名访问的 + "/userAuth/reg", //reg接口是可以匿名访问的 "/docInfoService/getFileInfoByFileIDWithAnon", //这个接口在文件不允许匿名访问时返回错误信息 diff --git a/ocean-gateway/src/main/java/com/oriole/ocean/gateway/filter/UserAuthGatewayFilterFactory.java b/ocean-gateway/src/main/java/com/oriole/ocean/gateway/filter/UserAuthGatewayFilterFactory.java index 9ca3737..3b9e584 100644 --- a/ocean-gateway/src/main/java/com/oriole/ocean/gateway/filter/UserAuthGatewayFilterFactory.java +++ b/ocean-gateway/src/main/java/com/oriole/ocean/gateway/filter/UserAuthGatewayFilterFactory.java @@ -75,20 +75,25 @@ public Mono writeWith(Publisher body) { JSONObject loginResponseObject = JSONObject.parseObject(responseData); if(loginResponseObject.get("state").equals("SUCCESS") && loginResponseObject.get("code").equals("1")){ - JSONObject loginInfo = loginResponseObject.getJSONObject("msg"); + Object msgObject = loginResponseObject.get("msg"); + if (msgObject instanceof JSONObject) { + JSONObject loginInfo = (JSONObject) msgObject; - Map chaim = new HashMap<>(); - chaim.put("username", loginInfo.getString("username")); - chaim.put("role", loginInfo.getString("role")); + Map chaim = new HashMap<>(); + chaim.put("username", loginInfo.getString("username")); + chaim.put("role", loginInfo.getString("role")); - String token = jwtUtils.encode(loginInfo.getString("username"), 24 * 60 * 60 * 1000, chaim); + String token = jwtUtils.encode(loginInfo.getString("username"), 24 * 60 * 60 * 1000, chaim); - loginResponseObject.put("msg", token); + loginResponseObject.put("msg", token); - byte[] uppedContent = loginResponseObject.toString().getBytes(); - originalResponse.getHeaders().set(HttpHeaders.AUTHORIZATION, token); - originalResponse.getHeaders().setContentLength(uppedContent.length); - return Mono.just(bufferFactory.wrap(uppedContent)); + byte[] uppedContent = loginResponseObject.toString().getBytes(); + originalResponse.getHeaders().set(HttpHeaders.AUTHORIZATION, token); + originalResponse.getHeaders().setContentLength(uppedContent.length); + return Mono.just(bufferFactory.wrap(uppedContent)); + } else { + return Mono.just(bufferFactory.wrap(bytes)); + } }else { originalResponse.setStatusCode(HttpStatus.UNAUTHORIZED); return Mono.just(bufferFactory.wrap(bytes)); diff --git a/ocean-gateway/src/main/java/com/oriole/ocean/gateway/security/SecurityRepository.java b/ocean-gateway/src/main/java/com/oriole/ocean/gateway/security/SecurityRepository.java index 8ad8c33..67585e1 100644 --- a/ocean-gateway/src/main/java/com/oriole/ocean/gateway/security/SecurityRepository.java +++ b/ocean-gateway/src/main/java/com/oriole/ocean/gateway/security/SecurityRepository.java @@ -45,6 +45,11 @@ public Mono load(ServerWebExchange exchange) { authInfo.put("username", claims.get("username")); authInfo.put("role", claims.get("role")); + // 打印authInfo + System.out.println("load"); + System.out.println(exchange.getRequest().getPath()); + System.out.println(authInfo); + /* 重设头信息 */ exchange.getRequest().mutate().headers(httpHeaders -> httpHeaders.set(HttpHeaders.AUTHORIZATION, authInfo.toString()) diff --git a/ocean-gateway/src/main/resources/application.yaml b/ocean-gateway/src/main/resources/application.yaml index f2ddba2..1a1d7e9 100644 --- a/ocean-gateway/src/main/resources/application.yaml +++ b/ocean-gateway/src/main/resources/application.yaml @@ -17,7 +17,7 @@ spring: - id: login uri: lb://ocean-user-service predicates: - - Path=/userAuth/login + - Path=/userAuth/login,/userAuth/reg filters: - UserAuth - id: user # 路由规则id,自定义,唯一 @@ -40,6 +40,10 @@ spring: uri: lb://ocean-notify-service predicates: - Path=/notify/** + - id: note + uri: lb://ocean-note-service + predicates: + - Path=/noteService/** auth: login: token: diff --git a/ocean-note-service/pom.xml b/ocean-note-service/pom.xml index bb3fea3..2627922 100644 --- a/ocean-note-service/pom.xml +++ b/ocean-note-service/pom.xml @@ -114,5 +114,11 @@ 1.0-SNAPSHOT compile + + + org.apache.commons + commons-lang3 + 3.12.0 + diff --git a/ocean-note-service/src/main/java/com/oriole/ocean/config/WebConfig.java b/ocean-note-service/src/main/java/com/oriole/ocean/config/WebConfig.java new file mode 100644 index 0000000..be05ee6 --- /dev/null +++ b/ocean-note-service/src/main/java/com/oriole/ocean/config/WebConfig.java @@ -0,0 +1,21 @@ +package com.oriole.ocean.config; + +import com.oriole.ocean.common.auth.AuthUserMethodArgumentResolver; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Autowired + AuthUserMethodArgumentResolver authUserMethodArgumentResolver; + + @Override + public void addArgumentResolvers(List argumentResolvers) { + argumentResolvers.add(authUserMethodArgumentResolver); + } +} \ No newline at end of file diff --git a/ocean-note-service/src/main/java/com/oriole/ocean/controller/NoteController.java b/ocean-note-service/src/main/java/com/oriole/ocean/controller/NoteController.java index a129929..35701bb 100644 --- a/ocean-note-service/src/main/java/com/oriole/ocean/controller/NoteController.java +++ b/ocean-note-service/src/main/java/com/oriole/ocean/controller/NoteController.java @@ -2,39 +2,424 @@ import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; -import com.oriole.ocean.common.po.mysql.NoteEntity; -import com.oriole.ocean.common.po.mysql.NoteTypeEntity; +import com.oriole.ocean.common.auth.AuthUser; +import com.oriole.ocean.common.enumerate.MainType; +import com.oriole.ocean.common.enumerate.NotifyAction; +import com.oriole.ocean.common.enumerate.NotifySubscriptionTargetType; +import com.oriole.ocean.common.enumerate.NotifyType; +import com.oriole.ocean.common.po.mongo.FavorEntity; +import com.oriole.ocean.common.po.mysql.*; +import com.oriole.ocean.common.service.*; +import com.oriole.ocean.common.vo.AuthUserEntity; import com.oriole.ocean.common.vo.MsgEntity; -import com.oriole.ocean.service.NoteService; -import com.oriole.ocean.service.NoteTypeService; +import org.apache.dubbo.config.annotation.DubboReference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; +/** + * Note controller + * Handles HTTP requests for note operations including like functionality + */ @RestController @RequestMapping("/noteService") public class NoteController { @Autowired NoteService noteService; - @Autowired - NoteTypeService noteTypeService; + NoteCommentService noteCommentService; + @DubboReference + UserBehaviorService userBehaviorService; + @DubboReference + NotifyService notifyService; + @DubboReference + NotifySubscriptionService notifySubscriptionService; + + /** + * Get latest notes with pagination + * Returns notes with like status for authenticated user + */ + @RequestMapping(value = "/getLatestNote", method = RequestMethod.POST) + public MsgEntity> getNoteByPage( + @AuthUser AuthUserEntity authUser, + @RequestParam int pageNO, + @RequestParam int pageSize) { + PageHelper.startPage(pageNO, pageSize, true); + + List noteEntityList; + if (authUser != null) { + // Authenticated user - include like status + noteEntityList = noteService.getLatestNotesWithLikeStatus(authUser.getUsername()); + } else { + // Anonymous user - without like status + noteEntityList = noteService.getLatestNotes(); + } + + PageInfo pageInfo = new PageInfo<>(noteEntityList); + return new MsgEntity<>("SUCCESS","1",pageInfo); + } + + /** + * Search notes by keywords with pagination + * Returns notes with like status for authenticated user + */ + @RequestMapping(value = "/getNotesByKeywords", method = RequestMethod.POST) + public MsgEntity> getNotesByKeywords( + @AuthUser AuthUserEntity authUser, + @RequestParam String searchString, + @RequestParam int pageNO, + @RequestParam int pageSize) { + PageHelper.startPage(pageNO, pageSize, true); + + List noteEntityList; + if (authUser != null) { + // Authenticated user - include like status + noteEntityList = noteService.getNotesByKeywordsWithLikeStatus(searchString, authUser.getUsername()); + } else { + // Anonymous user - without like status + noteEntityList = noteService.getNotesByKeywords(searchString); + } + + PageInfo pageInfo = new PageInfo<>(noteEntityList); + return new MsgEntity<>("SUCCESS","1",pageInfo); + } + + /** + * Select notes by creator name with pagination + * Returns notes with like status for authenticated user + */ + @RequestMapping(value = "/getMyNotes", method = RequestMethod.POST) + public MsgEntity> getMyNotes( + @AuthUser AuthUserEntity authUser, + @RequestParam int pageNO, + @RequestParam int pageSize) { + PageHelper.startPage(pageNO, pageSize, true); + List noteEntityList = noteService.getNotesByUsernameWithLikeStatus(authUser.getUsername()); + PageInfo pageInfo = new PageInfo<>(noteEntityList); + return new MsgEntity<>("SUCCESS","1",pageInfo); + } + + /** + * Get note by ID + * Returns note with like status for authenticated user + */ + @RequestMapping(value = "/getNoteById", method = RequestMethod.POST) + public MsgEntity getNoteById( + @AuthUser AuthUserEntity authUser, + @RequestParam String noteId + ) { + NoteEntity note; + if (authUser != null) { + // Authenticated user - include like status + note = noteService.getNoteByIdWithLikeStatus(noteId, authUser.getUsername()); + } else { + // Anonymous user - without like status + note = noteService.getNoteById(noteId); + } + + return new MsgEntity<>("SUCCESS", "1", note); + } + + /** + * Create a new note + */ + @RequestMapping(value = "/createNote", method = RequestMethod.POST) + public MsgEntity createNote( + @AuthUser AuthUserEntity authUser, + @RequestParam String content, + @RequestParam String tag, + @RequestParam Byte isAnon, + @RequestParam Byte isAllowComment) { + String buildUsername = authUser.getUsername(); + + NoteEntity noteEntity = new NoteEntity(); + noteEntity.setBuildUsername(buildUsername); + noteEntity.setContent(content); + noteEntity.setTag(tag); + noteEntity.setIsAnon(isAnon); + noteEntity.setIsAllowComment(isAllowComment); + NoteEntity note = noteService.createNote(noteEntity); + + // 增加用户消息订阅事件:用户需要订阅自己发布的评论或回复的动态 + List notifyActionList = new ArrayList<>(); + notifyActionList.add(NotifyAction.LIKE_COMMENT); + notifyActionList.add(NotifyAction.NEW_COMMENT); + notifySubscriptionService.setNotifySubscription(buildUsername, notifyActionList, + note.getId(), NotifySubscriptionTargetType.COMMENT); + + return new MsgEntity<>("SUCCESS", "1", note); + } + + /** + * Delete a note + */ + @RequestMapping(value = "/deleteNote", method = RequestMethod.POST) + public MsgEntity deleteNote( + @AuthUser AuthUserEntity authUser, + @RequestParam String noteID) { + if(!noteService.isNoteCreator(noteID, authUser.getUsername())) { + if(authUser.isAdmin()) { + //TODO: Record administrator operation + } else { + return new MsgEntity<>("FAILED", "400", "Unauthorized Operation"); + } + } + noteService.deleteNote(noteID); + return new MsgEntity<>("SUCCESS", "1", "Note deleted successfully"); + } - @RequestMapping(value = "/getAllNoteType",method = RequestMethod.GET) - public MsgEntity> getAllNoteType() { - return new MsgEntity<>("SUCCESS", "1", noteTypeService.getAllNoteType()); + @RequestMapping(value = "/canDeleteNote", method = RequestMethod.POST) + public MsgEntity canDeleteNote( + @AuthUser AuthUserEntity authUser, + @RequestParam String noteID) { + if(!noteService.isNoteCreator(noteID, authUser.getUsername()) && !authUser.isAdmin()) + return new MsgEntity<>("SUCCESS", "1", "false"); + return new MsgEntity<>("SUCCESS", "1", "true"); } - @RequestMapping(value = "/getNoteByNoteType",method = RequestMethod.GET) - public MsgEntity> getNoteByNoteType(@RequestParam(required = false) Integer noteType,@RequestParam Integer pageNum, @RequestParam Integer pageSize) { - PageHelper.startPage(pageNum, pageSize, true); - List noteEntityList = noteService.getNoteByNoteType(noteType); + /** + * Get notes by tag with pagination + * Returns notes with like status for authenticated user + */ + @RequestMapping(value = "/getNoteByTag",method = RequestMethod.GET) + public MsgEntity> getNoteByTag( + @AuthUser AuthUserEntity authUser, + @RequestParam(required = false) String tag, + @RequestParam Integer pageNo, + @RequestParam Integer pageSize) { + PageHelper.startPage(pageNo, pageSize, true); + + List noteEntityList; + if (authUser != null) { + // Authenticated user - include like status + noteEntityList = noteService.getNotesByTagWithLikeStatus(tag, authUser.getUsername()); + } else { + // Anonymous user - without like status + noteEntityList = noteService.getNotesByTag(tag); + } + PageInfo pageInfo = new PageInfo<>(noteEntityList); return new MsgEntity<>("SUCCESS", "1", pageInfo); } + + // === Like functionality APIs === + + /** + * Like or unlike a note + * @param authUser Authenticated user + * @param noteId Note ID to like/unlike + * @param isLike true to like, false to unlike + * @return Success message with like status + */ + @RequestMapping(value = "/likeNote", method = RequestMethod.POST) + public MsgEntity likeNote( + @AuthUser AuthUserEntity authUser, + @RequestParam String noteId, + @RequestParam Boolean isLike) { + + String username = authUser.getUsername(); + + NoteLikeEntity result = noteService.likeNote(username, noteId, isLike); + + if (isLike) return new MsgEntity<>("SUCCESS", "1", result); + else return new MsgEntity<>("SUCCESS", "1", null); + } + + @RequestMapping (value = "/readNote", method = RequestMethod.POST) + public MsgEntity readNote( + @AuthUser AuthUserEntity authUser, + @RequestParam String noteId) { + + String username = authUser.getUsername(); + + noteService.readNote(noteId); + + return new MsgEntity<>("SUCCESS", "1", "Note read successfully"); + } + + /** + * Check if current user has liked a specific note + * @param authUser Authenticated user + * @param noteId Note ID to check + * @return Like status + */ + @RequestMapping(value = "/checkNoteLikeStatus", method = RequestMethod.POST) + public MsgEntity checkNoteLikeStatus( + @AuthUser AuthUserEntity authUser, + @RequestParam String noteId) { + + String username = authUser.getUsername(); + boolean hasLiked = noteService.hasUserLikedNote(username, noteId); + + return new MsgEntity<>("SUCCESS", "1", hasLiked); + } + + /** + * Like or unlike a comment + * @param authUser Authenticated user + * @param commentId Comment ID to like/unlike + * @param isLike true to like, false to unlike + * @return Success message with like status + */ + @RequestMapping(value = "/likeNoteComment", method = RequestMethod.POST) + public MsgEntity likeNoteComment( + @AuthUser AuthUserEntity authUser, + @RequestParam String commentId, + @RequestParam Boolean isLike) { + + String username = authUser.getUsername(); + + NoteCommentLikeEntity result = noteCommentService.likeNoteComment(username, commentId, isLike); + + if (isLike) { + return new MsgEntity<>("SUCCESS", "1", result); + } else { + return new MsgEntity<>("SUCCESS", "1", null); + } + } + + /** + * Check if the current user has liked a specific comment + * @param authUser Authenticated user + * @param commentId Comment ID to check + * @return Like status + */ + @RequestMapping(value = "/checkNoteCommentLikeStatus", method = RequestMethod.POST) + public MsgEntity checkNoteCommentLikeStatus( + @AuthUser AuthUserEntity authUser, + @RequestParam String commentId) { + + String username = authUser.getUsername(); + boolean hasLiked = noteCommentService.hasUserLikedNoteComment(username, commentId); + + return new MsgEntity<>("SUCCESS", "1", hasLiked); + } + + // === Original comment and favorite APIs === + + /** + * Get note comments by note ID + */ + @RequestMapping(value = "/getNoteCommentByNoteId", method = RequestMethod.POST) + public MsgEntity> getNoteCommentByNoteId( + @AuthUser AuthUserEntity authUser, + @RequestParam String noteId, + @RequestParam Integer pageNo, + @RequestParam Integer pageSize) { + PageHelper.startPage(pageNo, pageSize, true); + List noteCommentEntityList = noteCommentService.getNoteCommentsByNoteIdWithLikeStatus(noteId, authUser.getUsername()); + PageInfo pageInfo = new PageInfo<>(noteCommentEntityList); + return new MsgEntity<>("SUCCESS", "1", pageInfo); + } + + @RequestMapping(value = "/getNoteCommentById", method = RequestMethod.POST) + public MsgEntity getNoteCommentById( + @AuthUser AuthUserEntity authUser, + @RequestParam String commentID) { + + return new MsgEntity<>("SUCCESS", "1", noteCommentService.getNoteComment(commentID)); + } + + /** + * Create a note comment + */ + @RequestMapping(value = "/createNoteComment", method = RequestMethod.POST) + public MsgEntity createNoteComment( + @AuthUser AuthUserEntity authUser, + @RequestParam String noteId, + @RequestParam String content, + @RequestParam String replyId, + @RequestParam String replyUsername + ) { + String userName = authUser.getUsername(); + + NoteCommentEntity noteCommentEntity = new NoteCommentEntity(noteId, userName, content, replyId, replyUsername); + + noteCommentEntity.setReplyId(replyId); + NoteCommentEntity noteComment = noteCommentService.createNoteComment(noteCommentEntity); + + //构建用户消息事件 + NotifyEntity notifyEntity = new NotifyEntity(NotifyType.REMIND, userName); + notifyEntity.setTargetIdAndType(noteId, MainType.NOTE); + notifyEntity.setContent(content); + System.out.println(replyId); + System.out.println(noteId); + if (Objects.equals(replyId, noteId)) { // 直接回复帖子 + notifyEntity.setAction(NotifyAction.NEW_COMMENT);// 新评论事件不面向任何其他评论 + } else { + notifyEntity.setAction(NotifyAction.NEW_REPLY); + } + System.out.println(replyId); + notifyEntity.setCommentID(replyId); + // 增加用户消息订阅事件:用户需要订阅自己发布的评论或回复的动态 + List notifyActionList = new ArrayList<>(); + notifyActionList.add(NotifyAction.LIKE_COMMENT); + notifyActionList.add(NotifyAction.NEW_REPLY); + notifySubscriptionService.setNotifySubscription(userName, notifyActionList, + noteComment.getId(), NotifySubscriptionTargetType.COMMENT); + + // 产生用户消息事件 + notifyService.addNotify(notifyEntity); + + return new MsgEntity<>("SUCCESS", "1", noteComment); + } + + /** + * Delete a note comment + */ + @RequestMapping(value = "/deleteNoteComment", method = RequestMethod.POST) + public MsgEntity deleteNoteComment( + @AuthUser AuthUserEntity authUser, + @RequestParam String _id) { + if(noteCommentService.isCommentCreator(_id, authUser.getUsername())) { + noteCommentService.deleteNoteComment(_id); + return new MsgEntity<>("SUCCESS", "1", "评论删除成功"); + } + NoteCommentEntity noteComment = noteCommentService.getNoteComment(_id); + if(noteService.isNoteCreator(noteComment.getNoteId(), authUser.getUsername())) { + noteCommentService.deleteNoteComment(_id); + return new MsgEntity<>("SUCCESS", "1", "帖主删除评论成功"); + } + if(authUser.isAdmin()) { + noteCommentService.deleteNoteComment(_id); + return new MsgEntity<>("SUCCESS", "1", "管理员删除评论成功"); + } + return new MsgEntity<>("FAILED", "400", "删除评论失败"); + } + + // 收藏功能 + @RequestMapping(value = "/favoriteNote", method = RequestMethod.POST) + public MsgEntity favoriteNote( + @AuthUser AuthUserEntity authUser, + @RequestParam Boolean isFavor, + @RequestParam String noteId, + @RequestParam(required = false) String id) { + return new MsgEntity<>("SUCCESS", "1", noteService.favoriteNote(authUser.getUsername(), isFavor, noteId, id)); + } + + @RequestMapping(value = "/getBehaviourByUsernameAndNoteId", method = RequestMethod.POST) + public MsgEntity getBehaviourByUsernameAndNoteId( + @AuthUser AuthUserEntity authUser, + @RequestParam String noteId) { + FavorEntity favorEntity = noteService.getBehaviourByUsernameAndNoteId(authUser.getUsername(), noteId); + if (favorEntity == null) return new MsgEntity<>("SUCCESS", "2", null); + return new MsgEntity<>("SUCCESS", "1", favorEntity); + } + + @RequestMapping(value = "/getBehaviourByUsername", method = RequestMethod.POST) + public MsgEntity> getBehaviourByUsername( + @AuthUser AuthUserEntity authUser, + @RequestParam Integer pageNo, + @RequestParam Integer pageSize) { + List collectionList = noteService.getBehaviourByUsername(authUser.getUsername(), pageNo, pageSize); + PageInfo pageInfo = new PageInfo<>(collectionList); + return new MsgEntity<>("SUCCESS", "1", pageInfo); + } } diff --git a/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteCollectionDao.java b/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteCollectionDao.java new file mode 100644 index 0000000..e250447 --- /dev/null +++ b/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteCollectionDao.java @@ -0,0 +1,52 @@ +package com.oriole.ocean.dao; + +import com.oriole.ocean.common.po.mongo.FavorEntity; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public class NoteCollectionDao { + @Autowired + private MongoTemplate mongoTemplate; + + // 根据用户名和笔记ID查找收藏(用于删除时获取完整对象) + public FavorEntity findByUsernameAndNoteId(String username, String noteId) { + Criteria criteria = new Criteria().orOperator( + Criteria.where("username").is(username), + Criteria.where("noteId").is(noteId) + ); + + Query query = new Query(criteria); + + List res = mongoTemplate.find(query, FavorEntity.class, "note_favor"); + if (res.isEmpty()) return null; + return res.iterator().next(); + } + + public List getBehaviourByUsername(String username, int pageNo, int pageSize) { + Pageable pageable = PageRequest.of(pageNo - 1, pageSize); + Criteria criteria = new Criteria().orOperator( + Criteria.where("username").is(username) + ); + + Query query = new Query(criteria); + query.with(pageable); + + return mongoTemplate.find(query, FavorEntity.class, "note_favor"); + } + + public void save(FavorEntity favorEntity) { + mongoTemplate.save(favorEntity, "note_favor"); + } + + public void delete(FavorEntity favorEntity) { + mongoTemplate.remove(favorEntity, "note_favor"); + } +} diff --git a/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteCommentDao.java b/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteCommentDao.java new file mode 100644 index 0000000..6ba207a --- /dev/null +++ b/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteCommentDao.java @@ -0,0 +1,36 @@ +package com.oriole.ocean.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.oriole.ocean.common.po.mysql.NoteCommentEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * Note comment data access object + * Handles database operations for note comment records + */ +@Mapper +public interface NoteCommentDao extends BaseMapper { + + void addNoteComment(NoteCommentEntity noteComment); + boolean deleteNoteComment(String commentId); + + /** + * Get note comments by note ID without like status (for anonymous users) + * @param noteId Note ID + * @return List of note comments + */ + List getNoteCommentsByNoteId(@Param("noteId") String noteId); + + /** + * Get note comments by note ID with like status for authenticated user + * @param noteId Note ID + * @param username Current user's username + * @return List of note comments with like status + */ + List getNoteCommentsByNoteIdWithLikeStatus(@Param("noteId") String noteId, @Param("username") String username); + + NoteCommentEntity getNoteComment(String commentId); +} diff --git a/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteCommentLikeDao.java b/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteCommentLikeDao.java new file mode 100644 index 0000000..50ee734 --- /dev/null +++ b/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteCommentLikeDao.java @@ -0,0 +1,45 @@ +package com.oriole.ocean.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.oriole.ocean.common.po.mysql.NoteCommentLikeEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * Note like data access object + * Handles database operations for comment like records + */ +@Mapper +public interface NoteCommentLikeDao extends BaseMapper { + + /** + * Add a like record + * @param noteCommentLikeEntity The like record to add + * @return Number of affected rows + */ + int addNoteCommentLike(NoteCommentLikeEntity noteCommentLikeEntity); + + /** + * Remove a like record + * @param username The username who likes the comment + * @param commentId The ID of the comment + * @return Number of affected rows + */ + int removeNoteCommentLike(@Param("username") String username, @Param("commentId") String commentId); + + /** + * Check if a user has liked a specific comment + * @param username The username to check + * @param commentId The ID of the comment + * @return The like record if exists, null otherwise + */ + NoteCommentLikeEntity findNoteCommentLike(@Param("username") String username, @Param("commentId") String commentId); + + /** + * Check if a user has liked a specific comment (returns boolean) + * @param username The username to check + * @param commentId The ID of the comment + * @return true if liked, false otherwise + */ + boolean hasUserLikedNoteComment(@Param("username") String username, @Param("commentId") String commentId); +} \ No newline at end of file diff --git a/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteDao.java b/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteDao.java index 4bb9739..0bbc47f 100644 --- a/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteDao.java +++ b/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteDao.java @@ -1,7 +1,104 @@ package com.oriole.ocean.dao; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.oriole.ocean.common.po.mysql.NoteCommentEntity; import com.oriole.ocean.common.po.mysql.NoteEntity; +import com.oriole.ocean.common.po.mysql.UserNotifyEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import java.util.Date; +import java.util.List; + +/** + * Note data access object + * Handles database operations for note records + */ +@Mapper public interface NoteDao extends BaseMapper { + + void readNote(String noteId); + + void changeNoteCommentNum(String noteId, int value); + + /** + * Get latest notes + * @return List of latest notes + */ + List getLatestNotes(); + + /** + * Get latest notes with like status for current user + * @param username Current user's username + * @return List of latest notes with like status + */ + List getLatestNotesWithLikeStatus(@Param("username") String username); + + /** + * Get notes by keywords + * @param searchString Search keywords + * @return List of notes matching keywords + */ + List getNotesByKeywords(@Param("searchString") String searchString); + + /** + * Get notes by keywords with like status for current user + * @param searchString Search keywords + * @param username Current user's username + * @return List of notes matching keywords with like status + */ + List getNotesByKeywordsWithLikeStatus(@Param("searchString") String searchString, + @Param("username") String username); + + /** + * Get notes by tag + * @param tag Note tag + * @return List of notes with specified tag + */ + List getNotesByTag(@Param("tag") String tag); + + /** + * Get notes by tag with like status for current user + * @param tag Note tag + * @param username Current user's username + * @return List of notes with specified tag and like status + */ + List getNotesByTagWithLikeStatus(@Param("tag") String tag, + @Param("username") String username); + + /** + * Get notes by creator name + * @param username Current user's name + * @return list of notes created by that user + */ + List getNotesByMyNameWithLikeStatus(@Param("username") String username); + + /** + * Get note by ID + * @param noteId Note ID + * @return Note entity + */ + NoteEntity getNoteById(@Param("noteId") String noteId); + + /** + * Get note by ID with like status for current user + * @param noteId Note ID + * @param username Current user's username + * @return Note entity with like status + */ + NoteEntity getNoteByIdWithLikeStatus(@Param("noteId") String noteId, + @Param("username") String username); + + /** + * Delete note (soft delete) + * @param noteID Note ID to delete + */ + void deleteNote(@Param("noteID") String noteID); + + /** + * Get note comments by note ID + * @param noteId Note ID + * @return List of note comments + */ + List getNoteCommentsByNoteId(@Param("noteId") String noteId); } diff --git a/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteLikeDao.java b/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteLikeDao.java new file mode 100644 index 0000000..a44e143 --- /dev/null +++ b/ocean-note-service/src/main/java/com/oriole/ocean/dao/NoteLikeDao.java @@ -0,0 +1,45 @@ +package com.oriole.ocean.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.oriole.ocean.common.po.mysql.NoteLikeEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * Note like data access object + * Handles database operations for note like records + */ +@Mapper +public interface NoteLikeDao extends BaseMapper { + + /** + * Add a like record + * @param noteLikeEntity The like record to add + * @return Number of affected rows + */ + int addNoteLike(NoteLikeEntity noteLikeEntity); + + /** + * Remove a like record + * @param username The username who likes the note + * @param noteId The ID of the note + * @return Number of affected rows + */ + int removeNoteLike(@Param("username") String username, @Param("noteId") String noteId); + + /** + * Check if a user has liked a specific note + * @param username The username to check + * @param noteId The ID of the note + * @return The like record if exists, null otherwise + */ + NoteLikeEntity findNoteLike(@Param("username") String username, @Param("noteId") String noteId); + + /** + * Check if a user has liked a specific note (returns boolean) + * @param username The username to check + * @param noteId The ID of the note + * @return true if liked, false otherwise + */ + boolean hasUserLikedNote(@Param("username") String username, @Param("noteId") String noteId); +} \ No newline at end of file diff --git a/ocean-note-service/src/main/java/com/oriole/ocean/service/NoteCommentServiceImpl.java b/ocean-note-service/src/main/java/com/oriole/ocean/service/NoteCommentServiceImpl.java new file mode 100644 index 0000000..82cc361 --- /dev/null +++ b/ocean-note-service/src/main/java/com/oriole/ocean/service/NoteCommentServiceImpl.java @@ -0,0 +1,145 @@ +package com.oriole.ocean.service; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.oriole.ocean.common.enumerate.NotifyAction; +import com.oriole.ocean.common.enumerate.NotifySubscriptionTargetType; +import com.oriole.ocean.common.po.mysql.NoteCommentEntity; +import com.oriole.ocean.common.po.mysql.NoteCommentLikeEntity; +import com.oriole.ocean.common.service.NoteCommentService; +import com.oriole.ocean.dao.*; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Note service implementation + * Implements business logic for note operations including like functionality + */ +@Service +@Transactional +public class NoteCommentServiceImpl extends ServiceImpl implements NoteCommentService { + + @Resource + private NoteCommentDao noteCommentDao; + + @Resource + private NoteCommentLikeDao noteCommentLikeDao; + + /** + * Check if user is the creator of a comment + * @param commentId Comment ID + * @param username Username to check + * @return true if user is creator, false otherwise + */ + public boolean isCommentCreator(String commentId, String username) { + NoteCommentEntity comment = noteCommentDao.getNoteComment(commentId); + return comment != null && comment.getBuildUsername().equals(username); + } + + /** + * Create a new note comment + * @param noteCommentEntity Note comment entity to create + * @return Created note comment entity + */ + public NoteCommentEntity createNoteComment(NoteCommentEntity noteCommentEntity) { + noteCommentEntity.setLikeNum(0); + noteCommentEntity.setBuildDate(new Date()); + + noteCommentDao.addNoteComment(noteCommentEntity); + + + return noteCommentEntity; + } + + /** + * Get note comments by note ID (for anonymous users) + * @param noteId Note ID + * @return List of note comments + */ + public List getNoteCommentsByNoteId(String noteId) { + return noteCommentDao.getNoteCommentsByNoteId(noteId); + } + + /** + * Get note comments by note ID with like status (for authenticated users) + * @param noteId Note ID + * @param username Current user's username + * @return List of note comments with like status + */ + public List getNoteCommentsByNoteIdWithLikeStatus(String noteId, String username) { + return noteCommentDao.getNoteCommentsByNoteIdWithLikeStatus(noteId, username); + } + + /** + * Get note comments by comment ID + * @param commentId comment ID + * @return note comment + */ + public NoteCommentEntity getNoteComment(String commentId) { + return noteCommentDao.getNoteComment(commentId); + } + + /** + * Delete a note comment + * @param commentId Comment ID + */ + public void deleteNoteComment(String commentId) { + noteCommentDao.deleteNoteComment(commentId); + } + + + // === Like functionality implementation === + + /** + * Like or unlike a note + * @param username Username who likes/unlikes + * @param commentId Note ID to like/unlike + * @param isLike true to like, false to unlike + * @return NoteLikeEntity if liked, null if unliked + */ + @Override + public NoteCommentLikeEntity likeNoteComment(String username, String commentId, boolean isLike) { + if (isLike) { + // Check if already liked + NoteCommentLikeEntity existingLike = noteCommentLikeDao.findNoteCommentLike(username, commentId); + if (existingLike != null) { + return existingLike; // Already liked, return existing record + } + + // Create new like record + NoteCommentLikeEntity noteCommentLikeEntity = new NoteCommentLikeEntity(username, commentId); + noteCommentLikeDao.addNoteCommentLike(noteCommentLikeEntity); + return null; + } else { + // Remove like record + noteCommentLikeDao.removeNoteCommentLike(username, commentId); + return null; + } + } + + /** + * Check if a user has liked a specific note + * @param username Username to check + * @param commentId Note ID to check + * @return true if liked, false otherwise + */ + @Override + public boolean hasUserLikedNoteComment(String username, String commentId) { + return noteCommentLikeDao.hasUserLikedNoteComment(username, commentId); + } + + /** + * Get like record for a user and note + * @param username Username + * @param commentId Note ID + * @return NoteLikeEntity if exists, null otherwise + */ + @Override + public NoteCommentLikeEntity getNoteCommentLike(String username, String commentId) { + return noteCommentLikeDao.findNoteCommentLike(username, commentId); + } +} \ No newline at end of file diff --git a/ocean-note-service/src/main/java/com/oriole/ocean/service/NoteService.java b/ocean-note-service/src/main/java/com/oriole/ocean/service/NoteService.java deleted file mode 100644 index 2ad3710..0000000 --- a/ocean-note-service/src/main/java/com/oriole/ocean/service/NoteService.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.oriole.ocean.service; - -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.oriole.ocean.dao.NoteDao; -import com.oriole.ocean.common.po.mysql.NoteEntity; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -@Service -@Transactional -public class NoteService extends ServiceImpl { - public List getNoteByNoteType(Integer noteType){ - QueryWrapper queryWrapper = new QueryWrapper<>(); - if(noteType!=null) { - queryWrapper.eq("note_type", noteType); - } - return list(queryWrapper); - } -} diff --git a/ocean-note-service/src/main/java/com/oriole/ocean/service/NoteServiceImpl.java b/ocean-note-service/src/main/java/com/oriole/ocean/service/NoteServiceImpl.java new file mode 100644 index 0000000..9c8703c --- /dev/null +++ b/ocean-note-service/src/main/java/com/oriole/ocean/service/NoteServiceImpl.java @@ -0,0 +1,263 @@ +package com.oriole.ocean.service; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.oriole.ocean.common.po.mongo.FavorEntity; +import com.oriole.ocean.common.po.mysql.NoteCommentEntity; +import com.oriole.ocean.common.po.mysql.NoteLikeEntity; +import com.oriole.ocean.common.service.NoteService; +import com.oriole.ocean.common.service.NotifyService; +import com.oriole.ocean.dao.NoteCollectionDao; +import com.oriole.ocean.dao.NoteDao; +import com.oriole.ocean.dao.NoteLikeDao; +import com.oriole.ocean.common.po.mysql.NoteEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.Date; +import java.util.List; + +/** + * Note service implementation + * Implements business logic for note operations including like functionality + */ +@Service +@Transactional +public class NoteServiceImpl extends ServiceImpl implements NoteService { + @Resource + private NoteDao noteDao; + + @Resource + private NoteCollectionDao noteCollectionDao; + + @Resource + private NoteLikeDao noteLikeDao; + + /** + * Check if user is the creator of a note + * @param noteId Note ID + * @param username Username to check + * @return true if user is creator, false otherwise + */ + public boolean isNoteCreator(String noteId, String username) { + System.out.println(noteId); + NoteEntity note = noteDao.getNoteById(noteId); + return note != null && note.getBuildUsername().equals(username); + } + + public void changeNoteCommentNum(String noteId, int value) { + noteDao.changeNoteCommentNum(noteId, value); + } + + /** + * Delete a note (soft delete) + * @param noteID Note ID to delete + */ + public void deleteNote(String noteID){ + noteDao.deleteNote(noteID); + } + + /** + * Get note by ID + * @param noteID Note ID + * @return Note entity + */ + public NoteEntity getNoteById(String noteID) { + return noteDao.getNoteById(noteID); + } + + /** + * Get note by ID with like status for current user + * @param noteID Note ID + * @param username Current user's username + * @return Note entity with like status + */ + public NoteEntity getNoteByIdWithLikeStatus(String noteID, String username) { + return noteDao.getNoteByIdWithLikeStatus(noteID, username); + } + + /** + * Favorite/unfavorite a note + * @param username Username + * @param isFavor Whether to favor or unfavor + * @param noteID Note ID + * @param id Existing favor record ID (for unfavor operation) + * @return Favor entity or null + */ + public FavorEntity favoriteNote(String username, boolean isFavor, String noteID, String id){ + if (isFavor) { + FavorEntity favorEntity = new FavorEntity(); + favorEntity.setUsername(username); + favorEntity.setNoteId(noteID); + noteCollectionDao.save(favorEntity); + return noteCollectionDao.findByUsernameAndNoteId(username, noteID); + } + else { + FavorEntity favorEntity = new FavorEntity(); + favorEntity.setUsername(username); + favorEntity.setNoteId(noteID); + favorEntity.setId(id); + noteCollectionDao.delete(favorEntity); + return null; + } + } + + /** + * Get latest notes + * @return List of latest notes + */ + public List getLatestNotes() { + return noteDao.getLatestNotes(); + } + + /** + * Get latest notes with like status for current user + * @param username Current user's username + * @return List of latest notes with like status + */ + public List getLatestNotesWithLikeStatus(String username) { + return noteDao.getLatestNotesWithLikeStatus(username); + } + + /** + * Get notes by creator name + * @param username Current user's name + * @return list of notes created by that user + */ + public List getNotesByUsernameWithLikeStatus(String username) { + return noteDao.getNotesByMyNameWithLikeStatus(username); + } + + /** + * Get notes by keywords + * @param searchString Search keywords + * @return List of matching notes + */ + public List getNotesByKeywords(String searchString) { + return noteDao.getNotesByKeywords(searchString); + } + + /** + * Get notes by keywords with like status for current user + * @param searchString Search keywords + * @param username Current user's username + * @return List of matching notes with like status + */ + public List getNotesByKeywordsWithLikeStatus(String searchString, String username) { + return noteDao.getNotesByKeywordsWithLikeStatus(searchString, username); + } + + /** + * Get notes by tag + * @param tag Note tag + * @return List of notes with specified tag + */ + public List getNotesByTag(String tag) { + return noteDao.getNotesByTag(tag); + } + + /** + * Get notes by tag with like status for current user + * @param tag Note tag + * @param username Current user's username + * @return List of notes with specified tag and like status + */ + public List getNotesByTagWithLikeStatus(String tag, String username) { + return noteDao.getNotesByTagWithLikeStatus(tag, username); + } + + /** + * Create a new note + * @param noteEntity Note entity to create + * @return Created note entity + */ + public NoteEntity createNote(NoteEntity noteEntity){ + noteEntity.setBuildDate(new Date()); + noteEntity.setRefreshDate(new Date()); + noteEntity.setCommentNum(0); + noteEntity.setLikeNum(0); + noteEntity.setReadNum(0); + noteEntity.setIsDeleted((byte) 0); + + save(noteEntity); + + return noteEntity; + } + + public void readNote(String noteId) { + noteDao.readNote(noteId); + } + + + /** + * Get user behavior (favorites) for a specific note + * @param username Username + * @param noteId Note ID + * @return Favor entity or null + */ + public FavorEntity getBehaviourByUsernameAndNoteId(String username, String noteId) { + return noteCollectionDao.findByUsernameAndNoteId(username, noteId); + } + + /** + * Get user behaviors (favorites) list + * @param username Username + * @param pageNo Page number + * @param pageSize Page size + * @return List of favor entities + */ + public List getBehaviourByUsername(String username, int pageNo, int pageSize) { + return noteCollectionDao.getBehaviourByUsername(username, pageNo, pageSize); + } + + // === Like functionality implementation === + + /** + * Like or unlike a note + * @param username Username who likes/unlikes + * @param noteId Note ID to like/unlike + * @param isLike true to like, false to unlike + * @return NoteLikeEntity if liked, null if unliked + */ + @Override + public NoteLikeEntity likeNote(String username, String noteId, boolean isLike) { + if (isLike) { + // Check if already liked + NoteLikeEntity existingLike = noteLikeDao.findNoteLike(username, noteId); + if (existingLike != null) { + return existingLike; // Already liked, return existing record + } + + // Create new like record + NoteLikeEntity noteLikeEntity = new NoteLikeEntity(username, noteId); + noteLikeDao.addNoteLike(noteLikeEntity); + return noteLikeEntity; + } else { + // Remove like record + noteLikeDao.removeNoteLike(username, noteId); + return null; + } + } + + /** + * Check if a user has liked a specific note + * @param username Username to check + * @param noteId Note ID to check + * @return true if liked, false otherwise + */ + @Override + public boolean hasUserLikedNote(String username, String noteId) { + return noteLikeDao.hasUserLikedNote(username, noteId); + } + + /** + * Get like record for a user and note + * @param username Username + * @param noteId Note ID + * @return NoteLikeEntity if exists, null otherwise + */ + @Override + public NoteLikeEntity getNoteLike(String username, String noteId) { + return noteLikeDao.findNoteLike(username, noteId); + } +} \ No newline at end of file diff --git a/ocean-note-service/src/main/java/com/oriole/ocean/service/NoteTypeService.java b/ocean-note-service/src/main/java/com/oriole/ocean/service/NoteTypeService.java deleted file mode 100644 index b021e9a..0000000 --- a/ocean-note-service/src/main/java/com/oriole/ocean/service/NoteTypeService.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.oriole.ocean.service; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.oriole.ocean.dao.NoteTypeDao; -import com.oriole.ocean.common.po.mysql.NoteTypeEntity; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -@Service -@Transactional -public class NoteTypeService extends ServiceImpl { - public List getAllNoteType(){ - return list(); - } -} diff --git a/ocean-note-service/src/main/resources/mapper/NoteCommentLikeMapper.xml b/ocean-note-service/src/main/resources/mapper/NoteCommentLikeMapper.xml new file mode 100644 index 0000000..bb7f2e7 --- /dev/null +++ b/ocean-note-service/src/main/resources/mapper/NoteCommentLikeMapper.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + INSERT INTO note_comment_like (id, username, comment_id, like_time) + VALUES (#{id}, #{username}, #{commentId}, #{likeTime}) + + + + + DELETE FROM note_comment_like + WHERE username = #{username} AND comment_id = #{commentId} + + + + + + + + + \ No newline at end of file diff --git a/ocean-note-service/src/main/resources/mapper/NoteCommentMapper.xml b/ocean-note-service/src/main/resources/mapper/NoteCommentMapper.xml new file mode 100644 index 0000000..54d84df --- /dev/null +++ b/ocean-note-service/src/main/resources/mapper/NoteCommentMapper.xml @@ -0,0 +1,42 @@ + + + + + + INSERT INTO note_comment + (id, note_id, build_username, like_num, build_date, + reply_id, reply_username, content, is_deleted) + VALUES (#{id}, #{noteId}, #{buildUsername}, 0, + #{buildDate}, #{replyId}, #{replyUsername}, #{content}, 0) + + + + UPDATE note_comment + SET is_deleted = 1 + WHERE id = #{commentId} + + + + + + + + \ No newline at end of file diff --git a/ocean-note-service/src/main/resources/mapper/NoteLikeMapper.xml b/ocean-note-service/src/main/resources/mapper/NoteLikeMapper.xml new file mode 100644 index 0000000..03fe54e --- /dev/null +++ b/ocean-note-service/src/main/resources/mapper/NoteLikeMapper.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + INSERT INTO note_like (id, username, note_id, like_time) + VALUES (#{id}, #{username}, #{noteId}, #{likeTime}) + + + + + DELETE FROM note_like + WHERE username = #{username} AND note_id = #{noteId} + + + + + + + + + \ No newline at end of file diff --git a/ocean-note-service/src/main/resources/mapper/NoteMapper.xml b/ocean-note-service/src/main/resources/mapper/NoteMapper.xml new file mode 100644 index 0000000..8b2b477 --- /dev/null +++ b/ocean-note-service/src/main/resources/mapper/NoteMapper.xml @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UPDATE note + SET read_num = read_num + 1 + WHERE note.note_id = #{noteId}; + + + + UPDATE note + SET comment_num = comment_num + #{value} + WHERE note.note_id = #{noteId}; + + + + + + UPDATE note + SET is_deleted = 1 + WHERE note.note_id = #{noteID}; + + + \ No newline at end of file diff --git a/ocean-notify-service/src/main/java/com/oriole/ocean/controller/UserNotifyController.java b/ocean-notify-service/src/main/java/com/oriole/ocean/controller/UserNotifyController.java index cc6bb83..ee49d13 100644 --- a/ocean-notify-service/src/main/java/com/oriole/ocean/controller/UserNotifyController.java +++ b/ocean-notify-service/src/main/java/com/oriole/ocean/controller/UserNotifyController.java @@ -31,10 +31,29 @@ public class UserNotifyController { @RequestMapping("/getLatestNotifications") public MsgEntity> getLatestNotifications( @AuthUser AuthUserEntity authUser, - @RequestParam(required = false) String username, + @RequestParam String username, @RequestParam(required = false) Date lastPullDate) { username = authUser.getAllowOperationUsername(username); return new MsgEntity<>("SUCCESS", "1", userNotifyService.getAllNotifyByUsernameAndLastPullDate(username, lastPullDate)); } + + @RequestMapping("/readNotifications") + public MsgEntity readNotifications( + @AuthUser AuthUserEntity authUser, + @RequestParam String username, + @RequestParam(required = false) Date latestReadNotifyDate) { + username = authUser.getAllowOperationUsername(username); + + return new MsgEntity<>("SUCCESS", "1", userNotifyService.readAllNotifyByUsernameAndLatestReadNotifyDate(username, latestReadNotifyDate)); + } + + @RequestMapping("/ifExistsUnreadNotifications") + public MsgEntity readNotifications( + @AuthUser AuthUserEntity authUser, + @RequestParam String username) { + username = authUser.getAllowOperationUsername(username); + + return new MsgEntity<>("SUCCESS", "1", userNotifyService.checkUnreadNotify(username)); + } } diff --git a/ocean-notify-service/src/main/java/com/oriole/ocean/dao/NotifyDao.java b/ocean-notify-service/src/main/java/com/oriole/ocean/dao/NotifyDao.java index df9b6b3..d56a26b 100644 --- a/ocean-notify-service/src/main/java/com/oriole/ocean/dao/NotifyDao.java +++ b/ocean-notify-service/src/main/java/com/oriole/ocean/dao/NotifyDao.java @@ -1,7 +1,10 @@ package com.oriole.ocean.dao; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.oriole.ocean.common.enumerate.MainType; +import com.oriole.ocean.common.enumerate.NotifyAction; import com.oriole.ocean.common.po.mysql.NotifyEntity; public interface NotifyDao extends BaseMapper { + void removeNotify(String targetId, MainType targetType, String commentId, NotifyAction action, String buildUsername); } diff --git a/ocean-notify-service/src/main/java/com/oriole/ocean/dao/UserNotifyDao.java b/ocean-notify-service/src/main/java/com/oriole/ocean/dao/UserNotifyDao.java index a629e14..78d26f3 100644 --- a/ocean-notify-service/src/main/java/com/oriole/ocean/dao/UserNotifyDao.java +++ b/ocean-notify-service/src/main/java/com/oriole/ocean/dao/UserNotifyDao.java @@ -8,4 +8,6 @@ public interface UserNotifyDao extends BaseMapper { List getAllNotifyByUsernameAndLastPullDate(String username, Date latestPullDate); + void readAllNotifyByUsernameAndLatestReadNotifyDate(String username, Date latestReadNotifyDate); + boolean checkUnreadNotify(String username); } diff --git a/ocean-notify-service/src/main/java/com/oriole/ocean/service/NotifyServiceImpl.java b/ocean-notify-service/src/main/java/com/oriole/ocean/service/NotifyServiceImpl.java index 06c50d6..e6eb306 100644 --- a/ocean-notify-service/src/main/java/com/oriole/ocean/service/NotifyServiceImpl.java +++ b/ocean-notify-service/src/main/java/com/oriole/ocean/service/NotifyServiceImpl.java @@ -2,9 +2,10 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.oriole.ocean.common.enumerate.NotifyAction; -import com.oriole.ocean.common.enumerate.NotifyType; +import com.oriole.ocean.common.enumerate.*; import com.oriole.ocean.common.po.mongo.UserBehaviorEntity; +import com.oriole.ocean.common.po.mongo.comment.CommentEntity; +import com.oriole.ocean.common.po.mongo.comment.CommentReplyEntity; import com.oriole.ocean.common.service.NotifyService; import com.oriole.ocean.dao.NotifyDao; import com.oriole.ocean.common.po.mysql.NotifyEntity; @@ -17,31 +18,66 @@ @Service @DubboService public class NotifyServiceImpl extends ServiceImpl implements NotifyService { + public void addNotify(NotifyEntity notifyEntity){ save(notifyEntity); } + public void addNotifyByComment(Integer bindID, MainType mainType, + CommentEntity fileCommentEntity){ + NotifyEntity notifyEntity = new NotifyEntity(NotifyType.REMIND, fileCommentEntity.getCommentBuildUsername()); + notifyEntity.setTargetId(bindID.toString()); + notifyEntity.setTargetType(mainType); + notifyEntity.setContent(fileCommentEntity.getCommentContent()); + notifyEntity.setAction(NotifyAction.NEW_COMMENT); + addNotify(notifyEntity); + } + + public void addNotifyByReply(Integer bindID, MainType mainType, + String replyInCommentID, CommentReplyEntity fileCommentReplyEntity){ + NotifyEntity notifyEntity = new NotifyEntity(NotifyType.REMIND, fileCommentReplyEntity.getReplyBuildUsername()); + notifyEntity.setTargetId(bindID.toString()); + notifyEntity.setTargetType(mainType); + notifyEntity.setCommentID(replyInCommentID); + notifyEntity.setContent(fileCommentReplyEntity.getCommentContent()); + notifyEntity.setAction(NotifyAction.NEW_REPLY); + addNotify(notifyEntity); + } + // 根据用户行为生产消息 public void addNotifyByBehaviorRecord(UserBehaviorEntity userBehaviorEntity) { // 收藏、每日签到、阅读等行为都不需要记录为通知行为 + boolean add_or_remove = true; NotifyEntity notifyEntity = new NotifyEntity(NotifyType.REMIND,userBehaviorEntity.getDoUsername()); switch (userBehaviorEntity.getBehaviorType()) { case DO_DOWNLOAD: notifyEntity.setAction(NotifyAction.DOWNLOAD); break; case DO_LIKE: + add_or_remove = !userBehaviorEntity.getIsCancel(); notifyEntity.setAction(NotifyAction.LIKE); + notifyEntity.setContent(userBehaviorEntity.getDoUsername() + "点赞了你的帖子"); break; case DO_COMMENT_LIKE: + add_or_remove = !userBehaviorEntity.getIsCancel(); notifyEntity.setAction(NotifyAction.LIKE_COMMENT); - notifyEntity.setCommentID(userBehaviorEntity.getExtraInfo().getString("commentID")); + notifyEntity.setCommentID((String) userBehaviorEntity.getExtraInfo(BehaviorExtraInfo.COMMENT_ID)); + notifyEntity.setContent(userBehaviorEntity.getDoUsername() + "点赞了你的评论"); break; default: return; } notifyEntity.setUserBehaviorID(userBehaviorEntity.getId()); - notifyEntity.setTargetIDAndType(String.valueOf(userBehaviorEntity.getBindID()),userBehaviorEntity.getType()); - addNotify(notifyEntity); + notifyEntity.setTargetIdAndType(String.valueOf(userBehaviorEntity.getBindID()),userBehaviorEntity.getType()); + if(add_or_remove) + addNotify(notifyEntity); + else + removeNotify(notifyEntity); + } + + private void removeNotify(NotifyEntity notifyEntity) { + NotifyDao notifyDao = getBaseMapper(); + notifyDao.removeNotify(notifyEntity.getTargetId(),notifyEntity.getTargetType(),notifyEntity.getCommentID(), notifyEntity.getAction(),notifyEntity.getBuildUsername()); } // 查询指定时间之后产生的所有消息 diff --git a/ocean-notify-service/src/main/java/com/oriole/ocean/service/UserNotifyServiceImpl.java b/ocean-notify-service/src/main/java/com/oriole/ocean/service/UserNotifyServiceImpl.java index fdc4733..d84cb62 100644 --- a/ocean-notify-service/src/main/java/com/oriole/ocean/service/UserNotifyServiceImpl.java +++ b/ocean-notify-service/src/main/java/com/oriole/ocean/service/UserNotifyServiceImpl.java @@ -15,11 +15,22 @@ public class UserNotifyServiceImpl extends ServiceImpl getAllNotifyByUsernameAndLastPullDate(String username, Date latestPullDate){ return userNotifyDao.getAllNotifyByUsernameAndLastPullDate(username,latestPullDate); } + // 用户阅读消息 + public boolean readAllNotifyByUsernameAndLatestReadNotifyDate(String username, Date latestReadNotifyDate){ + userNotifyDao.readAllNotifyByUsernameAndLatestReadNotifyDate(username, latestReadNotifyDate); + return true; + } + + // 用户阅读消息 + public boolean checkUnreadNotify(String username){ + return userNotifyDao.checkUnreadNotify(username); + } + // 查询用户消息表中最后的更新时间 public Date getUserNotifyLastUpdateTime() { QueryWrapper queryWrapper = new QueryWrapper<>(); diff --git a/ocean-notify-service/src/main/resources/mapper/Notify.xml b/ocean-notify-service/src/main/resources/mapper/Notify.xml new file mode 100644 index 0000000..f91e442 --- /dev/null +++ b/ocean-notify-service/src/main/resources/mapper/Notify.xml @@ -0,0 +1,11 @@ + + + + + CALL sp_safe_delete_notify( + #{targetId}, #{targetType}, + #{commentId}, #{action}, + #{buildUsername}); + + \ No newline at end of file diff --git a/ocean-notify-service/src/main/resources/mapper/UserNotifyMapper.xml b/ocean-notify-service/src/main/resources/mapper/UserNotifyMapper.xml index 08d6326..5284994 100644 --- a/ocean-notify-service/src/main/resources/mapper/UserNotifyMapper.xml +++ b/ocean-notify-service/src/main/resources/mapper/UserNotifyMapper.xml @@ -25,4 +25,24 @@ AND build_date >= #{latestPullDate} + + + UPDATE user_notify + SET is_read = 1 + WHERE username = #{username}; + + AND build_date >= #{latestReadNotifyDate} + + + + \ No newline at end of file diff --git a/ocean-user-behavior-service/pom.xml b/ocean-user-behavior-service/pom.xml index c74ff75..b9dc89b 100644 --- a/ocean-user-behavior-service/pom.xml +++ b/ocean-user-behavior-service/pom.xml @@ -71,5 +71,11 @@ 1.0-SNAPSHOT compile + + com.oriole + ocean-notify-service + 1.0-SNAPSHOT + compile + diff --git a/ocean-user-behavior-service/src/main/java/com/oriole/ocean/service/UserBehaviorServiceImpl.java b/ocean-user-behavior-service/src/main/java/com/oriole/ocean/service/UserBehaviorServiceImpl.java index b724d70..5c15fb8 100644 --- a/ocean-user-behavior-service/src/main/java/com/oriole/ocean/service/UserBehaviorServiceImpl.java +++ b/ocean-user-behavior-service/src/main/java/com/oriole/ocean/service/UserBehaviorServiceImpl.java @@ -5,6 +5,7 @@ import com.oriole.ocean.common.enumerate.BehaviorType; import com.oriole.ocean.common.enumerate.EvaluateType; import com.oriole.ocean.common.po.mongo.UserBehaviorEntity; +import com.oriole.ocean.common.service.NotifyService; import com.oriole.ocean.common.service.UserBehaviorService; import com.oriole.ocean.common.vo.BusinessException; import org.apache.dubbo.config.annotation.DubboService; @@ -30,8 +31,8 @@ public class UserBehaviorServiceImpl implements UserBehaviorService { @Autowired private MongoTemplate mongoTemplate; -// @Autowired -// private NotifyServiceImpl notifyService; + @Autowired + private NotifyService notifyService; // 检查某用户针对特定对象(某文档/某便签等)的评价行为,并得出此次评价所需发生的全部评价行为 // 对特定对象具有评论区时,也可处理对其评论区的评价行为 @@ -83,10 +84,12 @@ public void setUserEvaluateBehavior(UserBehaviorEntity userBehavior, List findAllBehaviorRecords(UserBehaviorEntity userBe public void setBehaviorRecord(UserBehaviorEntity userBehavior) { mongoTemplate.save(userBehavior, "user_behavior"); // 部分用户行为需要产生用户消息 -// notifyService.addNotifyByBehaviorRecord(userBehaviorEntity); + notifyService.addNotifyByBehaviorRecord(userBehavior); } public void updateBehaviorRecordExtraInfo(UserBehaviorEntity userBehavior, BehaviorExtraInfo behaviorExtraInfo, Object value) { @@ -147,6 +150,8 @@ public void updateBehaviorRecordExtraInfo(UserBehaviorEntity userBehavior, Behav } public void deleteBehaviorRecord(UserBehaviorEntity userBehavior) { + notifyService.addNotifyByBehaviorRecord(userBehavior); + userBehavior.setIsCancel(false); // bull shit Query query = userBehavior.getQuery(); Update update = new Update(); update.set("isCancel", true); @@ -157,5 +162,4 @@ public void deleteBehaviorRecordReal(UserBehaviorEntity userBehavior) { Query query = userBehavior.getQuery(); mongoTemplate.remove(query, "user_behavior"); } - } diff --git a/ocean-user-service/pom.xml b/ocean-user-service/pom.xml index b859331..14260bb 100644 --- a/ocean-user-service/pom.xml +++ b/ocean-user-service/pom.xml @@ -60,6 +60,12 @@ springfox-swagger-ui + + + org.springframework.boot + spring-boot-starter-validation + + diff --git a/ocean-user-service/src/main/java/com/oriole/ocean/ControllerExceptionHandler.java b/ocean-user-service/src/main/java/com/oriole/ocean/ControllerExceptionHandler.java index 0a1cd06..7e178ad 100644 --- a/ocean-user-service/src/main/java/com/oriole/ocean/ControllerExceptionHandler.java +++ b/ocean-user-service/src/main/java/com/oriole/ocean/ControllerExceptionHandler.java @@ -5,11 +5,16 @@ import com.oriole.ocean.common.vo.OceanExceptionHandler; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; +import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import java.util.stream.Collectors; + @Slf4j @ControllerAdvice @ResponseBody @@ -20,6 +25,47 @@ public MsgEntity businessExceptionHandler(Busine return super.businessExceptionHandler(ex); } + /** + * Handle validation errors from @Valid annotation on request body + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public MsgEntity handleValidationException(MethodArgumentNotValidException ex) { + String errorMessage = ex.getBindingResult() + .getFieldErrors() + .stream() + .map(error -> error.getDefaultMessage()) + .collect(Collectors.joining("; ")); + + log.warn("Validation error: {}", errorMessage); + ErrorMsg errorMsg = new ErrorMsg( + "VALIDATION_ERROR", + "handleValidationException", + "ControllerExceptionHandler", + "数据验证失败: " + errorMessage); + return new MsgEntity<>("VALIDATION_ERROR", "-8", errorMsg); + } + + /** + * Handle validation errors from @Validated annotation on method parameters + */ + @ExceptionHandler(ConstraintViolationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public MsgEntity handleConstraintViolationException(ConstraintViolationException ex) { + String errorMessage = ex.getConstraintViolations() + .stream() + .map(ConstraintViolation::getMessage) + .collect(Collectors.joining("; ")); + + log.warn("Constraint violation: {}", errorMessage); + ErrorMsg errorMsg = new ErrorMsg( + "CONSTRAINT_VIOLATION", + "handleConstraintViolationException", + "ControllerExceptionHandler", + "参数验证失败: " + errorMessage); + return new MsgEntity<>("VALIDATION_ERROR", "-9", errorMsg); + } + @ExceptionHandler(Exception.class) @ResponseStatus(value= HttpStatus.INTERNAL_SERVER_ERROR) @Override diff --git a/ocean-user-service/src/main/java/com/oriole/ocean/controller/AuthController.java b/ocean-user-service/src/main/java/com/oriole/ocean/controller/AuthController.java index 1df8023..dd9785d 100644 --- a/ocean-user-service/src/main/java/com/oriole/ocean/controller/AuthController.java +++ b/ocean-user-service/src/main/java/com/oriole/ocean/controller/AuthController.java @@ -25,4 +25,9 @@ public MsgEntity login(@RequestParam String username, @RequestParam return new MsgEntity<>("SUCCESS","1", userEntity); } + @RequestMapping(value = "/reg", method = RequestMethod.POST) + public MsgEntity register(@RequestParam String username, @RequestParam String password, @RequestParam(required = false) String nickname, @RequestParam(required = false) String phoneNum, @RequestParam(required = false) String email) { + return userBaseInfoService.registerUser(username, password, nickname, phoneNum, email); + } + } diff --git a/ocean-user-service/src/main/java/com/oriole/ocean/controller/UserInfoController.java b/ocean-user-service/src/main/java/com/oriole/ocean/controller/UserInfoController.java index 4f36fe8..8866424 100644 --- a/ocean-user-service/src/main/java/com/oriole/ocean/controller/UserInfoController.java +++ b/ocean-user-service/src/main/java/com/oriole/ocean/controller/UserInfoController.java @@ -6,12 +6,16 @@ import com.oriole.ocean.common.vo.AuthUserEntity; import com.oriole.ocean.common.vo.BusinessException; import com.oriole.ocean.common.vo.MsgEntity; +import com.oriole.ocean.dto.UserInfoUpdateDTO; import com.oriole.ocean.service.UserInfoServiceImpl; import com.oriole.ocean.service.base.UserBaseInfoServiceImpl; +import com.oriole.ocean.util.ValidationUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; + @RestController @Slf4j @RequestMapping("/userInfoService") @@ -48,4 +52,64 @@ public MsgEntity getUserAllInfo(@AuthUser AuthUserEntity authUser) { return new MsgEntity<>("SUCCESS", "1", userInfoService.getUserInfo(authUser.getUsername(), UserInfoLevel.ALL)); } + + @PostMapping("/updateUserInfo") + /** + * Update user personal information. Only non-null fields will be updated. + * Includes comprehensive validation for security and data integrity. + * @param authUser Authenticated user (from token) + * @param updateDTO Fields to update (validated) + * @return Success message or error + */ + public MsgEntity updateUserInfo(@AuthUser AuthUserEntity authUser, @Valid @RequestBody UserInfoUpdateDTO updateDTO) { + try { + // Additional security validation beyond annotation validation + validateSecurityConstraints(updateDTO); + + userInfoService.updateUserInfo(authUser.getUsername(), updateDTO); + return new MsgEntity<>("SUCCESS", "1", "User info updated successfully"); + } catch (Exception e) { + log.error("Failed to update user info for {}: {}", authUser.getUsername(), e.getMessage(), e); + throw new BusinessException("-1", "Failed to update user info: " + e.getMessage()); + } + } + + /** + * Validates security constraints for all string fields in the DTO + * Checks for HTML tags, SQL injection, and XSS attempts + * + * @param updateDTO DTO to validate + * @throws BusinessException if validation fails + */ + private void validateSecurityConstraints(UserInfoUpdateDTO updateDTO) { + // Validate all string fields for security threats + if (!ValidationUtil.isSafeInput(updateDTO.getNickname())) { + throw new BusinessException("-5", "昵称包含不允许的字符或标签"); + } + if (!ValidationUtil.isSafeInput(updateDTO.getRealname())) { + throw new BusinessException("-5", "真实姓名包含不允许的字符或标签"); + } + if (!ValidationUtil.isSafeInput(updateDTO.getAvatar())) { + throw new BusinessException("-5", "头像URL包含不允许的字符或标签"); + } + if (!ValidationUtil.isSafeInput(updateDTO.getCollege())) { + throw new BusinessException("-5", "学院名称包含不允许的字符或标签"); + } + if (!ValidationUtil.isSafeInput(updateDTO.getMajor())) { + throw new BusinessException("-5", "专业名称包含不允许的字符或标签"); + } + if (!ValidationUtil.isSafeInput(updateDTO.getPersonalSignature())) { + throw new BusinessException("-5", "个人签名包含不允许的字符或标签"); + } + + // Additional email validation (beyond annotation) + if (!ValidationUtil.isValidEmail(updateDTO.getEmail())) { + throw new BusinessException("-6", "邮箱格式不正确"); + } + + // Additional phone validation (beyond annotation) + if (!ValidationUtil.isValidPhoneNumber(updateDTO.getPhoneNum())) { + throw new BusinessException("-7", "手机号格式不正确"); + } + } } diff --git a/ocean-user-service/src/main/java/com/oriole/ocean/dto/UserInfoUpdateDTO.java b/ocean-user-service/src/main/java/com/oriole/ocean/dto/UserInfoUpdateDTO.java new file mode 100644 index 0000000..a07b7f0 --- /dev/null +++ b/ocean-user-service/src/main/java/com/oriole/ocean/dto/UserInfoUpdateDTO.java @@ -0,0 +1,57 @@ +package com.oriole.ocean.dto; + +import lombok.Data; +import javax.validation.constraints.*; +import java.io.Serializable; +import java.util.Date; + +/** + * Data Transfer Object for updating user information. + * Supports partial updates for both user and user_extra fields. + * Fields left null will not be updated. + * + * MAY NEED REVIEW: Adjust fields as needed for your business logic. + */ +@Data +public class UserInfoUpdateDTO implements Serializable { + // User table fields + @Size(max = 50, message = "昵称长度不能超过50个字符") + private String nickname; + + @Email(message = "邮箱格式不正确") + @Size(max = 100, message = "邮箱长度不能超过100个字符") + private String email; + + @Pattern(regexp = "^[0-9+\\-\\s()]{10,20}$", message = "手机号格式不正确") + private String phoneNum; + + @Size(max = 50, message = "真实姓名长度不能超过50个字符") + private String realname; + + @Size(max = 255, message = "头像URL长度不能超过255个字符") + private String avatar; + + @Min(value = 0, message = "等级不能小于0") + @Max(value = 999999, message = "等级不能大于999999") + private Integer levelGrade; + + @Min(value = 1, message = "认证ID必须大于0") + private Integer certID; + + // UserExtra table fields + @Size(max = 100, message = "学院名称长度不能超过100个字符") + private String college; + + @Size(max = 100, message = "专业名称长度不能超过100个字符") + private String major; + + @Past(message = "生日必须是过去的日期") + private Date birthday; + + @Min(value = 0, message = "性别值不正确") + @Max(value = 2, message = "性别值不正确") + private Integer sex; + + @Size(max = 500, message = "个人签名长度不能超过500个字符") + private String personalSignature; +} \ No newline at end of file diff --git a/ocean-user-service/src/main/java/com/oriole/ocean/service/UserInfoServiceImpl.java b/ocean-user-service/src/main/java/com/oriole/ocean/service/UserInfoServiceImpl.java index 97ec15a..344c9be 100644 --- a/ocean-user-service/src/main/java/com/oriole/ocean/service/UserInfoServiceImpl.java +++ b/ocean-user-service/src/main/java/com/oriole/ocean/service/UserInfoServiceImpl.java @@ -8,6 +8,8 @@ import com.oriole.ocean.service.base.UserCertificationServiceImpl; import com.oriole.ocean.service.base.UserExtraInfoServiceImpl; import com.oriole.ocean.service.base.UserWalletServiceImpl; +import com.oriole.ocean.dto.UserInfoUpdateDTO; +import com.oriole.ocean.common.po.mysql.UserExtraEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.RequestParam; @@ -51,4 +53,48 @@ public UserEntity getUserInfo(String username, UserInfoLevel userInfoLevel) { throw new RuntimeException("not support this userInfo level"); } } + + /** + * Update user and user_extra info for the given username. Only non-null fields in updateDTO will be updated. + * @param username Username to update + * @param updateDTO DTO containing fields to update + * @throws BusinessException if user does not exist or update fails + */ + public void updateUserInfo(String username, UserInfoUpdateDTO updateDTO) { + UserEntity user = userService.getById(username); + if (user == null) { + throw new BusinessException("-2", "User not found"); + } + boolean userChanged = false; + // Update user fields + if (updateDTO.getNickname() != null) { user.setNickname(updateDTO.getNickname()); userChanged = true; } + if (updateDTO.getEmail() != null) { user.setEmail(updateDTO.getEmail()); userChanged = true; } + if (updateDTO.getPhoneNum() != null) { user.setPhoneNum(updateDTO.getPhoneNum()); userChanged = true; } + if (updateDTO.getRealname() != null) { user.setRealname(updateDTO.getRealname()); userChanged = true; } + if (updateDTO.getAvatar() != null) { user.setAvatar(updateDTO.getAvatar()); userChanged = true; } + if (updateDTO.getLevelGrade() != null) { user.setLevelGrade(updateDTO.getLevelGrade()); userChanged = true; } + if (updateDTO.getCertID() != null) { user.setCertID(updateDTO.getCertID()); userChanged = true; } + if (userChanged) { + if (!userService.updateById(user)) { + throw new BusinessException("-3", "Failed to update user base info"); + } + } + // Update user_extra fields + UserExtraEntity extra = userExtraService.getById(username); + boolean extraChanged = false; + if (extra == null) { + extra = new UserExtraEntity(); + extra.setUsername(username); + } + if (updateDTO.getCollege() != null) { extra.setCollege(updateDTO.getCollege()); extraChanged = true; } + if (updateDTO.getMajor() != null) { extra.setMajor(updateDTO.getMajor()); extraChanged = true; } + if (updateDTO.getBirthday() != null) { extra.setBirthday(updateDTO.getBirthday()); extraChanged = true; } + if (updateDTO.getSex() != null) { extra.setSex(updateDTO.getSex()); extraChanged = true; } + if (updateDTO.getPersonalSignature() != null) { extra.setPersonalSignature(updateDTO.getPersonalSignature()); extraChanged = true; } + if (extraChanged) { + if (!userExtraService.saveOrUpdate(extra)) { + throw new BusinessException("-4", "Failed to update user extra info"); + } + } + } } diff --git a/ocean-user-service/src/main/java/com/oriole/ocean/service/base/UserBaseInfoServiceImpl.java b/ocean-user-service/src/main/java/com/oriole/ocean/service/base/UserBaseInfoServiceImpl.java index a5309ce..af71c07 100644 --- a/ocean-user-service/src/main/java/com/oriole/ocean/service/base/UserBaseInfoServiceImpl.java +++ b/ocean-user-service/src/main/java/com/oriole/ocean/service/base/UserBaseInfoServiceImpl.java @@ -30,4 +30,32 @@ private UserEntity userLoginHandler(UserEntity userEntity) { } return userEntity; } + + public MsgEntity registerUser(String username, String password, String nickname, String phoneNum, String email) { + // Check if username already exists + UserEntity existingUser = getById(username); + if (existingUser != null) { + throw new BusinessException("-5", "Username already exists"); + } + + // Create new user + UserEntity newUser = new UserEntity(); + newUser.setUsername(username); + newUser.setPassword(password); + newUser.setNickname(nickname); + newUser.setPhoneNum(phoneNum); + newUser.setEmail(email); + newUser.setIsValid((byte) 0); // Set to pending review + newUser.setRegDate(new java.util.Date()); + newUser.setRole("USER"); + newUser.setLevelGrade(0); + + // Save user to database + boolean saved = save(newUser); + if (saved) { + return new MsgEntity<>("SUCCESS", "1", "Registration successful, waiting for admin review"); + } else { + throw new BusinessException("-6", "Registration failed, please try again later"); + } + } } diff --git a/ocean-user-service/src/main/java/com/oriole/ocean/util/ValidationUtil.java b/ocean-user-service/src/main/java/com/oriole/ocean/util/ValidationUtil.java new file mode 100644 index 0000000..f64de88 --- /dev/null +++ b/ocean-user-service/src/main/java/com/oriole/ocean/util/ValidationUtil.java @@ -0,0 +1,119 @@ +package com.oriole.ocean.util; + +import java.util.regex.Pattern; + +/** + * Utility class for data validation including security checks + * + * @author Ocean Team + */ +public class ValidationUtil { + + // Regex patterns for security validation + private static final Pattern HTML_TAG_PATTERN = Pattern.compile("<[^>]*>"); + private static final Pattern EMAIL_PATTERN = Pattern.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"); + private static final Pattern PHONE_PATTERN = Pattern.compile("^[0-9+\\-\\s()]{10,20}$"); + + // SQL injection patterns + private static final Pattern[] SQL_INJECTION_PATTERNS = { + Pattern.compile("(\\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION|SCRIPT)\\b)", Pattern.CASE_INSENSITIVE), + Pattern.compile("(--|/\\*|\\*/|;|'|\"|`)"), + Pattern.compile("(\\bOR\\b|\\bAND\\b).*[=<>]", Pattern.CASE_INSENSITIVE) + }; + + // XSS patterns + private static final Pattern[] XSS_PATTERNS = { + Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE), + Pattern.compile("on\\w+\\s*=", Pattern.CASE_INSENSITIVE), + Pattern.compile("data:text/html", Pattern.CASE_INSENSITIVE), + Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE) + }; + + /** + * Validates input for security threats (HTML tags, SQL injection, XSS) + * + * @param input Input string to validate + * @return true if input is safe, false otherwise + */ + public static boolean isSafeInput(String input) { + if (input == null || input.trim().isEmpty()) { + return true; // Allow empty values + } + + // Check for HTML tags + if (HTML_TAG_PATTERN.matcher(input).find()) { + return false; + } + + // Check for SQL injection patterns + for (Pattern pattern : SQL_INJECTION_PATTERNS) { + if (pattern.matcher(input).find()) { + return false; + } + } + + // Check for XSS patterns + for (Pattern pattern : XSS_PATTERNS) { + if (pattern.matcher(input).find()) { + return false; + } + } + + return true; + } + + /** + * Validates email format + * + * @param email Email string to validate + * @return true if valid email format, false otherwise + */ + public static boolean isValidEmail(String email) { + if (email == null || email.trim().isEmpty()) { + return true; // Allow empty email (optional field) + } + return EMAIL_PATTERN.matcher(email.trim()).matches(); + } + + /** + * Validates phone number format + * + * @param phoneNum Phone number to validate + * @return true if valid phone format, false otherwise + */ + public static boolean isValidPhoneNumber(String phoneNum) { + if (phoneNum == null || phoneNum.trim().isEmpty()) { + return true; // Allow empty phone (optional field) + } + return PHONE_PATTERN.matcher(phoneNum.trim()).matches(); + } + + /** + * Validates string length + * + * @param input String to validate + * @param maxLength Maximum allowed length + * @return true if within length limit, false otherwise + */ + public static boolean isValidLength(String input, int maxLength) { + if (input == null) { + return true; + } + return input.length() <= maxLength; + } + + /** + * Validates that integer is within specified range + * + * @param value Integer value to validate + * @param min Minimum allowed value (inclusive) + * @param max Maximum allowed value (inclusive) + * @return true if within range, false otherwise + */ + public static boolean isValidRange(Integer value, int min, int max) { + if (value == null) { + return true; + } + return value >= min && value <= max; + } +} \ No newline at end of file diff --git a/ocean-user-service/src/test/java/com/oriole/ocean/validation/ValidationTest.java b/ocean-user-service/src/test/java/com/oriole/ocean/validation/ValidationTest.java new file mode 100644 index 0000000..fc5dbce --- /dev/null +++ b/ocean-user-service/src/test/java/com/oriole/ocean/validation/ValidationTest.java @@ -0,0 +1,154 @@ +package com.oriole.ocean.validation; + +import com.oriole.ocean.dto.UserInfoUpdateDTO; +import com.oriole.ocean.util.ValidationUtil; +import junit.framework.TestCase; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import java.util.Date; +import java.util.Set; + +/** + * Test class for validation functionality + * Demonstrates various validation scenarios for UserInfoUpdateDTO + */ +public class ValidationTest extends TestCase { + + private Validator validator; + + public void setUp() { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + validator = factory.getValidator(); + } + + public void testValidUserInfoUpdateDTO() { + UserInfoUpdateDTO dto = new UserInfoUpdateDTO(); + dto.setNickname("TestUser"); + dto.setEmail("test@example.com"); + dto.setPhoneNum("1234567890"); + dto.setRealname("Test User"); + dto.setLevelGrade(100); + dto.setCollege("Computer Science"); + dto.setMajor("Software Engineering"); + dto.setBirthday(new Date(System.currentTimeMillis() - 86400000L)); // Yesterday + dto.setSex(1); + dto.setPersonalSignature("Hello World"); + + Set> violations = validator.validate(dto); + assertTrue("Valid DTO should have no violations", violations.isEmpty()); + } + + public void testInvalidEmail() { + UserInfoUpdateDTO dto = new UserInfoUpdateDTO(); + dto.setEmail("invalid-email"); + + Set> violations = validator.validate(dto); + assertFalse("Invalid email should cause violations", violations.isEmpty()); + assertTrue("Should contain email validation error", + violations.stream().anyMatch(v -> v.getMessage().contains("邮箱格式不正确"))); + } + + public void testInvalidPhoneNumber() { + UserInfoUpdateDTO dto = new UserInfoUpdateDTO(); + dto.setPhoneNum("abc"); + + Set> violations = validator.validate(dto); + assertFalse("Invalid phone should cause violations", violations.isEmpty()); + assertTrue("Should contain phone validation error", + violations.stream().anyMatch(v -> v.getMessage().contains("手机号格式不正确"))); + } + + public void testMaxLengthViolation() { + UserInfoUpdateDTO dto = new UserInfoUpdateDTO(); + // Create a string longer than 50 characters + StringBuilder longNickname = new StringBuilder(); + for (int i = 0; i < 51; i++) { + longNickname.append("a"); + } + dto.setNickname(longNickname.toString()); + + Set> violations = validator.validate(dto); + assertFalse("Long nickname should cause violations", violations.isEmpty()); + assertTrue("Should contain length validation error", + violations.stream().anyMatch(v -> v.getMessage().contains("昵称长度不能超过50个字符"))); + } + + public void testFutureDateValidation() { + UserInfoUpdateDTO dto = new UserInfoUpdateDTO(); + dto.setBirthday(new Date(System.currentTimeMillis() + 86400000L)); // Tomorrow + + Set> violations = validator.validate(dto); + assertFalse("Future birthday should cause violations", violations.isEmpty()); + assertTrue("Should contain past date validation error", + violations.stream().anyMatch(v -> v.getMessage().contains("生日必须是过去的日期"))); + } + + public void testInvalidSexValue() { + UserInfoUpdateDTO dto = new UserInfoUpdateDTO(); + dto.setSex(5); // Invalid value (should be 0-2) + + Set> violations = validator.validate(dto); + assertFalse("Invalid sex value should cause violations", violations.isEmpty()); + assertTrue("Should contain sex validation error", + violations.stream().anyMatch(v -> v.getMessage().contains("性别值不正确"))); + } + + // Security validation tests using ValidationUtil + + public void testSafeInputValidation() { + assertTrue("Normal text should be safe", ValidationUtil.isSafeInput("Normal text")); + assertTrue("Empty string should be safe", ValidationUtil.isSafeInput("")); + assertTrue("Null should be safe", ValidationUtil.isSafeInput(null)); + } + + public void testHtmlTagDetection() { + assertFalse("HTML tags should be detected", ValidationUtil.isSafeInput("")); + assertFalse("HTML tags should be detected", ValidationUtil.isSafeInput("Hello world")); + assertFalse("HTML tags should be detected", ValidationUtil.isSafeInput("
content
")); + } + + public void testSqlInjectionDetection() { + assertFalse("SQL injection should be detected", ValidationUtil.isSafeInput("'; DROP TABLE users; --")); + assertFalse("SQL injection should be detected", ValidationUtil.isSafeInput("1' OR '1'='1")); + assertFalse("SQL injection should be detected", ValidationUtil.isSafeInput("SELECT * FROM users")); + assertFalse("SQL injection should be detected", ValidationUtil.isSafeInput("admin'--")); + } + + public void testXssDetection() { + assertFalse("XSS should be detected", ValidationUtil.isSafeInput("javascript:alert('xss')")); + assertFalse("XSS should be detected", ValidationUtil.isSafeInput("onclick=alert('xss')")); + assertFalse("XSS should be detected", ValidationUtil.isSafeInput("data:text/html,")); + assertFalse("XSS should be detected", ValidationUtil.isSafeInput("vbscript:msgbox('xss')")); + } + + public void testEmailValidation() { + assertTrue("Valid email should pass", ValidationUtil.isValidEmail("test@example.com")); + assertTrue("Valid email should pass", ValidationUtil.isValidEmail("user.name@domain.co.uk")); + assertTrue("Empty email should pass", ValidationUtil.isValidEmail("")); + assertTrue("Null email should pass", ValidationUtil.isValidEmail(null)); + + assertFalse("Invalid email should fail", ValidationUtil.isValidEmail("invalid-email")); + assertFalse("Invalid email should fail", ValidationUtil.isValidEmail("@domain.com")); + assertFalse("Invalid email should fail", ValidationUtil.isValidEmail("user@")); + } + + public void testPhoneValidation() { + assertTrue("Valid phone should pass", ValidationUtil.isValidPhoneNumber("1234567890")); + assertTrue("Valid phone should pass", ValidationUtil.isValidPhoneNumber("+1-234-567-8900")); + assertTrue("Valid phone should pass", ValidationUtil.isValidPhoneNumber("(123) 456-7890")); + assertTrue("Empty phone should pass", ValidationUtil.isValidPhoneNumber("")); + assertTrue("Null phone should pass", ValidationUtil.isValidPhoneNumber(null)); + + assertFalse("Invalid phone should fail", ValidationUtil.isValidPhoneNumber("abc")); + assertFalse("Invalid phone should fail", ValidationUtil.isValidPhoneNumber("123")); + // Create a very long phone number + StringBuilder longPhone = new StringBuilder(); + for (int i = 0; i < 25; i++) { + longPhone.append("1"); + } + assertFalse("Invalid phone should fail", ValidationUtil.isValidPhoneNumber(longPhone.toString())); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 9d00873..76f770d 100644 --- a/pom.xml +++ b/pom.xml @@ -69,6 +69,17 @@ springfox-swagger-ui 2.9.2
+ + + org.jodconverter + jodconverter-core + 4.4.8 + + + org.jodconverter + jodconverter-local + 4.4.8 +