| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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<EnterpriseNewsMapper, EnterpriseNews> 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<EnterpriseNewsVo> listPage(Page<EnterpriseNewsVo> page, EnterpriseNewsDto enterpriseNewsDto) { |
| | | IPage<EnterpriseNewsVo> 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<Long> deptIds = distinctIds(enterpriseNewsDto.getDeptIds()); |
| | | List<Long> 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<Long> deptIds = distinctIds(enterpriseNewsDto.getDeptIds()); |
| | | List<Long> 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<Long> 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<Long> deptIds = distinctIds(enterpriseNewsDto.getDeptIds()); |
| | | List<Long> 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<Long> 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<Long> userIds) { |
| | | if (StringUtils.isEmpty(userIds)) { |
| | | return; |
| | | } |
| | | List<SysUser> users = sysUserMapper.selectUsersByIds(userIds); |
| | | if (users.size() != userIds.size()) { |
| | | throw new ServiceException("èªå®ä¹é
读人åå
嫿 æç¨æ·"); |
| | | } |
| | | } |
| | | |
| | | private Integer calculateRequiredReadCount(String readScope, List<Long> deptIds, List<Long> userIds) { |
| | | if (READ_SCOPE_ALL.equals(readScope)) { |
| | | Long count = sysUserMapper.selectCount(new LambdaQueryWrapper<SysUser>() |
| | | .eq(SysUser::getDelFlag, "0")); |
| | | return count == null ? 0 : count.intValue(); |
| | | } |
| | | if (READ_SCOPE_DEPT.equals(readScope)) { |
| | | List<Long> 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<Long> collectDeptIdsWithChildren(List<Long> deptIds) { |
| | | Set<Long> allDeptIds = new LinkedHashSet<>(); |
| | | for (Long deptId : deptIds) { |
| | | if (deptId == null) { |
| | | continue; |
| | | } |
| | | allDeptIds.add(deptId); |
| | | List<SysDept> 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<Long> deptIds, List<Long> userIds) { |
| | | if (READ_SCOPE_DEPT.equals(readScope)) { |
| | | List<EnterpriseNewsScopeDept> 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<EnterpriseNewsScopeUser> 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<EnterpriseNewsScopeDept>() |
| | | .eq(EnterpriseNewsScopeDept::getNewsId, newsId)); |
| | | enterpriseNewsScopeUserService.remove(new LambdaQueryWrapper<EnterpriseNewsScopeUser>() |
| | | .eq(EnterpriseNewsScopeUser::getNewsId, newsId)); |
| | | } |
| | | |
| | | private void resetEnterpriseNewsApprovalFlow(EnterpriseNews oldEnterpriseNews) { |
| | | if (oldEnterpriseNews == null || !STATUS_DRAFT.equals(oldEnterpriseNews.getStatus())) { |
| | | return; |
| | | } |
| | | List<Long> approvalInstanceIds = approvalInstanceMapper.selectList(new LambdaQueryWrapper<ApprovalInstance>() |
| | | .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<Long> distinctIds(List<Long> ids) { |
| | | if (StringUtils.isEmpty(ids)) { |
| | | return new ArrayList<>(); |
| | | } |
| | | Set<Long> 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<ApprovalTask> tasks) { |
| | | if (instance == null || tasks == null || tasks.isEmpty()) { |
| | | return; |
| | | } |
| | | List<Long> 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); |
| | | } |
| | | } |