package cn.yinlihupo.service.risk.impl; import cn.hutool.core.util.IdUtil; import cn.yinlihupo.common.page.TableDataInfo; import cn.yinlihupo.common.util.SecurityUtils; import cn.yinlihupo.domain.dto.CreateRiskRequest; import cn.yinlihupo.domain.dto.CreateWorkOrderRequest; import cn.yinlihupo.domain.entity.Project; import cn.yinlihupo.domain.entity.Risk; import cn.yinlihupo.domain.entity.SysUser; import cn.yinlihupo.domain.entity.WorkOrder; import cn.yinlihupo.domain.vo.RiskStatisticsVO; import cn.yinlihupo.domain.vo.RiskVO; import cn.yinlihupo.mapper.ProjectMapper; import cn.yinlihupo.mapper.RiskMapper; import cn.yinlihupo.mapper.SysUserMapper; import cn.yinlihupo.mapper.WorkOrderMapper; import cn.yinlihupo.service.risk.RiskService; import cn.yinlihupo.service.risk.WorkOrderService; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; /** * 风险服务实现类 */ @Slf4j @Service @RequiredArgsConstructor public class RiskServiceImpl implements RiskService { private final RiskMapper riskMapper; private final ProjectMapper projectMapper; private final SysUserMapper sysUserMapper; private final WorkOrderMapper workOrderMapper; private final WorkOrderService workOrderService; @Override @Transactional(rollbackFor = Exception.class) public Long createRisk(CreateRiskRequest request) { log.info("创建风险评估, projectId: {}, riskName: {}", request.getProjectId(), request.getRiskName()); // 验证项目存在 Project project = projectMapper.selectById(request.getProjectId()); if (project == null || project.getDeleted() == 1) { throw new RuntimeException("项目不存在"); } Risk risk = new Risk(); risk.setProjectId(request.getProjectId()); risk.setRiskCode(generateRiskCode()); risk.setCategory(request.getCategory()); risk.setRiskName(request.getRiskName()); risk.setDescription(request.getDescription()); risk.setRiskSource(request.getRiskSource() != null ? request.getRiskSource() : "manual"); // 计算风险得分和等级 if (request.getProbability() != null && request.getImpact() != null) { risk.setProbability(BigDecimal.valueOf(request.getProbability())); risk.setImpact(BigDecimal.valueOf(request.getImpact())); BigDecimal score = risk.getProbability() .multiply(risk.getImpact()) .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); risk.setRiskScore(score); risk.setRiskLevel(calculateRiskLevel(request.getProbability(), request.getImpact())); } risk.setOwnerId(request.getOwnerId()); risk.setMitigationPlan(request.getMitigationPlan()); risk.setContingencyPlan(request.getContingencyPlan()); risk.setTriggerCondition(request.getTriggerCondition()); risk.setDueDate(request.getDueDate()); risk.setTags(request.getTags()); risk.setStatus("identified"); risk.setDiscoverTime(LocalDateTime.now()); riskMapper.insert(risk); log.info("风险评估创建成功, riskId: {}", risk.getId()); return risk.getId(); } @Override @Transactional(rollbackFor = Exception.class) public Boolean updateRisk(Long riskId, CreateRiskRequest request) { log.info("更新风险, riskId: {}", riskId); Risk risk = riskMapper.selectById(riskId); if (risk == null || risk.getDeleted() == 1) { throw new RuntimeException("风险不存在"); } if (request.getCategory() != null) { risk.setCategory(request.getCategory()); } if (request.getRiskName() != null) { risk.setRiskName(request.getRiskName()); } if (request.getDescription() != null) { risk.setDescription(request.getDescription()); } if (request.getProbability() != null && request.getImpact() != null) { risk.setProbability(BigDecimal.valueOf(request.getProbability())); risk.setImpact(BigDecimal.valueOf(request.getImpact())); BigDecimal score = risk.getProbability() .multiply(risk.getImpact()) .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); risk.setRiskScore(score); risk.setRiskLevel(calculateRiskLevel(request.getProbability(), request.getImpact())); } if (request.getOwnerId() != null) { risk.setOwnerId(request.getOwnerId()); } if (request.getMitigationPlan() != null) { risk.setMitigationPlan(request.getMitigationPlan()); } if (request.getContingencyPlan() != null) { risk.setContingencyPlan(request.getContingencyPlan()); } if (request.getTriggerCondition() != null) { risk.setTriggerCondition(request.getTriggerCondition()); } if (request.getDueDate() != null) { risk.setDueDate(request.getDueDate()); } if (request.getTags() != null) { risk.setTags(request.getTags()); } riskMapper.updateById(risk); log.info("风险更新成功, riskId: {}", riskId); return true; } @Override @Transactional(rollbackFor = Exception.class) public Boolean deleteRisk(Long riskId) { log.info("删除风险, riskId: {}", riskId); Risk risk = riskMapper.selectById(riskId); if (risk == null || risk.getDeleted() == 1) { throw new RuntimeException("风险不存在"); } riskMapper.deleteById(riskId); log.info("风险删除成功, riskId: {}", riskId); return true; } @Override public RiskVO getRiskDetail(Long riskId) { log.info("获取风险详情, riskId: {}", riskId); Risk risk = riskMapper.selectById(riskId); if (risk == null || risk.getDeleted() == 1) { throw new RuntimeException("风险不存在"); } return convertToRiskVO(risk); } @Override public TableDataInfo getRiskList(Long projectId, Integer pageNum, Integer pageSize, String category, String riskLevel, String status, String keyword) { log.info("分页查询风险列表, projectId: {}, pageNum: {}, pageSize: {}", projectId, pageNum, pageSize); Page page = new Page<>(pageNum, pageSize); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Risk::getDeleted, 0); if (projectId != null) { wrapper.eq(Risk::getProjectId, projectId); } if (category != null && !category.isEmpty()) { wrapper.eq(Risk::getCategory, category); } if (riskLevel != null && !riskLevel.isEmpty()) { wrapper.eq(Risk::getRiskLevel, riskLevel); } if (status != null && !status.isEmpty()) { wrapper.eq(Risk::getStatus, status); } if (keyword != null && !keyword.isEmpty()) { wrapper.and(w -> w.like(Risk::getRiskName, keyword) .or() .like(Risk::getDescription, keyword)); } // 按风险等级和得分排序 wrapper.orderByAsc( r -> "critical".equals(r.getRiskLevel()) ? 1 : "high".equals(r.getRiskLevel()) ? 2 : "medium".equals(r.getRiskLevel()) ? 3 : 4 ).orderByDesc(Risk::getRiskScore); Page riskPage = riskMapper.selectPage(page, wrapper); List voList = riskPage.getRecords().stream() .map(this::convertToRiskVO) .collect(Collectors.toList()); return TableDataInfo.build(new Page(riskPage.getCurrent(), riskPage.getSize(), riskPage.getTotal()) .setRecords(voList)); } @Override public RiskStatisticsVO getRiskStatistics(Long projectId) { log.info("获取风险统计信息, projectId: {}", projectId); RiskStatisticsVO statistics = new RiskStatisticsVO(); // 构建查询条件 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Risk::getDeleted, 0); if (projectId != null) { wrapper.eq(Risk::getProjectId, projectId); } List risks = riskMapper.selectList(wrapper); // 统计总数 statistics.setTotalCount(risks.size()); // 按状态统计 Map statusCount = risks.stream() .collect(Collectors.groupingBy(Risk::getStatus, Collectors.counting())); statistics.setIdentifiedCount(statusCount.getOrDefault("identified", 0L).intValue()); statistics.setAssignedCount(statusCount.getOrDefault("assigned", 0L).intValue()); statistics.setMitigatingCount(statusCount.getOrDefault("mitigating", 0L).intValue()); statistics.setResolvedCount(statusCount.getOrDefault("resolved", 0L).intValue()); statistics.setClosedCount(statusCount.getOrDefault("closed", 0L).intValue()); // 按等级统计 Map levelCount = risks.stream() .collect(Collectors.groupingBy(Risk::getRiskLevel, Collectors.counting())); statistics.setCriticalCount(levelCount.getOrDefault("critical", 0L).intValue()); statistics.setHighCount(levelCount.getOrDefault("high", 0L).intValue()); statistics.setMediumCount(levelCount.getOrDefault("medium", 0L).intValue()); statistics.setLowCount(levelCount.getOrDefault("low", 0L).intValue()); // 计算平均风险得分 double avgScore = risks.stream() .filter(r -> r.getRiskScore() != null) .mapToDouble(r -> r.getRiskScore().doubleValue()) .average() .orElse(0.0); statistics.setAverageRiskScore(BigDecimal.valueOf(avgScore).setScale(2, RoundingMode.HALF_UP)); // 统计未解决的高风险数量 int unresolvedHigh = (int) risks.stream() .filter(r -> !"resolved".equals(r.getStatus()) && !"closed".equals(r.getStatus())) .filter(r -> "critical".equals(r.getRiskLevel()) || "high".equals(r.getRiskLevel())) .count(); statistics.setUnresolvedHighCount(unresolvedHigh); // 按分类统计 Map categoryStats = risks.stream() .collect(Collectors.groupingBy( r -> r.getCategory() != null ? r.getCategory() : "other", Collectors.counting() )); statistics.setCategoryStats(categoryStats); // 按等级统计 statistics.setLevelStats(levelCount); return statistics; } @Override @Transactional(rollbackFor = Exception.class) public Long assignWorkOrder(Long riskId, CreateWorkOrderRequest request) { log.info("为风险分配工单, riskId: {}", riskId); Risk risk = riskMapper.selectById(riskId); if (risk == null || risk.getDeleted() == 1) { throw new RuntimeException("风险不存在"); } // 设置工单关联 request.setRiskId(riskId); request.setProjectId(risk.getProjectId()); request.setOrderType("risk_handle"); request.setSource("risk"); // 创建工单 Long workOrderId = workOrderService.createWorkOrder(request); // 更新风险的工单关联 List workOrderIds = risk.getWorkOrderIds(); if (workOrderIds == null) { workOrderIds = new ArrayList<>(); } workOrderIds.add(workOrderId); risk.setWorkOrderIds(workOrderIds); risk.setStatus("assigned"); riskMapper.updateById(risk); log.info("风险工单分配成功, riskId: {}, workOrderId: {}", riskId, workOrderId); return workOrderId; } @Override @Transactional(rollbackFor = Exception.class) public Boolean batchUpdateStatus(List riskIds, String status) { log.info("批量更新风险状态, riskIds: {}, status: {}", riskIds, status); for (Long riskId : riskIds) { Risk risk = riskMapper.selectById(riskId); if (risk != null && risk.getDeleted() == 0) { risk.setStatus(status); if ("resolved".equals(status)) { risk.setResolvedTime(LocalDateTime.now()); } riskMapper.updateById(risk); } } log.info("批量更新风险状态完成, 更新数量: {}", riskIds.size()); return true; } /** * 生成风险编号 */ private String generateRiskCode() { return "RSK" + IdUtil.fastSimpleUUID().substring(0, 12).toUpperCase(); } /** * 计算风险等级 */ private String calculateRiskLevel(Integer probability, Integer impact) { if (probability == null || impact == null) { return "low"; } int score = probability * impact; if (score >= 300) { return "critical"; } else if (score >= 200) { return "high"; } else if (score >= 100) { return "medium"; } else { return "low"; } } /** * 转换为VO */ private RiskVO convertToRiskVO(Risk risk) { RiskVO vo = new RiskVO(); vo.setId(risk.getId()); vo.setRiskCode(risk.getRiskCode()); vo.setProjectId(risk.getProjectId()); vo.setCategory(risk.getCategory()); vo.setRiskName(risk.getRiskName()); vo.setDescription(risk.getDescription()); vo.setRiskSource(risk.getRiskSource()); vo.setProbability(risk.getProbability()); vo.setImpact(risk.getImpact()); vo.setRiskScore(risk.getRiskScore()); vo.setRiskLevel(risk.getRiskLevel()); vo.setStatus(risk.getStatus()); vo.setOwnerId(risk.getOwnerId()); vo.setMitigationPlan(risk.getMitigationPlan()); vo.setContingencyPlan(risk.getContingencyPlan()); vo.setTriggerCondition(risk.getTriggerCondition()); vo.setDiscoverTime(risk.getDiscoverTime()); vo.setDueDate(risk.getDueDate()); vo.setResolvedTime(risk.getResolvedTime()); vo.setTags(risk.getTags()); vo.setCreateTime(risk.getCreateTime()); vo.setUpdateTime(risk.getUpdateTime()); // 查询项目名称 if (risk.getProjectId() != null) { Project project = projectMapper.selectById(risk.getProjectId()); if (project != null) { vo.setProjectName(project.getProjectName()); } } // 查询负责人信息 if (risk.getOwnerId() != null) { SysUser owner = sysUserMapper.selectById(risk.getOwnerId()); if (owner != null) { vo.setOwnerName(owner.getRealName()); vo.setOwnerAvatar(owner.getAvatar()); } } // 计算关联工单数量 if (risk.getWorkOrderIds() != null) { vo.setWorkOrderCount(risk.getWorkOrderIds().size()); } else { vo.setWorkOrderCount(0); } return vo; } }