package com.ruoyi.collaborativeApproval.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.approve.mapper.ApprovalInstanceMapper; import com.ruoyi.approve.mapper.ApprovalTemplateMapper; import com.ruoyi.approve.pojo.ApprovalInstance; import com.ruoyi.approve.pojo.ApprovalTask; import com.ruoyi.approve.pojo.ApprovalTemplate; import com.ruoyi.approve.service.ApprovalInstanceService; import com.ruoyi.approve.utils.ApproveProcessConfigNodeUtils; import com.ruoyi.basic.enums.ApplicationTypeEnum; import com.ruoyi.basic.enums.RecordTypeEnum; import com.ruoyi.basic.utils.FileUtil; import com.ruoyi.collaborativeApproval.dto.EnterpriseNewsDto; import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsMapper; import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews; import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept; import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser; import com.ruoyi.collaborativeApproval.service.EnterpriseNewsScopeDeptService; import com.ruoyi.collaborativeApproval.service.EnterpriseNewsScopeUserService; import com.ruoyi.collaborativeApproval.service.EnterpriseNewsService; import com.ruoyi.collaborativeApproval.vo.EnterpriseNewsVo; import com.ruoyi.common.enums.TypeEnums; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.OrderUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.project.system.domain.SysDept; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysDeptMapper; import com.ruoyi.project.system.mapper.SysUserDeptMapper; import com.ruoyi.project.system.mapper.SysUserMapper; import com.ruoyi.project.system.service.ISysNoticeService; import lombok.RequiredArgsConstructor; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * 企业新闻表服务实现类 * * @author 芯导软件(江苏)有限公司 * @since 2026-05-20 11:50:59 */ @Service @RequiredArgsConstructor public class EnterpriseNewsServiceImpl extends ServiceImpl implements EnterpriseNewsService { private static final String READ_SCOPE_ALL = "all"; private static final String READ_SCOPE_DEPT = "dept"; private static final String READ_SCOPE_CUSTOM = "custom"; private static final String STATUS_DRAFT = "DRAFT"; private static final String STATUS_PENDING = "PENDING"; private static final String STATUS_PUBLISHED = "PUBLISHED"; private static final String STATUS_REJECTED = "REJECTED"; private static final String STATUS_OFFLINE = "OFFLINE"; private final EnterpriseNewsMapper enterpriseNewsMapper; private final EnterpriseNewsScopeDeptService enterpriseNewsScopeDeptService; private final EnterpriseNewsScopeUserService enterpriseNewsScopeUserService; private final SysUserMapper sysUserMapper; private final SysDeptMapper sysDeptMapper; private final SysUserDeptMapper sysUserDeptMapper; private final ApprovalInstanceMapper approvalInstanceMapper; private final ApprovalInstanceService approvalInstanceService; private final ApprovalTemplateMapper approvalTemplateMapper; private final ApproveProcessConfigNodeUtils approveProcessConfigNodeUtils; private final ISysNoticeService sysNoticeService; private final FileUtil fileUtil; @Override public IPage listPage(Page page, EnterpriseNewsDto enterpriseNewsDto) { IPage enterpriseNewsVoIPage = enterpriseNewsMapper.listPage(page, enterpriseNewsDto); enterpriseNewsVoIPage.getRecords().forEach(enterpriseNewsVo -> { enterpriseNewsVo.setStorageBlobDTOs(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.ENTERPRISE_NEWS, enterpriseNewsVo.getId())); }); return enterpriseNewsVoIPage; } @Override @Transactional(rollbackFor = Exception.class) public Boolean add(EnterpriseNewsDto enterpriseNewsDto) { validateForSave(enterpriseNewsDto); String readScope = normalizeReadScope(enterpriseNewsDto.getReadScope()); List deptIds = distinctIds(enterpriseNewsDto.getDeptIds()); List userIds = distinctIds(enterpriseNewsDto.getUserIds()); EnterpriseNews enterpriseNews = new EnterpriseNews(); BeanUtils.copyProperties(enterpriseNewsDto, enterpriseNews); enterpriseNews.setReadScope(readScope); enterpriseNews.setIsRequired(enterpriseNewsDto.getIsRequired() == null ? (byte) 0 : enterpriseNewsDto.getIsRequired()); enterpriseNews.setStatus(normalizeStatus(enterpriseNewsDto.getStatus(), STATUS_DRAFT)); enterpriseNews.setReadCount(0); enterpriseNews.setRequiredReadCount(calculateRequiredReadCount(readScope, deptIds, userIds)); Long[] loginDeptIds = SecurityUtils.getDeptId(); if (StringUtils.isNotEmpty(loginDeptIds)) { enterpriseNews.setDeptId(loginDeptIds[0]); } if (!save(enterpriseNews) || enterpriseNews.getId() == null) { throw new ServiceException("新增企业新闻失败"); } saveReadScopeRelations(enterpriseNews.getId(), readScope, deptIds, userIds); if (STATUS_PENDING.equals(enterpriseNews.getStatus())) { startEnterpriseNewsApproval(enterpriseNews, enterpriseNewsDto); } //添加附件 fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.ENTERPRISE_NEWS, enterpriseNews.getId(), enterpriseNewsDto.getStorageBlobDTOs()); return true; } @Override @Transactional(rollbackFor = Exception.class) public Boolean updateEnterpriseNewsDto(EnterpriseNewsDto enterpriseNewsDto) { if (enterpriseNewsDto == null || enterpriseNewsDto.getId() == null) { throw new ServiceException("企业新闻ID不能为空"); } EnterpriseNews oldEnterpriseNews = getById(enterpriseNewsDto.getId()); if (oldEnterpriseNews == null) { throw new ServiceException("企业新闻不存在"); } if (!STATUS_DRAFT.equals(oldEnterpriseNews.getStatus()) && !STATUS_REJECTED.equals(oldEnterpriseNews.getStatus())) { throw new ServiceException("待审批或已发布的企业新闻不允许修改"); } validateForSave(enterpriseNewsDto); String readScope = normalizeReadScope(enterpriseNewsDto.getReadScope()); List deptIds = distinctIds(enterpriseNewsDto.getDeptIds()); List userIds = distinctIds(enterpriseNewsDto.getUserIds()); EnterpriseNews enterpriseNews = new EnterpriseNews(); BeanUtils.copyProperties(enterpriseNewsDto, enterpriseNews); enterpriseNews.setReadScope(readScope); enterpriseNews.setIsRequired(enterpriseNewsDto.getIsRequired() == null ? oldEnterpriseNews.getIsRequired() : enterpriseNewsDto.getIsRequired()); enterpriseNews.setStatus(normalizeStatus(enterpriseNewsDto.getStatus(), oldEnterpriseNews.getStatus())); enterpriseNews.setReadCount(oldEnterpriseNews.getReadCount()); enterpriseNews.setRequiredReadCount(calculateRequiredReadCount(readScope, deptIds, userIds)); enterpriseNews.setCreateUser(oldEnterpriseNews.getCreateUser()); enterpriseNews.setCreateTime(oldEnterpriseNews.getCreateTime()); enterpriseNews.setDeptId(oldEnterpriseNews.getDeptId()); if (!updateById(enterpriseNews)) { throw new ServiceException("修改企业新闻失败"); } clearReadScopeRelations(enterpriseNews.getId()); saveReadScopeRelations(enterpriseNews.getId(), readScope, deptIds, userIds); fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.ENTERPRISE_NEWS, enterpriseNews.getId(), enterpriseNewsDto.getStorageBlobDTOs()); if (STATUS_PENDING.equals(enterpriseNews.getStatus())) { resetEnterpriseNewsApprovalFlow(oldEnterpriseNews); startEnterpriseNewsApproval(enterpriseNews, enterpriseNewsDto); } return true; } @Override public Boolean delete(List ids) { if (ids == null || ids.isEmpty()) { return false; } if (!removeByIds(ids)) { throw new ServiceException("删除企业新闻失败"); } ids.forEach(this::clearReadScopeRelations); return true; } private void validateForSave(EnterpriseNewsDto enterpriseNewsDto) { if (enterpriseNewsDto == null) { throw new ServiceException("企业新闻数据不能为空"); } if (StringUtils.isEmpty(enterpriseNewsDto.getTitle())) { throw new ServiceException("标题不能为空"); } if (StringUtils.isEmpty(enterpriseNewsDto.getContent())) { throw new ServiceException("正文不能为空"); } normalizeStatus(enterpriseNewsDto.getStatus(), STATUS_DRAFT); String readScope = normalizeReadScope(enterpriseNewsDto.getReadScope()); List deptIds = distinctIds(enterpriseNewsDto.getDeptIds()); List userIds = distinctIds(enterpriseNewsDto.getUserIds()); if (READ_SCOPE_DEPT.equals(readScope) && StringUtils.isEmpty(deptIds)) { throw new ServiceException("请选择阅读范围部门"); } if (READ_SCOPE_CUSTOM.equals(readScope) && StringUtils.isEmpty(userIds)) { throw new ServiceException("请选择自定义阅读人员"); } validateDeptIds(deptIds); validateUserIds(userIds); } private String normalizeReadScope(String readScope) { String normalized = StringUtils.isEmpty(readScope) ? READ_SCOPE_ALL : readScope.trim(); if (!READ_SCOPE_ALL.equals(normalized) && !READ_SCOPE_DEPT.equals(normalized) && !READ_SCOPE_CUSTOM.equals(normalized)) { throw new ServiceException("阅读范围不合法"); } return normalized; } private String normalizeStatus(String status, String defaultStatus) { String normalized = StringUtils.isEmpty(status) ? defaultStatus : status.trim().toUpperCase(); if (!STATUS_DRAFT.equals(normalized) && !STATUS_PENDING.equals(normalized) && !STATUS_PUBLISHED.equals(normalized) && !STATUS_REJECTED.equals(normalized) && !STATUS_OFFLINE.equals(normalized)) { throw new ServiceException("企业新闻状态不合法"); } return normalized; } private void validateDeptIds(List deptIds) { if (StringUtils.isEmpty(deptIds)) { return; } for (Long deptId : deptIds) { SysDept sysDept = sysDeptMapper.selectDeptById(deptId); if (deptId == null || sysDept == null) { throw new ServiceException("阅读范围部门不存在"); } } } private void validateUserIds(List userIds) { if (StringUtils.isEmpty(userIds)) { return; } List users = sysUserMapper.selectUsersByIds(userIds); if (users.size() != userIds.size()) { throw new ServiceException("自定义阅读人员包含无效用户"); } } private Integer calculateRequiredReadCount(String readScope, List deptIds, List userIds) { if (READ_SCOPE_ALL.equals(readScope)) { Long count = sysUserMapper.selectCount(new LambdaQueryWrapper() .eq(SysUser::getDelFlag, "0")); return count == null ? 0 : count.intValue(); } if (READ_SCOPE_DEPT.equals(readScope)) { List allDeptIds = collectDeptIdsWithChildren(deptIds); if (StringUtils.isEmpty(allDeptIds)) { return 0; } Long count = sysUserDeptMapper.countDistinctUserIdsByDeptIds(allDeptIds); return count == null ? 0 : count.intValue(); } return userIds.size(); } private List collectDeptIdsWithChildren(List deptIds) { Set allDeptIds = new LinkedHashSet<>(); for (Long deptId : deptIds) { if (deptId == null) { continue; } allDeptIds.add(deptId); List children = sysDeptMapper.selectChildrenDeptById(deptId); if (StringUtils.isNotEmpty(children)) { for (SysDept child : children) { allDeptIds.add(child.getDeptId()); } } } return new ArrayList<>(allDeptIds); } private void saveReadScopeRelations(Long newsId, String readScope, List deptIds, List userIds) { if (READ_SCOPE_DEPT.equals(readScope)) { List scopeDeptList = new ArrayList<>(); for (Long deptId : deptIds) { EnterpriseNewsScopeDept scopeDept = new EnterpriseNewsScopeDept(); scopeDept.setNewsId(newsId); scopeDept.setDeptId(deptId); scopeDeptList.add(scopeDept); } if (StringUtils.isNotEmpty(scopeDeptList)) { enterpriseNewsScopeDeptService.saveBatch(scopeDeptList); } return; } if (READ_SCOPE_CUSTOM.equals(readScope)) { List scopeUserList = new ArrayList<>(); for (Long userId : userIds) { EnterpriseNewsScopeUser scopeUser = new EnterpriseNewsScopeUser(); scopeUser.setNewsId(newsId); scopeUser.setUserId(userId); scopeUserList.add(scopeUser); } if (StringUtils.isNotEmpty(scopeUserList)) { enterpriseNewsScopeUserService.saveBatch(scopeUserList); } } } private void clearReadScopeRelations(Long newsId) { enterpriseNewsScopeDeptService.remove(new LambdaQueryWrapper() .eq(EnterpriseNewsScopeDept::getNewsId, newsId)); enterpriseNewsScopeUserService.remove(new LambdaQueryWrapper() .eq(EnterpriseNewsScopeUser::getNewsId, newsId)); } private void resetEnterpriseNewsApprovalFlow(EnterpriseNews oldEnterpriseNews) { if (oldEnterpriseNews == null || !STATUS_DRAFT.equals(oldEnterpriseNews.getStatus())) { return; } List approvalInstanceIds = approvalInstanceMapper.selectList(new LambdaQueryWrapper() .eq(ApprovalInstance::getBusinessId, oldEnterpriseNews.getId()) .eq(ApprovalInstance::getBusinessType, TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode()) .eq(ApprovalInstance::getDeleted, (byte) 0)) .stream() .map(ApprovalInstance::getId) .filter(id -> id != null && id > 0) .collect(Collectors.toList()); if (StringUtils.isEmpty(approvalInstanceIds)) { return; } approvalInstanceService.delete(approvalInstanceIds); } private List distinctIds(List ids) { if (StringUtils.isEmpty(ids)) { return new ArrayList<>(); } Set distinctSet = new LinkedHashSet<>(); for (Long id : ids) { if (id != null) { distinctSet.add(id); } } return new ArrayList<>(distinctSet); } private void startEnterpriseNewsApproval(EnterpriseNews enterpriseNews, EnterpriseNewsDto enterpriseNewsDto) { if (enterpriseNewsDto.getTemplateId() == null) { throw new ServiceException("审批模板不能为空"); } String templateName = enterpriseNewsDto.getTemplateName(); if (StringUtils.isEmpty(templateName)) { ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectById(enterpriseNewsDto.getTemplateId()); if (approvalTemplate == null) { throw new ServiceException("审批模板不存在"); } templateName = approvalTemplate.getTemplateName(); } ApprovalInstance approvalInstance = new ApprovalInstance(); approvalInstance.setInstanceNo(OrderUtils.countTodayByCreateTime(approvalInstanceMapper, "SP", "instance_no", enterpriseNews.getCreateTime() != null ? enterpriseNews.getCreateTime() : LocalDateTime.now())); approvalInstance.setTemplateId(enterpriseNewsDto.getTemplateId()); approvalInstance.setTemplateName(templateName); approvalInstance.setBusinessId(enterpriseNews.getId()); approvalInstance.setBusinessType(TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode()); approvalInstance.setTitle(enterpriseNews.getTitle()); approvalInstance.setStatus("PENDING"); approvalInstance.setCurrentLevel(1); approvalInstance.setApplicantId(SecurityUtils.getUserId()); approvalInstance.setApplicantName(SecurityUtils.getLoginUser().getNickName()); approvalInstance.setApplyTime(LocalDateTime.now()); approvalInstance.setDeleted((byte) 0); approvalInstance.setCreateUser(SecurityUtils.getUserId()); approvalInstance.setUpdateUser(SecurityUtils.getUserId()); approvalInstance.setDeptId(enterpriseNews.getDeptId()); approvalInstanceMapper.insert(approvalInstance); approveProcessConfigNodeUtils.createCurrentNodeAndTasks(approvalInstance); sendApproveNotice(approvalInstance, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstance.getId())); } private void sendApproveNotice(ApprovalInstance instance, List tasks) { if (instance == null || tasks == null || tasks.isEmpty()) { return; } List approverIds = tasks.stream() .map(ApprovalTask::getApproverId) .filter(id -> id != null && id > 0) .distinct() .collect(Collectors.toList()); if (approverIds.isEmpty()) { return; } String title = StringUtils.isNotEmpty(instance.getTemplateName()) ? instance.getTemplateName() : "审批提醒"; String message = "审批单号 " + instance.getInstanceNo() + " 需要您审批"; String jumpPath = "/officeProcessAutomation/ApproveManage/approve-list/?id=" + instance.getId(); sysNoticeService.simpleNoticeByUser(title, message, approverIds, jumpPath); } }