package cn.iocoder.yudao.module.bpm.service.oa; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.OaNoticePageReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.OaNoticeSaveReqVO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.OaNoticeDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.OaNoticeReadDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.OaNoticeUserDO; import cn.iocoder.yudao.module.bpm.dal.mysql.oa.OaNoticeMapper; import cn.iocoder.yudao.module.bpm.dal.mysql.oa.OaNoticeReadMapper; import cn.iocoder.yudao.module.bpm.dal.mysql.oa.OaNoticeUserMapper; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.module.bpm.enums.oa.OaNoticeSendScopeEnum; import cn.iocoder.yudao.module.bpm.enums.oa.OaNoticeStatusEnum; import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper; import cn.iocoder.yudao.module.system.service.notify.NotifySendService; import cn.iocoder.yudao.module.system.service.user.AdminUserService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_NOTICE_NOT_EXISTS; /** * OA 通知公告 Service 实现类 */ @Service @Slf4j public class OaNoticeServiceImpl implements OaNoticeService { @Resource private OaNoticeMapper oaNoticeMapper; @Resource private OaNoticeUserMapper oaNoticeUserMapper; @Resource private OaNoticeReadMapper oaNoticeReadMapper; @Resource private NotifySendService notifySendService; @Resource private AdminUserService adminUserService; @Resource private NotifyMessageMapper notifyMessageMapper; @Override @Transactional(rollbackFor = Exception.class) public Long createNotice(Long userId, OaNoticeSaveReqVO createReqVO) { // 创建通知公告 OaNoticeDO notice = BeanUtils.toBean(createReqVO, OaNoticeDO.class); notice.setPublishUserId(userId); if (OaNoticeStatusEnum.PUBLISHED.getStatus().equals(createReqVO.getStatus())) { notice.setPublishTime(LocalDateTime.now()); } oaNoticeMapper.insert(notice); // 指定人写入关联表 if (OaNoticeSendScopeEnum.SPECIFIED.getScope().equals(createReqVO.getSendScope()) && CollUtil.isNotEmpty(createReqVO.getUserIds())) { saveNoticeUsers(notice.getId(), createReqVO.getUserIds()); } // 已发布则发送系统通知 if (OaNoticeStatusEnum.PUBLISHED.getStatus().equals(notice.getStatus())) { sendPublishNotify(notice); } return notice.getId(); } @Override @Transactional(rollbackFor = Exception.class) public void updateNotice(OaNoticeSaveReqVO updateReqVO) { // 校验是否存在 OaNoticeDO oldNotice = validateNoticeExists(updateReqVO.getId()); // 更新通知公告 OaNoticeDO updateObj = BeanUtils.toBean(updateReqVO, OaNoticeDO.class); // 从未发布变为已发布时,设置发布时间 if (!OaNoticeStatusEnum.PUBLISHED.getStatus().equals(oldNotice.getStatus()) && OaNoticeStatusEnum.PUBLISHED.getStatus().equals(updateReqVO.getStatus())) { updateObj.setPublishTime(LocalDateTime.now()); } oaNoticeMapper.updateById(updateObj); // 更新指定人关联 oaNoticeUserMapper.deleteByNoticeId(updateReqVO.getId()); if (OaNoticeSendScopeEnum.SPECIFIED.getScope().equals(updateReqVO.getSendScope()) && CollUtil.isNotEmpty(updateReqVO.getUserIds())) { saveNoticeUsers(updateReqVO.getId(), updateReqVO.getUserIds()); } // 新发布时发送系统通知 if (!OaNoticeStatusEnum.PUBLISHED.getStatus().equals(oldNotice.getStatus()) && OaNoticeStatusEnum.PUBLISHED.getStatus().equals(updateReqVO.getStatus())) { sendPublishNotify(updateObj); } // 撤销时删除已发送的系统通知 if (OaNoticeStatusEnum.REVOKED.getStatus().equals(updateReqVO.getStatus()) && OaNoticeStatusEnum.PUBLISHED.getStatus().equals(oldNotice.getStatus())) { notifyMessageMapper.deleteByTemplateCodeAndNoticeId( "oa-notice-publish", updateReqVO.getId()); } } @Override @Transactional(rollbackFor = Exception.class) public void deleteNotice(Long id) { validateNoticeExists(id); oaNoticeMapper.deleteById(id); oaNoticeUserMapper.deleteByNoticeId(id); } @Override public PageResult getNoticePage(Long userId, OaNoticePageReqVO reqVO) { // 构建可见的通知ID集合 Set visibleIds = buildVisibleNoticeIds(userId); if (visibleIds != null && visibleIds.isEmpty()) { return PageResult.empty(); } return oaNoticeMapper.selectPage(reqVO, visibleIds); } @Override public OaNoticeDO getNotice(Long id, Long userId) { OaNoticeDO notice = validateNoticeExists(id); // 标记已读 markAsRead(id, userId); return notice; } @Override public Long getUnreadCount(Long userId) { // 获取所有已发布且当前用户可见的公告ID Set visibleIds = buildVisibleNoticeIds(userId); if (CollUtil.isEmpty(visibleIds)) { return 0L; } List visibleIdList = new ArrayList<>(visibleIds); return oaNoticeReadMapper.selectUnreadCountByUserId(userId, visibleIdList); } /** * 构建当前用户可见的通知公告ID集合 * * @param userId 当前用户ID * @return null表示全部可见,空集合表示无可看 */ private Set buildVisibleNoticeIds(Long userId) { // 所有通知所有人公告ID List allPublishedNotices = oaNoticeMapper.selectList( new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX() .eq(OaNoticeDO::getSendScope, OaNoticeSendScopeEnum.ALL.getScope())); Set allNoticeIds = allPublishedNotices.stream() .map(OaNoticeDO::getId).collect(Collectors.toSet()); // 指定人能看到的本用户相关公告ID List specifiedNotices = oaNoticeMapper.selectList( new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX() .eq(OaNoticeDO::getSendScope, OaNoticeSendScopeEnum.SPECIFIED.getScope())); for (OaNoticeDO notice : specifiedNotices) { // 创建者可见 if (userId.equals(notice.getPublishUserId())) { allNoticeIds.add(notice.getId()); continue; } // 被指定用户可见 Long count = oaNoticeUserMapper.selectCount( new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX() .eq(OaNoticeUserDO::getNoticeId, notice.getId()) .eq(OaNoticeUserDO::getUserId, userId)); if (count > 0) { allNoticeIds.add(notice.getId()); } } return allNoticeIds; } /** * 标记已读 */ private void markAsRead(Long noticeId, Long userId) { OaNoticeReadDO existing = oaNoticeReadMapper.selectByNoticeIdAndUserId(noticeId, userId); if (existing == null) { OaNoticeReadDO readRecord = new OaNoticeReadDO(); readRecord.setNoticeId(noticeId); readRecord.setUserId(userId); readRecord.setReadTime(LocalDateTime.now()); oaNoticeReadMapper.insert(readRecord); } } /** * 保存指定人关联 */ private void saveNoticeUsers(Long noticeId, List userIds) { List userList = userIds.stream().map(uid -> { OaNoticeUserDO user = new OaNoticeUserDO(); user.setNoticeId(noticeId); user.setUserId(uid); return user; }).collect(Collectors.toList()); oaNoticeUserMapper.insertBatch(userList); } /** * 发送发布通知给目标用户 */ private void sendPublishNotify(OaNoticeDO notice) { Map templateParams = new HashMap<>(); templateParams.put("title", notice.getTitle()); templateParams.put("content", notice.getContent()); templateParams.put("noticeId", notice.getId()); List targetUserIds; if (OaNoticeSendScopeEnum.ALL.getScope().equals(notice.getSendScope())) { // 通知所有人:查询所有启用的管理员用户 targetUserIds = adminUserService.getUserListByStatus(CommonStatusEnum.ENABLE.getStatus()) .stream().map(cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO::getId) .collect(Collectors.toList()); } else { // 指定人 targetUserIds = oaNoticeUserMapper.selectListByNoticeId(notice.getId()).stream() .map(OaNoticeUserDO::getUserId) .collect(Collectors.toList()); } for (Long userId : targetUserIds) { try { notifySendService.sendSingleNotifyToAdmin(userId, "oa-notice-publish", templateParams); } catch (Exception e) { log.warn("发送通知公告[{}]给用户[{}]失败", notice.getId(), userId, e); } } } private OaNoticeDO validateNoticeExists(Long id) { OaNoticeDO notice = oaNoticeMapper.selectById(id); if (notice == null) { throw exception(OA_NOTICE_NOT_EXISTS); } return notice; } }