diff --git a/src/main/java/com/teaching/backend/common/ErrorCode.java b/src/main/java/com/teaching/backend/common/ErrorCode.java index 95830fb..4883d57 100644 --- a/src/main/java/com/teaching/backend/common/ErrorCode.java +++ b/src/main/java/com/teaching/backend/common/ErrorCode.java @@ -20,7 +20,8 @@ public enum ErrorCode { CONTENT_NOT_EXIT(40008, "内容id不存在"), KNOWS_EXIT(40009, "该项下面存在关联的知识点,请在删除关联的知识点后再来操作!"), INVALID_ROLE(400010, "角色不存在"), - + MESSAGE_NO_READ(400011, "消息未读,禁止删除!"), + MESSAGE_NO_EXIT(400012, "消息不存在!"), NOT_LOGIN_ERROR(40100, "未登录"), NO_AUTH_ERROR(40101, "无权限"), NOT_FOUND_ERROR(40400, "请求数据不存在"), diff --git a/src/main/java/com/teaching/backend/controller/message/MessagesController.java b/src/main/java/com/teaching/backend/controller/message/MessagesController.java new file mode 100644 index 0000000..be95f7f --- /dev/null +++ b/src/main/java/com/teaching/backend/controller/message/MessagesController.java @@ -0,0 +1,79 @@ +package com.teaching.backend.controller.message; + + +import com.teaching.backend.common.BaseResponse; +import com.teaching.backend.common.ResultUtils; +import com.teaching.backend.filter.ValidateParams; +import com.teaching.backend.model.dto.courses.PageDTO; +import com.teaching.backend.model.dto.message.SysMessageDTO; +import com.teaching.backend.model.entity.message.SysMessages; +import com.teaching.backend.model.query.CourseQuery; +import com.teaching.backend.model.query.MessagesQuery; +import com.teaching.backend.model.vo.courses.CoursesVO; +import com.teaching.backend.model.vo.message.SysMessageVO; +import com.teaching.backend.service.message.IMessagesService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDateTime; +import java.util.List; + +/** + *

+ * 前端控制器 + *

+ * + * @author zjh + * @since 2024-08-06 + */ +@Api(tags = "系统消息管理接口") +@RestController +@RequestMapping("/messages") +public class MessagesController { + + @Autowired + private IMessagesService messagesService; + + @ApiOperation("发送消息") + @ValidateParams({"senderId","title","content","receiverId"}) // 需要校验的参数 + @PostMapping("/addmessage") + public BaseResponse sendMessage(@RequestBody SysMessageDTO sysMessageDTO){ + String data = messagesService.sendMessageToUserInbox(sysMessageDTO); + return ResultUtils.success(data); + } + + //TODO 功能待完善 + @ApiOperation("删除发件箱的消息--批删--逻辑删除") + @ValidateParams({"messageIds"}) // 需要校验的参数 + @DeleteMapping ("/deletesendmessage") + public BaseResponse deleteMessages(@RequestParam List messageIds){ + String data = messagesService.deleteSendMessagesByLogical(messageIds); + return ResultUtils.success(data); + } + + + + @ApiOperation("根据id查询消息") + @ValidateParams({"id"}) // 需要校验的参数 + @GetMapping ("/getmessage/{id}") + public BaseResponse getMessageById(@PathVariable("id") Long id){ + SysMessages data = messagesService.getById(id); + return ResultUtils.success(data); + } + + /** + * 修改消息 + * @param sysMessageDTO + * @return + */ + @ApiOperation("修改消息--仅限于未发送成功的消息(草稿功能)") + @PutMapping ("/modifymessage") + public BaseResponse modifyMessage(@RequestBody SysMessageDTO sysMessageDTO){ + boolean b = messagesService.updateById(sysMessageDTO); + return ResultUtils.success(b ? "修改成功" : "修改失败"); + } + + +} diff --git a/src/main/java/com/teaching/backend/controller/message/UserInboxController.java b/src/main/java/com/teaching/backend/controller/message/UserInboxController.java new file mode 100644 index 0000000..0fb7469 --- /dev/null +++ b/src/main/java/com/teaching/backend/controller/message/UserInboxController.java @@ -0,0 +1,83 @@ +package com.teaching.backend.controller.message; + + +import com.teaching.backend.common.BaseResponse; +import com.teaching.backend.common.ResultUtils; +import com.teaching.backend.filter.ValidateParams; +import com.teaching.backend.model.dto.courses.PageDTO; +import com.teaching.backend.model.query.MessagesQuery; +import com.teaching.backend.model.vo.message.SysMessageVO; +import com.teaching.backend.service.message.IUserInboxService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + *

+ * 前端控制器 + *

+ * + * @author zjh + * @since 2024-08-06 + */ +@Api(tags = "用户信箱管理接口") +@RestController +@RequestMapping("/user-inbox") +public class UserInboxController { + + + @Autowired + private IUserInboxService userInboxService; + @ApiOperation("用户分页查看个人消息(收件箱)") + @ValidateParams({"userId","isDelete"}) // 需要校验的参数 + @GetMapping("/page/receivemessages") + public BaseResponse> getMessages(MessagesQuery messagesQuery){ + PageDTO messageList = userInboxService.queryReceiveMessages(messagesQuery); + return ResultUtils.success(messageList); + } + + @ApiOperation("用户分页查看个人消息(发件箱)") + @ValidateParams({"userId","isDelete","isSend"}) + @GetMapping("/page/sendmessages") + public BaseResponse> getSendMessages(MessagesQuery messagesQuery){ + PageDTO messageList = userInboxService.querySendMessages(messagesQuery); + return ResultUtils.success(messageList); + } + + //用户查看消息详情,如果是第一次查看,则会更新“已读状态”和“阅读时间” + @ApiOperation("查看消息详情") + @ValidateParams({"userId","messageId"}) // 需要校验的参数 + @GetMapping("/getmessagedetails") + public BaseResponse getMessageDetail(MessagesQuery messagesQuery){ + SysMessageVO userCheckMessageVO = userInboxService.getMessageDetail(messagesQuery); + return ResultUtils.success(userCheckMessageVO); + } + + @ApiOperation("获取已读/未读用户集合") + @ValidateParams({"userId", "messageId"}) + @GetMapping("/message/readstatus") + public BaseResponse getMessageReadStatus(@RequestParam Long userId, @RequestParam Long messageId) { + SysMessageVO messageVO = userInboxService.getReadStatus(userId, messageId); + return ResultUtils.success(messageVO); + } + + + @ApiOperation("一键已读") + @PutMapping ("/readmessagesbatch") + public BaseResponse readMessagesBatch(@RequestParam List messageIds, @RequestParam Long userId){ + String data = userInboxService.readMessagesBatch(messageIds,userId); + return ResultUtils.success(data); + } + + @ApiOperation("删除收件箱的消息--批删--逻辑删除") + @ValidateParams({"messageIds","userId"}) // 需要校验的参数 + @DeleteMapping ("/deletereceivemessage") + public BaseResponse deleteReceiveMessages(@RequestParam List messageIds,@RequestParam Long userId){ + String data = userInboxService.deleteReceiveMessagesByLogical(messageIds,userId); + return ResultUtils.success(data); + } + +} diff --git a/src/main/java/com/teaching/backend/mapper/message/MessagesMapper.java b/src/main/java/com/teaching/backend/mapper/message/MessagesMapper.java new file mode 100644 index 0000000..7c267c4 --- /dev/null +++ b/src/main/java/com/teaching/backend/mapper/message/MessagesMapper.java @@ -0,0 +1,16 @@ +package com.teaching.backend.mapper.message; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.teaching.backend.model.entity.message.SysMessages; + +/** + *

+ * Mapper 接口 + *

+ * + * @author zjh + * @since 2024-08-06 + */ +public interface MessagesMapper extends BaseMapper { + +} diff --git a/src/main/java/com/teaching/backend/mapper/message/UserInboxMapper.java b/src/main/java/com/teaching/backend/mapper/message/UserInboxMapper.java new file mode 100644 index 0000000..bf18b1f --- /dev/null +++ b/src/main/java/com/teaching/backend/mapper/message/UserInboxMapper.java @@ -0,0 +1,19 @@ +package com.teaching.backend.mapper.message; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.teaching.backend.model.entity.message.UserInbox; + +import java.util.List; + +/** + *

+ * Mapper 接口 + *

+ * + * @author zjh + * @since 2024-08-06 + */ +public interface UserInboxMapper extends BaseMapper { + + boolean insertBatchInBox(List userInboxes); +} diff --git a/src/main/java/com/teaching/backend/model/dto/message/SysMessageDTO.java b/src/main/java/com/teaching/backend/model/dto/message/SysMessageDTO.java new file mode 100644 index 0000000..87a82c5 --- /dev/null +++ b/src/main/java/com/teaching/backend/model/dto/message/SysMessageDTO.java @@ -0,0 +1,14 @@ +package com.teaching.backend.model.dto.message; + +import com.teaching.backend.model.entity.message.SysMessages; +import io.swagger.annotations.ApiModel; +import lombok.Data; + +@Data +@ApiModel(description = "教师创建课程参数实体") +public class SysMessageDTO extends SysMessages { + /** + * 收件人id集合 + */ + private String receiverId; +} diff --git a/src/main/java/com/teaching/backend/model/entity/message/SysMessages.java b/src/main/java/com/teaching/backend/model/entity/message/SysMessages.java new file mode 100644 index 0000000..1677b01 --- /dev/null +++ b/src/main/java/com/teaching/backend/model/entity/message/SysMessages.java @@ -0,0 +1,56 @@ +package com.teaching.backend.model.entity.message; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import java.time.LocalDateTime; +import java.io.Serializable; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + *

+ * + *

+ * + * @author zjh + * @since 2024-08-06 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("sys_messages") +@ApiModel(value="SysMessages对象", description="") +public class SysMessages implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + @ApiModelProperty(value = "发件人id") + private Long senderId; + + @ApiModelProperty(value = "课程id") + private String courseId; + + @ApiModelProperty(value = "消息标题") + private String title; + + @ApiModelProperty(value = "消息内容") + private String content; + + @ApiModelProperty(value = "发送时间") + private LocalDateTime sendTime; + + @ApiModelProperty(value = "是否删除(逻辑删除)") + private Integer isDelete; + + @ApiModelProperty(value = "是否发送(未发送的存草稿)") + private Boolean isSend; + + +} diff --git a/src/main/java/com/teaching/backend/model/entity/message/UserInbox.java b/src/main/java/com/teaching/backend/model/entity/message/UserInbox.java new file mode 100644 index 0000000..aca75c1 --- /dev/null +++ b/src/main/java/com/teaching/backend/model/entity/message/UserInbox.java @@ -0,0 +1,50 @@ +package com.teaching.backend.model.entity.message; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import java.time.LocalDateTime; +import java.io.Serializable; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + *

+ * + *

+ * + * @author zjh + * @since 2024-08-06 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("user_inbox") +@ApiModel(value="UserInbox对象", description="") +public class UserInbox implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + @ApiModelProperty(value = "消息id") + private Long messageId; + + @ApiModelProperty(value = "接收人id") + private Long receiverId; + + @ApiModelProperty(value = "消息查看时间") + private LocalDateTime readTime; + + @ApiModelProperty(value = "是否已读(0没有,1有)") + private Boolean isRead; + + @ApiModelProperty(value = "是否删除(0没有,1有)") + private Boolean isDelete; + + +} diff --git a/src/main/java/com/teaching/backend/model/query/CourseQuery.java b/src/main/java/com/teaching/backend/model/query/CourseQuery.java index b8d1e73..0e523dc 100644 --- a/src/main/java/com/teaching/backend/model/query/CourseQuery.java +++ b/src/main/java/com/teaching/backend/model/query/CourseQuery.java @@ -25,11 +25,11 @@ public class CourseQuery extends PageQuery { private String teacher; @ApiModelProperty("课程类别") - private String category; + private Integer category; @ApiModelProperty("课程性质") - private String nature; + private Integer nature; @ApiModelProperty("课程考核类型") - private String assessmenttype; + private Integer assessmenttype; } diff --git a/src/main/java/com/teaching/backend/model/query/MessagesQuery.java b/src/main/java/com/teaching/backend/model/query/MessagesQuery.java new file mode 100644 index 0000000..33172d4 --- /dev/null +++ b/src/main/java/com/teaching/backend/model/query/MessagesQuery.java @@ -0,0 +1,24 @@ +package com.teaching.backend.model.query; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +@ApiModel(description = "系统消息查询条件实体") +public class MessagesQuery extends PageQuery { + @ApiModelProperty(value = "用户的userId",required = true) + private Long userId; + @ApiModelProperty(value = "消息id") + private Long messageId; + @ApiModelProperty(value = "是否发送 false没有 true已发送") + private Boolean isSend; + @ApiModelProperty(value = "是否已读 false没有 true已读 可以作为消息筛选条件 用户不选这个就不用传") + private Boolean isRead; + @ApiModelProperty(value = "是否删除 0没有 1回收站 2回收站已删除(发件人不可见,收件人可见)") + private Integer isDelete; + + +} diff --git a/src/main/java/com/teaching/backend/model/query/PageQuery.java b/src/main/java/com/teaching/backend/model/query/PageQuery.java index 7b8e7da..7896a66 100644 --- a/src/main/java/com/teaching/backend/model/query/PageQuery.java +++ b/src/main/java/com/teaching/backend/model/query/PageQuery.java @@ -41,4 +41,5 @@ public class PageQuery { public Page toMpPageDefaultSortByUpdateTime(){ return toMpPage(new OrderItem("update_time", false)); } + } diff --git a/src/main/java/com/teaching/backend/model/vo/message/SysMessageVO.java b/src/main/java/com/teaching/backend/model/vo/message/SysMessageVO.java new file mode 100644 index 0000000..f4ddd9c --- /dev/null +++ b/src/main/java/com/teaching/backend/model/vo/message/SysMessageVO.java @@ -0,0 +1,53 @@ +package com.teaching.backend.model.vo.message; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Data +public class SysMessageVO { + @ApiModelProperty("id") + private Long id; + + @ApiModelProperty("课程id") + private String courseId; + + @ApiModelProperty("课程名称") + private String courseName; + + @ApiModelProperty(value = "发件人id") + private Long senderId; + @ApiModelProperty(value = "发件人name") + private String senderName; + + @ApiModelProperty(value = "消息标题") + private String title; + + @ApiModelProperty(value = "消息内容") + private String content; + + @ApiModelProperty(value = "发送时间") + private LocalDateTime sendTime; + + @ApiModelProperty(value = "收信总人数") + private Long userNum; + + @ApiModelProperty(value = "已读用户人数") + private Long readUserNum; + + @ApiModelProperty(value = "收件用户信息集合") + private List receiveUsers; + + @ApiModelProperty(value = "是否已读(0没有,1有)") + private Boolean isRead; + + @ApiModelProperty(value = "已读用户集合") + private List readUsers; + + @ApiModelProperty(value = "未读用户集合") + private List noReadUsers; + +} diff --git a/src/main/java/com/teaching/backend/model/vo/message/UserCheckMessageVO.java b/src/main/java/com/teaching/backend/model/vo/message/UserCheckMessageVO.java new file mode 100644 index 0000000..373ade4 --- /dev/null +++ b/src/main/java/com/teaching/backend/model/vo/message/UserCheckMessageVO.java @@ -0,0 +1,36 @@ +package com.teaching.backend.model.vo.message; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class UserCheckMessageVO { + @ApiModelProperty("id") + private Long id; + + @ApiModelProperty("课程id") + private String courseId; + + @ApiModelProperty("课程名称") + private String courseName; + + @ApiModelProperty(value = "发件人id") + private Long senderId; + @ApiModelProperty(value = "发件人name") + private String senderName; + + @ApiModelProperty(value = "消息标题") + private String title; + + @ApiModelProperty(value = "消息内容") + private String content; + + @ApiModelProperty(value = "发送时间") + private LocalDateTime sendTime; + + @ApiModelProperty(value = "是否已读(0没有,1有)") + private Boolean isRead; + +} diff --git a/src/main/java/com/teaching/backend/model/vo/message/UserReadMessageTimeVO.java b/src/main/java/com/teaching/backend/model/vo/message/UserReadMessageTimeVO.java new file mode 100644 index 0000000..413ce06 --- /dev/null +++ b/src/main/java/com/teaching/backend/model/vo/message/UserReadMessageTimeVO.java @@ -0,0 +1,21 @@ +package com.teaching.backend.model.vo.message; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +public class UserReadMessageTimeVO { + + @ApiModelProperty(value = "接收人id") + private Long id; + + @ApiModelProperty(value = "接收人name") + private String receiverName; + + @ApiModelProperty(value = "消息查看时间") + private LocalDateTime readTime; + +} diff --git a/src/main/java/com/teaching/backend/service/impl/courses/CoursesServiceImpl.java b/src/main/java/com/teaching/backend/service/impl/courses/CoursesServiceImpl.java index 313528a..ebfe8ae 100644 --- a/src/main/java/com/teaching/backend/service/impl/courses/CoursesServiceImpl.java +++ b/src/main/java/com/teaching/backend/service/impl/courses/CoursesServiceImpl.java @@ -195,10 +195,10 @@ public class CoursesServiceImpl extends ServiceImpl impl Page p = lambdaQuery() .like(courseQuery.getName() != null, Courses::getName, courseQuery.getName()) .apply("FIND_IN_SET({0}, teacher)", courseQuery.getUserId()) - .eq(StringUtils.hasText(courseQuery.getCategory()), Courses::getCategory, courseQuery.getCategory()) -// .eq(courseQuery.getCategory() != null && !courseQuery.getCategory().isEmpty(), Courses::getCategory, courseQuery.getCategory()) - .eq(courseQuery.getNature() != null && !courseQuery.getNature().isEmpty(), Courses::getNature, courseQuery.getNature()) - .eq(courseQuery.getAssessmenttype() != null &&!courseQuery.getAssessmenttype().isEmpty(), Courses::getAssessmenttype, courseQuery.getAssessmenttype()) +// .eq(StringUtils.hasText(courseQuery.getCategory()), Courses::getCategory, courseQuery.getCategory()) + .eq(courseQuery.getCategory() != null, Courses::getCategory, courseQuery.getCategory()) + .eq(courseQuery.getNature() != null, Courses::getNature, courseQuery.getNature()) + .eq(courseQuery.getAssessmenttype() != null, Courses::getAssessmenttype, courseQuery.getAssessmenttype()) .select(Courses::getId,Courses::getTeacher,Courses::getImg,Courses::getName,Courses::getCredit,Courses::getClasshours) .page(page); return PageDTO.of(p,CoursesVO.class); @@ -222,9 +222,9 @@ public class CoursesServiceImpl extends ServiceImpl impl Page page = courseQuery.toMpPageDefaultSortByCreateTime(); Page p = lambdaQuery() .like(courseQuery.getName() != null && !courseQuery.getName().isEmpty(), Courses::getName, courseQuery.getName()) - .eq(courseQuery.getCategory() != null && !courseQuery.getCategory().isEmpty(), Courses::getCategory, courseQuery.getCategory()) - .eq(courseQuery.getNature() != null && !courseQuery.getNature().isEmpty(), Courses::getNature, courseQuery.getNature()) - .eq(courseQuery.getAssessmenttype() != null &&!courseQuery.getAssessmenttype().isEmpty(), Courses::getAssessmenttype, courseQuery.getAssessmenttype()) + .eq(courseQuery.getCategory() != null, Courses::getCategory, courseQuery.getCategory()) + .eq(courseQuery.getNature() != null, Courses::getNature, courseQuery.getNature()) + .eq(courseQuery.getAssessmenttype() != null, Courses::getAssessmenttype, courseQuery.getAssessmenttype()) .apply(courseQuery.getTeacher() != null && !courseQuery.getTeacher().isEmpty(), "FIND_IN_SET({0}, teacher)", courseQuery.getTeacher()) .select(Courses::getId,Courses::getTeacher,Courses::getImg,Courses::getName,Courses::getCredit,Courses::getClasshours) .page(page); diff --git a/src/main/java/com/teaching/backend/service/impl/message/MessagesServiceImpl.java b/src/main/java/com/teaching/backend/service/impl/message/MessagesServiceImpl.java new file mode 100644 index 0000000..b20226c --- /dev/null +++ b/src/main/java/com/teaching/backend/service/impl/message/MessagesServiceImpl.java @@ -0,0 +1,134 @@ +package com.teaching.backend.service.impl.message; + + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.teaching.backend.common.ErrorCode; +import com.teaching.backend.exception.BusinessException; +import com.teaching.backend.mapper.message.MessagesMapper; +import com.teaching.backend.mapper.message.UserInboxMapper; +import com.teaching.backend.model.dto.message.SysMessageDTO; +import com.teaching.backend.model.entity.message.SysMessages; +import com.teaching.backend.model.entity.message.UserInbox; +import com.teaching.backend.service.message.IMessagesService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; + +/** + *

+ * 服务实现类 + *

+ * + * @author zjh + * @since 2024-08-06 + */ +@Service +@Slf4j +public class MessagesServiceImpl extends ServiceImpl implements IMessagesService { + + @Autowired + private UserInboxMapper userInboxMapper; + @Override + @Transactional + public String sendMessageToUserInbox(SysMessageDTO sysMessageDTO) { + // 1.数据非空校验(已完成) + + // 2.消息ID检查,如果存在,说明是草稿箱编辑过的消息,否则生成新的ID + Long messageId = Optional.ofNullable(sysMessageDTO.getId()) + .orElseGet(() -> System.currentTimeMillis() + ThreadLocalRandom.current().nextLong(1000)); + + // 3.解析传过来的receiverId + List receiverIds = Arrays.stream(sysMessageDTO.getReceiverId().split(",")) + .map(Long::valueOf) + .collect(Collectors.toList()); + + // 4.数据写入收件箱 + List userInboxes = receiverIds.stream() + .map(receiverId -> { + UserInbox userInbox = new UserInbox(); + userInbox.setMessageId(messageId); + userInbox.setReceiverId(receiverId); + return userInbox; + }) + .collect(Collectors.toList()); + + // 执行批量插入操作,并判断是否成功 + boolean inboxInsertSuccess = userInboxMapper.insertBatchInBox(userInboxes); + if (!inboxInsertSuccess) { + throw new BusinessException(ErrorCode.OPERATION_ERROR,"收件箱数据插入失败!"); + } + + // 5.更新或保存消息数据 + SysMessages sysMessages = new SysMessages(); + BeanUtils.copyProperties(sysMessageDTO, sysMessages); + sysMessages.setId(messageId); + sysMessages.setSendTime(LocalDateTime.now()); + sysMessages.setIsSend(true); + + boolean messageSaveSuccess = updateById(sysMessages); + if (!messageSaveSuccess) { + throw new BusinessException(ErrorCode.OPERATION_ERROR,"消息保存失败!"); + } + return "消息发送成功!"; + } + + + /** + * 发件箱的删除 + * + * @param messageIds 消息ID列表 + * @return 删除结果 + */ + @Override + @Transactional + public String deleteSendMessagesByLogical(List messageIds) { + for (Long messageId : messageIds) { + // 1.查询消息体 + SysMessages message = query().getBaseMapper().selectById(messageId); + if (message == null) { + log.error("消息未找到,消息id:{}", messageId); + continue; + } + // 2.根据消息状态处理删除逻辑 + switch (message.getIsDelete()) { + case 0: // 未删除状态 + if (Boolean.FALSE.equals(message.getIsSend())) { + // 未发送,物理删除 + if (query().getBaseMapper().deleteById(messageId) < 1) { + log.error("删除失败,消息id:{}", messageId); + } + } else { + // 已发送,标记为回收站 + updateMessageDeleteStatus(message, 1); + } + break; + case 1: // 回收站状态 + // 标记为彻底删除 + updateMessageDeleteStatus(message, 2); + break; + default: + log.warn("未知删除状态,消息id:{}", messageId); + break; + } + } + return "删除成功"; + } + + private void updateMessageDeleteStatus(SysMessages message, int deleteStatus) { + message.setIsDelete(deleteStatus); + if (query().getBaseMapper().updateById(message) < 1) { + log.error("删除失败,消息id:{}", message.getId()); + } + } + +} diff --git a/src/main/java/com/teaching/backend/service/impl/message/UserInboxServiceImpl.java b/src/main/java/com/teaching/backend/service/impl/message/UserInboxServiceImpl.java new file mode 100644 index 0000000..a3de4f1 --- /dev/null +++ b/src/main/java/com/teaching/backend/service/impl/message/UserInboxServiceImpl.java @@ -0,0 +1,445 @@ +package com.teaching.backend.service.impl.message; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.teaching.backend.common.ErrorCode; +import com.teaching.backend.exception.BusinessException; +import com.teaching.backend.mapper.courses.CoursesMapper; +import com.teaching.backend.mapper.message.UserInboxMapper; +import com.teaching.backend.mapper.umsAdmin.UmsStudentMapper; +import com.teaching.backend.mapper.umsAdmin.UmsTeacherMapper; +import com.teaching.backend.model.dto.courses.PageDTO; +import com.teaching.backend.model.entity.courses.Courses; +import com.teaching.backend.model.entity.message.SysMessages; +import com.teaching.backend.model.entity.message.UserInbox; +import com.teaching.backend.model.entity.umsAdmin.UmsStudent; +import com.teaching.backend.model.entity.umsAdmin.UmsTeacher; +import com.teaching.backend.model.query.MessagesQuery; +import com.teaching.backend.model.vo.message.SysMessageVO; +import com.teaching.backend.model.vo.message.UserReadMessageTimeVO; +import com.teaching.backend.service.message.IMessagesService; +import com.teaching.backend.service.message.IUserInboxService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 服务实现类 + *

+ * + * @author zjh + * @since 2024-08-06 + */ +@Service +@Slf4j +public class UserInboxServiceImpl extends ServiceImpl implements IUserInboxService { + + @Autowired + private UmsTeacherMapper umsTeacherMapper; + + @Autowired + private UmsStudentMapper umsStudentMapper; + @Autowired + private CoursesMapper coursesMapper; + @Autowired + private IMessagesService messagesService; + @Autowired + private UserInboxMapper userInboxMapper; + + //TODO 不需要特别指定“已读”或者是“未读” + @Override + public PageDTO queryReceiveMessages(MessagesQuery messagesQuery) { + // 先按用户的id找到其的消息列表 默认看未删除的,(是否读 作为筛选条件后传进去) + LambdaQueryWrapper userInboxQueryWrapper = new LambdaQueryWrapper<>(); + userInboxQueryWrapper + .eq(UserInbox::getReceiverId, messagesQuery.getUserId()) + .eq(messagesQuery.getIsRead() != null,UserInbox::getIsRead,messagesQuery.getIsRead()) + .eq(UserInbox::getIsDelete, messagesQuery.getIsDelete()); + List userInboxes = userInboxMapper.selectList(userInboxQueryWrapper); + if (userInboxes.isEmpty()){ + throw new BusinessException(ErrorCode.MESSAGE_NO_EXIT); + } + Map messageReadStatusMap = userInboxes.stream() + .collect(Collectors.toMap(UserInbox::getMessageId, UserInbox::getIsRead)); + + List messageIds = new ArrayList<>(messageReadStatusMap.keySet()); + + Page page = messagesQuery.toMpPage("send_time", false); + + // 根据条件查询SysMessages表 + Page p = messagesService.page(page, + new LambdaQueryWrapper() + .in(SysMessages::getId,messageIds) + .select(SysMessages::getId,SysMessages::getCourseId,SysMessages::getTitle,SysMessages::getSenderId,SysMessages::getSendTime) + ); + + // 将Page转换为PageDTO + + PageDTO messages = PageDTO.of(p, SysMessageVO.class); +// PageDTO messages = PageDTO.of(p, message -> { +// SysMessageVO vo = new SysMessageVO(); +// BeanUtil.copyProperties(message, vo, CopyOptions.create().setIgnoreProperties(SysMessageVO::getContent)); +// return vo; +// }); + // 获取PageDTO中的SysMessageVO列表 + List messagesList = messages.getList(); + // 填充附加数据 + for (SysMessageVO messageVO : messagesList) { + //获取发件人信息 + UmsTeacher umsTeacher = umsTeacherMapper.selectOne( + new LambdaQueryWrapper() + .eq(UmsTeacher::getUserId, messageVO.getSenderId())); + //没有这个教师那说明就是学生 + if (umsTeacher == null) { + messageVO.setSenderName(umsStudentMapper.selectOne( + new LambdaQueryWrapper() + .eq(UmsStudent::getUserId, messageVO.getSenderId()) + ).getName()); + } else { + messageVO.setSenderName(umsTeacher.getName()); + } + + Courses course = coursesMapper.selectOne(new LambdaQueryWrapper() + .eq(Courses::getId, messageVO.getCourseId())); + if (course != null) { + messageVO.setCourseName(course.getName()); + } + + // 设置isRead字段 + Boolean isRead = messageReadStatusMap.get(messageVO.getId()); + messageVO.setIsRead(isRead != null ? isRead : false); + } + // 设置消息列表并返回 + messages.setList(messagesList); + return messages; + } + + @Override + public PageDTO querySendMessages(MessagesQuery messagesQuery) { + // 初始化一个按发送时间降序排序的Page对象 + Page page = messagesQuery.toMpPage("send_time", false); + + // 根据条件查询SysMessages表 + Page p = messagesService.page(page, + new LambdaQueryWrapper() + .eq(SysMessages::getSenderId, messagesQuery.getUserId()) + .eq(SysMessages::getIsSend, messagesQuery.getIsSend()) + .eq(SysMessages::getIsDelete, messagesQuery.getIsDelete()) + .select(SysMessages::getId,SysMessages::getCourseId,SysMessages::getTitle,SysMessages::getSendTime)); + + // 将Page转换为PageDTO +// PageDTO messages = PageDTO.of(p, sysMessages -> { +// SysMessageVO vo = new SysMessageVO(); +// BeanUtil.copyProperties(sysMessages, vo, CopyOptions.create().setIgnoreProperties(SysMessageVO::getSenderId)); +// return vo; +// }); + PageDTO messages = PageDTO.of(p, SysMessageVO.class); + // 获取PageDTO中的SysMessageVO列表 + List messagesList = messages.getList(); + + if (messagesList.isEmpty()) { + throw new BusinessException(ErrorCode.MESSAGE_NO_EXIT); + } + + // 遍历每个消息以填充附加数据 + for (SysMessageVO sysMessageVO : messagesList) { + // 设置课程名称 + if (!StringUtils.isEmpty(sysMessageVO.getCourseId())) { + Courses courses = coursesMapper.selectById(sysMessageVO.getCourseId()); + if (courses != null) { + sysMessageVO.setCourseName(courses.getName()); + } + } + + // 查询UserInbox以获取当前消息的所有收件人数量 + Long totalReceiverCount = userInboxMapper.selectCount( + new LambdaQueryWrapper() + .eq(UserInbox::getMessageId, sysMessageVO.getId()) + ); + + sysMessageVO.setUserNum(totalReceiverCount); // 设置收信总人数 + + // 查询前5个UserInbox以获取当前消息的收件人列表 + List userInboxes = userInboxMapper.selectList( + new LambdaQueryWrapper() + .eq(UserInbox::getMessageId, sysMessageVO.getId()) + .select(UserInbox::getReceiverId, UserInbox::getIsRead) + .last("LIMIT 5") + ); + + // 设置收件用户集合 + List receiveUsers = userInboxes.stream().map(userInbox -> { + UserReadMessageTimeVO vo = new UserReadMessageTimeVO(); + vo.setId(userInbox.getReceiverId()); + + UmsStudent umsStudent = umsStudentMapper.selectOne( + new LambdaQueryWrapper() + .eq(UmsStudent::getUserId, userInbox.getReceiverId())); + if (umsStudent == null) { + vo.setReceiverName(umsTeacherMapper.selectOne( + new LambdaQueryWrapper() + .eq(UmsTeacher::getUserId, userInbox.getReceiverId()) + ).getName()); + } else { + vo.setReceiverName(umsStudent.getName()); + } + return vo; + }).collect(Collectors.toList()); + sysMessageVO.setReceiveUsers(receiveUsers); // 设置收件人信息 + + // 计算已读用户人数 + long readUserCount = userInboxes.stream() + .filter(UserInbox::getIsRead) + .count(); + sysMessageVO.setReadUserNum(readUserCount); + } + + // 更新PageDTO中的消息列表并返回 + messages.setList(messagesList); + return messages; + } + + + + + @Override + public SysMessageVO getMessageDetail(MessagesQuery messagesQuery) { + SysMessages sysMessage = messagesService.getById(messagesQuery.getMessageId()); + SysMessageVO sysMessageVO = new SysMessageVO(); +// BeanUtil.copyProperties(sysMessage, sysMessageVO); + sysMessageVO.setId(messagesQuery.getMessageId()); + sysMessageVO.setContent(sysMessage.getContent()); + + + // 设置课程名称 列表已查 前端自己去拿 +// if (!StringUtils.isEmpty(sysMessage.getCourseId())) { +// Courses courses = coursesMapper.selectById(sysMessage.getCourseId()); +// if (courses != null) { +// sysMessageVO.setCourseName(courses.getName()); +// } +// } + + sysMessageVO.setSenderId(messagesQuery.getUserId()); +// 获取发件人信息 + UmsTeacher umsTeacher = umsTeacherMapper.selectOne( + new LambdaQueryWrapper() + .eq(UmsTeacher::getUserId, sysMessage.getSenderId())); + if (umsTeacher == null) { + sysMessageVO.setSenderName(umsStudentMapper.selectOne( + new LambdaQueryWrapper() + .eq(UmsStudent::getUserId, sysMessage.getSenderId()) + ).getName()); + } else { + sysMessageVO.setSenderName(umsTeacher.getName()); + } + + // 根据当前用户是发件人还是收件人决定查询的内容 + if (sysMessage.getSenderId().equals(messagesQuery.getUserId())) { + // 发件人查看详情,查询所有收件人信息 + List userInboxes = userInboxMapper.selectList( + new LambdaQueryWrapper() + .eq(UserInbox::getMessageId, messagesQuery.getMessageId()) + ); + + List receiveUsers = userInboxes.stream().map(userInbox -> { + UserReadMessageTimeVO vo = new UserReadMessageTimeVO(); + vo.setId(userInbox.getReceiverId()); + UmsStudent umsStudent = umsStudentMapper.selectOne( + new LambdaQueryWrapper() + .eq(UmsStudent::getUserId, userInbox.getReceiverId())); + if (umsStudent == null) { + vo.setReceiverName(umsTeacherMapper.selectOne( + new LambdaQueryWrapper() + .eq(UmsTeacher::getUserId, userInbox.getReceiverId()) + ).getName()); + } else { + vo.setReceiverName(umsStudent.getName()); + } + vo.setReadTime(userInbox.getReadTime()); + return vo; + }).collect(Collectors.toList()); + sysMessageVO.setReceiveUsers(receiveUsers); + } else { + // 收件人查看详情,无需再次查询收件人信息,前端可复用列表中已查询到的部分数据 + UserInbox userInbox = userInboxMapper.selectOne( + new LambdaQueryWrapper() + .eq(UserInbox::getMessageId, messagesQuery.getMessageId()) + .eq(UserInbox::getReceiverId, messagesQuery.getUserId()) + ); + if (userInbox != null) { + if (userInbox.getReadTime() == null) { + userInbox.setReadTime(LocalDateTime.now()); + } + if (!userInbox.getIsRead()) { + userInbox.setIsRead(true); + } + sysMessageVO.setIsRead(userInbox.getIsRead()); + userInboxMapper.updateById(userInbox); + } + } + + return sysMessageVO; + } + + + /** + * 收件箱消息删除(逻辑删除) + * + * @param messageIds 消息ID列表 + * @param userId 用户ID + * @return 删除结果 + */ + @Override + @Transactional + public String deleteReceiveMessagesByLogical(List messageIds, Long userId) { + for (Long messageId : messageIds) { + UserInbox userInbox = getUserInboxByMessageIdAndUserId(messageId, userId); + if (userInbox == null) { + log.error("未找到收件箱消息,消息id:{},用户id:{}", messageId, userId); + continue; + } + + if (!userInbox.getIsRead()) { + log.error("此消息未读:{}", messageId); + throw new BusinessException(ErrorCode.MESSAGE_NO_READ); + } + + if (!userInbox.getIsDelete()) { + userInbox.setIsDelete(true); + userInboxMapper.updateById(userInbox); + } else { + userInboxMapper.deleteById(userInbox); + } + } + return "删除成功!"; + } + /** + * 一键已读 + * + * @param messageIds 消息ID列表 + * @param userId 用户ID + * @return 操作结果 + */ + @Override + @Transactional + public String readMessagesBatch(List messageIds, Long userId) { + for (Long messageId : messageIds) { + UserInbox userInbox = getUserInboxByMessageIdAndUserId(messageId, userId); + if (userInbox == null) { + log.error("未找到收件箱消息,消息id:{},用户id:{}", messageId, userId); + continue; + } + + if (!userInbox.getIsRead()) { + userInbox.setIsRead(true); + userInbox.setReadTime(LocalDateTime.now()); + userInboxMapper.updateById(userInbox); + } + } + return "操作成功!"; + } + private UserInbox getUserInboxByMessageIdAndUserId(Long messageId, Long userId) { + return userInboxMapper.selectOne(new LambdaQueryWrapper() + .eq(UserInbox::getMessageId, messageId) + .eq(UserInbox::getReceiverId, userId)); + } + + /** + * + * @param userId + * @param messageId + * @return + */ + @Override + public SysMessageVO getReadStatus(Long userId, Long messageId) { + // 验证当前用户是否是消息的发件人 + SysMessages sysMessages = messagesService.getById(messageId); + if (sysMessages == null || !sysMessages.getSenderId().equals(userId)) { + throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "您没有权限查看此消息的已读/未读用户信息。"); + } + + SysMessageVO sysMessageVO = new SysMessageVO(); + sysMessageVO.setId(messageId); + + // 查询 UserInbox 以获取当前消息的收件人列表 + List userInboxes = userInboxMapper.selectList( + new LambdaQueryWrapper() + .eq(UserInbox::getMessageId, messageId) + .select(UserInbox::getReceiverId, UserInbox::getIsRead, UserInbox::getReadTime) + ); + + List receiverIds = userInboxes.stream().map(UserInbox::getReceiverId).collect(Collectors.toList()); + sysMessageVO.setUserNum((long) receiverIds.size()); // 设置收信总人数 + + // 提取已读消息的 UserInbox 并按阅读时间降序排序 + List readUserInboxes = userInboxes.stream() + .filter(UserInbox::getIsRead) + .sorted(Comparator.comparing(UserInbox::getReadTime, Comparator.nullsLast(Comparator.reverseOrder()))) + .collect(Collectors.toList()); + + // 设置已读学生人数 + sysMessageVO.setReadUserNum((long) readUserInboxes.size()); + + // 创建已读用户的 UserReadMessageTimeVO 列表 + List readStudents = readUserInboxes.stream() + .map(userInbox -> { + UserReadMessageTimeVO vo = new UserReadMessageTimeVO(); + vo.setId(userInbox.getReceiverId()); + UmsStudent umsStudent = umsStudentMapper.selectOne( + new LambdaQueryWrapper() + .eq(UmsStudent::getUserId, userInbox.getReceiverId())); + if (umsStudent == null) { + vo.setReceiverName(umsTeacherMapper.selectOne( + new LambdaQueryWrapper() + .eq(UmsTeacher::getUserId, userInbox.getReceiverId()) + ).getName()); + } else { + vo.setReceiverName(umsStudent.getName()); + } + vo.setReadTime(userInbox.getReadTime()); + return vo; + }) + .collect(Collectors.toList()); + + // 创建未读用户的 UserReadMessageTimeVO 列表 + List noReadStudents = receiverIds.stream() + .filter(id -> readUserInboxes.stream().noneMatch(ri -> ri.getReceiverId().equals(id))) + .map(id -> { + UserReadMessageTimeVO vo = new UserReadMessageTimeVO(); + vo.setId(id); + UmsStudent umsStudent = umsStudentMapper.selectOne( + new LambdaQueryWrapper() + .eq(UmsStudent::getUserId, id)); + if (umsStudent == null) { + vo.setReceiverName(umsTeacherMapper.selectOne( + new LambdaQueryWrapper() + .eq(UmsTeacher::getUserId, id) + ).getName()); + } else { + vo.setReceiverName(umsStudent.getName()); + } + return vo; + }) + .collect(Collectors.toList()); + + // 设置已读和未读学生集合 + sysMessageVO.setReadUsers(readStudents); + sysMessageVO.setNoReadUsers(noReadStudents); + + return sysMessageVO; + } + +} diff --git a/src/main/java/com/teaching/backend/service/message/IMessagesService.java b/src/main/java/com/teaching/backend/service/message/IMessagesService.java new file mode 100644 index 0000000..687d204 --- /dev/null +++ b/src/main/java/com/teaching/backend/service/message/IMessagesService.java @@ -0,0 +1,27 @@ +package com.teaching.backend.service.message; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.teaching.backend.model.dto.courses.PageDTO; +import com.teaching.backend.model.dto.message.SysMessageDTO; +import com.teaching.backend.model.entity.message.SysMessages; +import com.teaching.backend.model.entity.message.UserInbox; +import com.teaching.backend.model.query.MessagesQuery; +import com.teaching.backend.model.vo.message.SysMessageVO; + +import java.util.List; + +/** + *

+ * 服务类 + *

+ * + * @author zjh + * @since 2024-08-06 + */ +public interface IMessagesService extends IService { + + String sendMessageToUserInbox(SysMessageDTO sysMessageDTO); + + String deleteSendMessagesByLogical(List messageIds); + +} diff --git a/src/main/java/com/teaching/backend/service/message/IUserInboxService.java b/src/main/java/com/teaching/backend/service/message/IUserInboxService.java new file mode 100644 index 0000000..9bc13ba --- /dev/null +++ b/src/main/java/com/teaching/backend/service/message/IUserInboxService.java @@ -0,0 +1,32 @@ +package com.teaching.backend.service.message; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.teaching.backend.model.dto.courses.PageDTO; +import com.teaching.backend.model.entity.message.UserInbox; +import com.teaching.backend.model.query.MessagesQuery; +import com.teaching.backend.model.vo.message.SysMessageVO; + +import java.util.List; + +/** + *

+ * 服务类 + *

+ * + * @author zjh + * @since 2024-08-06 + */ +public interface IUserInboxService extends IService { + + PageDTO queryReceiveMessages(MessagesQuery messagesQuery); + + SysMessageVO getMessageDetail(MessagesQuery messagesQuery); + + PageDTO querySendMessages(MessagesQuery messagesQuery); + + String readMessagesBatch(List messageIds, Long userId); + + String deleteReceiveMessagesByLogical(List messageIds, Long userId); + + SysMessageVO getReadStatus(Long userId, Long messageId); +} diff --git a/src/main/resources/mapper/UserInboxMapper.xml b/src/main/resources/mapper/UserInboxMapper.xml new file mode 100644 index 0000000..99c8bec --- /dev/null +++ b/src/main/resources/mapper/UserInboxMapper.xml @@ -0,0 +1,12 @@ + + + + + + INSERT INTO user_inbox (message_id, receiver_id) + VALUES + + (#{item.messageId}, #{item.receiverId}) + + +