添加消息相关表
This commit is contained in:
@@ -26,7 +26,8 @@ offerpie/back-end
|
||||
│ │ ├─ JobIntentionController.java # 求职意向接口(查询与保存)
|
||||
│ │ ├─ JobController.java # 岗位接口(岗位列表查询、收藏、投递、不感兴趣、求职助手任务列表、AI岗位推荐)
|
||||
│ │ ├─ JobAgentConfigController.java # 求职助手配置接口(配置查询与保存)
|
||||
│ │ └─ UserResumeController.java # 用户简历接口(简历列表、主表及5张子表的查询与保存、子表单条添加/编辑/删除、简历删除、设置默认简历)
|
||||
│ │ ├─ UserResumeController.java # 用户简历接口(简历列表、主表及5张子表的查询与保存、子表单条添加/编辑/删除、简历删除、设置默认简历)
|
||||
│ │ └─ MessageController.java # 站内信消息接口(消息列表、未读数、按类型未读数、标记已读)
|
||||
│ ├─ service/
|
||||
│ │ ├─ LoginService.java # 登录业务逻辑(验证码校验、自动注册、JWT生成、Cookie设置)
|
||||
│ │ ├─ UserRegisterService.java # 用户注册服务(注册逻辑、邀请码生成与绑定)
|
||||
@@ -37,19 +38,22 @@ offerpie/back-end
|
||||
│ │ ├─ JobService.java # 岗位服务(岗位列表查询、匹配度计算编排、求职助手任务列表、AI岗位推荐)
|
||||
│ │ ├─ JobAgentConfigService.java # 求职助手配置服务(配置查询与保存)
|
||||
│ │ ├─ UserResumeService.java # 用户简历服务(简历列表、主表及5张子表的CRUD、子表单条添加/编辑/删除、设置默认简历)
|
||||
│ │ ├─ MessageQueryService.java # 站内信查询服务(消息分页列表、未读数、按类型未读数)
|
||||
│ │ └─ WxPayNotifyMessageAbstractImpl.java # 微信支付回调实现
|
||||
│ └─ pojo/
|
||||
│ ├─ param/
|
||||
│ │ ├─ userProfile/ # 个人资料入参(UserProfileParam、各子表Param)
|
||||
│ │ ├─ resume/ # 简历入参(ResumeParam、各子表Param、各子表UpdateParam、ResumeSubTableParam)
|
||||
│ │ ├─ job/ # 岗位相关入参(JobIntentionParam、JobQueryParam、JobAgentTaskQueryParam、JobAgentRecommendParam)
|
||||
│ │ └─ jobAgent/ # 求职助手入参(JobAgentConfigParam)
|
||||
│ │ ├─ jobAgent/ # 求职助手入参(JobAgentConfigParam)
|
||||
│ │ └─ message/ # 站内信入参(MessageQueryParam)
|
||||
│ ├─ dto/
|
||||
│ │ ├─ SmsLoginDto.java # 短信登录入参(mobileNumber + code + inviteCode)
|
||||
│ │ ├─ userProfile/ # 个人资料出参(UserProfileDto、各子表Dto)
|
||||
│ │ ├─ resume/ # 简历出参(ResumeDto、ResumeListItemDto、各子表Dto)
|
||||
│ │ ├─ job/ # 岗位相关出参(JobIntentionDto、JobDto、JobMatchScoreDto、JobAgentRecommendDto)
|
||||
│ │ └─ jobAgent/ # 求职助手出参(JobAgentConfigDto)
|
||||
│ │ ├─ jobAgent/ # 求职助手出参(JobAgentConfigDto)
|
||||
│ │ └─ message/ # 站内信出参(MessageDto、MessageUnreadCountDto)
|
||||
│ └─ vo/
|
||||
│ ├─ LoginVo.java # 登录返回(userId + nick)
|
||||
│ ├─ LanguageAbility.java # 语言能力对象(求职助手配置JSON字段)
|
||||
@@ -119,6 +123,8 @@ offerpie/back-end
|
||||
│ ├─ JobAgentConfigMapper.java # 求职助手配置Mapper
|
||||
│ ├─ JobAgentChatMessageMapper.java # 求职助手对话消息Mapper
|
||||
│ ├─ UserJobCustomizeResumeMapper.java # 用户岗位定制简历Mapper
|
||||
│ ├─ MessageMapper.java # 站内信消息Mapper
|
||||
│ ├─ MessageReadMapper.java # 消息已读状态Mapper
|
||||
│ └─ AppJobDataMapper.java # 爬虫岗位原始数据Mapper
|
||||
├─ pojo/
|
||||
│ ├─ po/ # 持久化实体
|
||||
@@ -161,11 +167,13 @@ offerpie/back-end
|
||||
│ │ ├─ JobAgentConfig.java # 求职助手配置表(bg_job_agent_config)
|
||||
│ │ ├─ JobAgentChatMessage.java # 求职助手对话消息表(bg_job_agent_chat_message)
|
||||
│ │ ├─ UserJobCustomizeResume.java # 用户岗位定制简历表(bg_user_job_customize_resume)
|
||||
│ │ ├─ Message.java # 站内信消息表(bg_message)
|
||||
│ │ ├─ MessageRead.java # 消息已读状态表(bg_message_read)
|
||||
│ │ └─ AppJobData.java # 爬虫岗位原始数据表(app_job_data)
|
||||
│ └─ vo/ # ViewObject(OssUrlVo、DescriptionParagraph、JobListItemVo、UserHonorsVo 等)
|
||||
├─ resources/mapper/ # MyBatis XML 映射文件
|
||||
│ └─ JobMapper.xml # 岗位自定义SQL(selectJobPage)
|
||||
└─ service/ # 业务 Service(OssService、SmsService、DictCacheService、JobCleanService、JobCleanTransactionService、CompanyCleanService、CompanyCleanTransactionService、UserProfileAnalyzeService、JobMatchService 等)
|
||||
└─ service/ # 业务 Service(OssService、SmsService、DictCacheService、JobCleanService、JobCleanTransactionService、CompanyCleanService、CompanyCleanTransactionService、UserProfileAnalyzeService、JobMatchService、MessageService 等)
|
||||
```
|
||||
> **设计理念** – 业务实体和 Mapper 位于 `manager`,B 端和 C 端共享;C 端特有的注解、切面、权限服务、路由菜单服务位于 `client-api`,避免 B 端误用;`common` 提供统一的技术支撑。
|
||||
|
||||
@@ -174,7 +182,7 @@ offerpie/back-end
|
||||
|------|----------|-----------|
|
||||
| **client-api** | - 面向终端用户的 REST API <br> - 启动 Spring Boot 应用 <br> - 短信验证码登录(含自动注册、邀请码绑定) <br> - **功能权限校验**:注解 + 切面 + 权限服务(校验、扣减、回退) <br> - **路由菜单**:获取用户有效菜单树 <br> - **求职助手**:配置管理、AI岗位推荐、任务列表 | `ClientApplication`、`LoginController`、`RouteMenuController`、`JobController`、`JobAgentConfigController`、`UserResumeController`、`FuncPermission`、`FuncPermissionAspect`、`FuncPermissionService`、`RouteMenuService`、`JobService`、`JobAgentConfigService`、`UserRegisterService`、`RouteMenuVo` |
|
||||
| **common** | - **统一配置**:OSS、Redis、Security、WxPay、Sms、Async 等 <br> - **跨层工具**:HTTP、IP、认证、验证码、Redis Server 等 <br> - **全局拦截/切面**:日志、TraceId、黑名单、SQL 打印 <br> - **统一异常/响应**:`GlobalExceptionAdvice`、`UnifiedResponse` <br> - **业务抽象**:邮件发送、微信支付(Native/JS/Transfer)、异步任务 <br> - **公共 POJO**:登录令牌、防重放信息等 | `config/`, `tool/`, `interceptor/`, `aop/`, `exception/`, `email/`, `wxPay/`, `pojo/` |
|
||||
| **manager** | - **业务实体**(`User`、`OssFile`、`UserInvite`、`RouteMenu`、`FuncPermission`、`UserRouteMenuStock`、`UserFuncPermissionStock`、`UserFuncUsageLog`、`ChinaRegionsCode`、`JobCategory`、`Company`、`Job`、`JobRegionRelation`、`Industry`、`SkillTag`、`UserJobFavorite`、`UserJobApplication`、`UserJobDislike`、`UserJobIntention`、`UserProfile`及5张子表、`UserProfileSkillTagRelation`、`UserResume`及5张子表、`ResumeDiagnosisReport`、`ResumeDiagnosisIssue`、`JobAgentConfig`、`UserJobCustomizeResume`、`AppJobData`) <br> - **MyBatis Mapper**(对应全部业务实体的 Mapper,含 `JobAgentConfigMapper`、`UserJobCustomizeResumeMapper`) <br> - **业务 API**:文件上传/下载、健康检查、地区/岗位分类/行业字典查询 <br> - **业务逻辑**:OssService、SmsService、DictCacheService、JobCleanService、CompanyCleanService、UserProfileAnalyzeService、JobMatchService 等 <br> - **既供 B 端 UI(待实现)使用,也供 C 端业务直接调用** | `controller/`, `mapper/`, `pojo/po/`, `pojo/vo/`, `service/`, `constant/` |
|
||||
| **manager** | - **业务实体**(`User`、`OssFile`、`UserInvite`、`RouteMenu`、`FuncPermission`、`UserRouteMenuStock`、`UserFuncPermissionStock`、`UserFuncUsageLog`、`ChinaRegionsCode`、`JobCategory`、`Company`、`Job`、`JobRegionRelation`、`Industry`、`SkillTag`、`UserJobFavorite`、`UserJobApplication`、`UserJobDislike`、`UserJobIntention`、`UserProfile`及5张子表、`UserProfileSkillTagRelation`、`UserResume`及5张子表、`ResumeDiagnosisReport`、`ResumeDiagnosisIssue`、`JobAgentConfig`、`UserJobCustomizeResume`、`Message`、`MessageRead`、`AppJobData`) <br> - **MyBatis Mapper**(对应全部业务实体的 Mapper,含 `JobAgentConfigMapper`、`UserJobCustomizeResumeMapper`、`MessageMapper`、`MessageReadMapper`) <br> - **业务 API**:文件上传/下载、健康检查、地区/岗位分类/行业字典查询 <br> - **业务逻辑**:OssService、SmsService、DictCacheService、JobCleanService、CompanyCleanService、UserProfileAnalyzeService、JobMatchService、MessageService 等 <br> - **既供 B 端 UI(待实现)使用,也供 C 端业务直接调用** | `controller/`, `mapper/`, `pojo/po/`, `pojo/vo/`, `service/`, `constant/` |
|
||||
|
||||
## 3️⃣ 关键业务实体
|
||||
| 实体 | 所属模块 | 作用概述 |
|
||||
@@ -222,6 +230,8 @@ offerpie/back-end
|
||||
| `JobAgentConfig` | manager | 求职助手配置表(bg_job_agent_config),一个用户一条记录,存储Agent模式、投递目标、网申常见问题预设答案(部门调剂、地点调剂、面试方式、语言能力、到岗时间、实习天数/时长)。 |
|
||||
| `JobAgentChatMessage` | manager | 求职助手对话消息表(bg_job_agent_chat_message),记录用户与求职助手的完整对话流,含4种消息类型(user/assistant/recommend/apply_progress),文本存content,JSON数据存extra由前端维护。 |
|
||||
| `UserJobCustomizeResume` | manager | 用户岗位定制简历表(bg_user_job_customize_resume),一个用户+一个岗位=一份定制简历,content字段存完整CustomizeResume JSON,唯一索引(user_id, job_id)。 |
|
||||
| `Message` | manager | 站内信消息表(bg_message),统一存储系统消息(1)、运营消息(2)、订单消息(3),支持指定用户(targetType=1)和全员推送(targetType=2),可关联业务(bizType+bizId)。 |
|
||||
| `MessageRead` | manager | 消息已读状态表(bg_message_read),记录用户对消息的已读状态,唯一索引(message_id, user_id)。 |
|
||||
|
||||
## 4️⃣ 权限体系设计
|
||||
### 整体架构
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.jiayunet.controller;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.jiayunet.pojo.PageResult;
|
||||
import org.jiayunet.pojo.dto.message.MessageDto;
|
||||
import org.jiayunet.pojo.dto.message.MessageUnreadCountDto;
|
||||
import org.jiayunet.pojo.param.message.MessageQueryParam;
|
||||
import org.jiayunet.service.MessageQueryService;
|
||||
import org.jiayunet.service.MessageService;
|
||||
import org.jiayunet.tool.UserSecurityTool;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 站内信消息接口
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/message")
|
||||
@AllArgsConstructor
|
||||
public class MessageController {
|
||||
|
||||
private final MessageQueryService messageQueryService;
|
||||
private final MessageService messageService;
|
||||
|
||||
/**
|
||||
* 分页查询消息列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public PageResult<MessageDto> listMessages(@Validated MessageQueryParam param) {
|
||||
return messageQueryService.listMessages(param, UserSecurityTool.getUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询未读消息数
|
||||
*/
|
||||
@GetMapping("/unread-count")
|
||||
public Long countUnread() {
|
||||
return messageQueryService.countUnread(UserSecurityTool.getUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按类型查询未读消息数
|
||||
*/
|
||||
@GetMapping("/unread-count-by-type")
|
||||
public MessageUnreadCountDto countUnreadByType() {
|
||||
return messageQueryService.countUnreadByType(UserSecurityTool.getUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记消息已读
|
||||
*/
|
||||
@PostMapping("/read/{messageId}")
|
||||
public void markRead(@PathVariable Long messageId) {
|
||||
messageService.markRead(messageId, UserSecurityTool.getUserId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.jiayunet.pojo.dto.message;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 消息列表出参
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@Data
|
||||
public class MessageDto {
|
||||
|
||||
private Long id;
|
||||
|
||||
/** 消息类型 1=系统消息 2=运营消息 3=订单消息 */
|
||||
private Integer type;
|
||||
|
||||
/** 消息标题 */
|
||||
private String title;
|
||||
|
||||
/** 消息内容 */
|
||||
private String content;
|
||||
|
||||
/** 关联业务类型 */
|
||||
private String bizType;
|
||||
|
||||
/** 关联业务ID */
|
||||
private Long bizId;
|
||||
|
||||
/** 是否已读 */
|
||||
private Boolean read;
|
||||
|
||||
/** 创建时间 */
|
||||
private Instant createTime;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.jiayunet.pojo.dto.message;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 各类型消息未读数出参
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@Data
|
||||
public class MessageUnreadCountDto {
|
||||
|
||||
/** 总未读数 */
|
||||
private Long total;
|
||||
|
||||
/** 系统消息未读数 */
|
||||
private Long system;
|
||||
|
||||
/** 运营消息未读数 */
|
||||
private Long operation;
|
||||
|
||||
/** 订单消息未读数 */
|
||||
private Long order;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.jiayunet.pojo.param.message;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.jiayunet.pojo.PageParam;
|
||||
|
||||
/**
|
||||
* 消息分页查询入参
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MessageQueryParam extends PageParam {
|
||||
|
||||
/** 消息类型筛选,null=全部 */
|
||||
private Integer type;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package org.jiayunet.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.jiayunet.mapper.MessageMapper;
|
||||
import org.jiayunet.mapper.MessageReadMapper;
|
||||
import org.jiayunet.pojo.PageResult;
|
||||
import org.jiayunet.pojo.dto.message.MessageDto;
|
||||
import org.jiayunet.pojo.dto.message.MessageUnreadCountDto;
|
||||
import org.jiayunet.pojo.param.message.MessageQueryParam;
|
||||
import org.jiayunet.pojo.po.Message;
|
||||
import org.jiayunet.pojo.po.MessageRead;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* C端消息查询服务
|
||||
* <p>提供用户消息分页列表和未读数查询</p>
|
||||
* <p>依赖:MessageMapper、MessageReadMapper</p>
|
||||
* <p>使用表:bg_message(查询消息)、bg_message_read(查询已读状态)</p>
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@Service
|
||||
public class MessageQueryService {
|
||||
|
||||
@Autowired
|
||||
private MessageMapper messageMapper;
|
||||
|
||||
@Autowired
|
||||
private MessageReadMapper messageReadMapper;
|
||||
|
||||
/**
|
||||
* 分页查询用户消息列表
|
||||
* <p>1. 查询用户可见消息(指定用户+全员)分页 2. 批量查已读状态 3. 组装DTO返回</p>
|
||||
*/
|
||||
public PageResult<MessageDto> listMessages(MessageQueryParam param, Long userId) {
|
||||
Page<Message> page = messageMapper.selectPage(param.toPage(), new LambdaQueryWrapper<Message>()
|
||||
.and(w -> w.eq(Message::getUserId, userId).or().eq(Message::getTargetType, 2))
|
||||
.eq(param.getType() != null, Message::getType, param.getType()).orderByDesc(Message::getCreateTime));
|
||||
|
||||
List<Long> messageIds = page.getRecords().stream().map(Message::getId).collect(Collectors.toList());
|
||||
|
||||
// 批量查已读状态
|
||||
Set<Long> readIds = messageIds.isEmpty() ? Set.of() : messageReadMapper.selectList(new LambdaQueryWrapper<MessageRead>().eq(MessageRead::getUserId, userId).in(MessageRead::getMessageId, messageIds)).stream().map(MessageRead::getMessageId).collect(Collectors.toSet());
|
||||
|
||||
List<MessageDto> dtoList = page.getRecords().stream().map(msg -> {
|
||||
MessageDto dto = new MessageDto();
|
||||
BeanUtils.copyProperties(msg, dto);
|
||||
dto.setRead(readIds.contains(msg.getId()));
|
||||
return dto;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
return new PageResult<>(page.getCurrent(), page.getSize(), page.getTotal(), dtoList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询未读消息数
|
||||
*/
|
||||
public Long countUnread(Long userId) {
|
||||
Long totalCount = messageMapper.selectCount(new LambdaQueryWrapper<Message>().and(w -> w.eq(Message::getUserId, userId).or().eq(Message::getTargetType, 2)));
|
||||
Long readCount = messageReadMapper.selectCount(new LambdaQueryWrapper<MessageRead>().eq(MessageRead::getUserId, userId));
|
||||
return Math.max(0, totalCount - readCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按类型查询未读消息数
|
||||
* <p>分别统计系统消息(1)、运营消息(2)、订单消息(3)的未读数量</p>
|
||||
*/
|
||||
public MessageUnreadCountDto countUnreadByType(Long userId) {
|
||||
Set<Long> readIds = messageReadMapper.selectList(new LambdaQueryWrapper<MessageRead>().eq(MessageRead::getUserId, userId).select(MessageRead::getMessageId)).stream().map(MessageRead::getMessageId).collect(Collectors.toSet());
|
||||
|
||||
Map<Integer, Long> unreadMap = messageMapper.selectList(new LambdaQueryWrapper<Message>()
|
||||
.and(w -> w.eq(Message::getUserId, userId).or().eq(Message::getTargetType, 2)).select(Message::getId, Message::getType))
|
||||
.stream().filter(msg -> !readIds.contains(msg.getId())).collect(Collectors.groupingBy(Message::getType, Collectors.counting()));
|
||||
|
||||
MessageUnreadCountDto dto = new MessageUnreadCountDto();
|
||||
dto.setSystem(unreadMap.getOrDefault(1, 0L));
|
||||
dto.setOperation(unreadMap.getOrDefault(2, 0L));
|
||||
dto.setOrder(unreadMap.getOrDefault(3, 0L));
|
||||
dto.setTotal(dto.getSystem() + dto.getOperation() + dto.getOrder());
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.jiayunet.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.jiayunet.pojo.po.Message;
|
||||
|
||||
/**
|
||||
* 站内信消息Mapper
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@Mapper
|
||||
public interface MessageMapper extends CommonMapper<Message> {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.jiayunet.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.jiayunet.pojo.po.MessageRead;
|
||||
|
||||
/**
|
||||
* 消息已读状态Mapper
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@Mapper
|
||||
public interface MessageReadMapper extends CommonMapper<MessageRead> {
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.jiayunet.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 站内信消息表(bg_message)
|
||||
* <p>统一存储系统消息、运营消息、订单消息</p>
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@Data
|
||||
@TableName("bg_message")
|
||||
public class Message {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/** 消息类型 1=系统消息 2=运营消息 3=订单消息 */
|
||||
private Integer type;
|
||||
|
||||
/** 目标类型 1=指定用户 2=全部用户 */
|
||||
private Integer targetType;
|
||||
|
||||
/** 目标用户ID,targetType=1时必填,2时为NULL */
|
||||
private Long userId;
|
||||
|
||||
/** 消息标题 */
|
||||
private String title;
|
||||
|
||||
/** 消息内容 */
|
||||
private String content;
|
||||
|
||||
/** 关联业务类型,如 order/resume_diagnose */
|
||||
private String bizType;
|
||||
|
||||
/** 关联业务ID,配合 bizType 跳转 */
|
||||
private Long bizId;
|
||||
|
||||
/** 创建时间 */
|
||||
private Instant createTime;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.jiayunet.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 消息已读状态表(bg_message_read)
|
||||
* <p>记录用户对消息的已读状态,用户读了才插入一条记录</p>
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@Data
|
||||
@TableName("bg_message_read")
|
||||
public class MessageRead {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/** 消息ID,关联 bg_message.id */
|
||||
private Long messageId;
|
||||
|
||||
/** 用户ID */
|
||||
private Long userId;
|
||||
|
||||
/** 阅读时间 */
|
||||
private Instant readTime;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.jiayunet.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.jiayunet.mapper.MessageMapper;
|
||||
import org.jiayunet.mapper.MessageReadMapper;
|
||||
import org.jiayunet.pojo.po.Message;
|
||||
import org.jiayunet.pojo.po.MessageRead;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 站内信消息服务
|
||||
* <p>提供消息发送和已读标记能力,供 C 端和 B 端共用</p>
|
||||
* <p>使用表:bg_message(消息主表)、bg_message_read(已读状态表)</p>
|
||||
*
|
||||
* @author zk
|
||||
*/
|
||||
@Service
|
||||
public class MessageService {
|
||||
|
||||
@Autowired
|
||||
private MessageMapper messageMapper;
|
||||
|
||||
@Autowired
|
||||
private MessageReadMapper messageReadMapper;
|
||||
|
||||
/**
|
||||
* 发送消息(指定用户)
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void sendToUser(Integer type, Long userId, String title, String content, String bizType, Long bizId) {
|
||||
Assert.notNull(userId, "目标用户ID不能为空");
|
||||
Message msg = new Message();
|
||||
msg.setType(type);
|
||||
msg.setTargetType(1);
|
||||
msg.setUserId(userId);
|
||||
msg.setTitle(title);
|
||||
msg.setContent(content);
|
||||
msg.setBizType(bizType);
|
||||
msg.setBizId(bizId);
|
||||
msg.setCreateTime(Instant.now());
|
||||
messageMapper.insert(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息(全部用户)
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void sendToAll(Integer type, String title, String content) {
|
||||
Message msg = new Message();
|
||||
msg.setType(type);
|
||||
msg.setTargetType(2);
|
||||
msg.setTitle(title);
|
||||
msg.setContent(content);
|
||||
msg.setCreateTime(Instant.now());
|
||||
messageMapper.insert(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记消息已读(幂等,重复调用不报错)
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void markRead(Long messageId, Long userId) {
|
||||
Long count = messageReadMapper.selectCount(new LambdaQueryWrapper<MessageRead>().eq(MessageRead::getMessageId, messageId).eq(MessageRead::getUserId, userId));
|
||||
if (count > 0) return;
|
||||
MessageRead read = new MessageRead();
|
||||
read.setMessageId(messageId);
|
||||
read.setUserId(userId);
|
||||
read.setReadTime(Instant.now());
|
||||
messageReadMapper.insert(read);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user