- 在 convertToProjectListVO 方法中添加逻辑,根据 managerId 查询项目经理姓名 - 查询到有效项目经理时,将其真实姓名设置到 VO 的 managerName 字段 - 增强项目列表展示信息的完整性和可读性
1136 lines
48 KiB
Java
1136 lines
48 KiB
Java
package cn.yinlihupo.service.project.impl;
|
||
|
||
import cn.hutool.core.util.IdUtil;
|
||
import cn.yinlihupo.common.page.TableDataInfo;
|
||
import cn.yinlihupo.domain.entity.*;
|
||
import cn.yinlihupo.domain.vo.*;
|
||
import cn.yinlihupo.mapper.*;
|
||
import cn.yinlihupo.service.oss.OssService;
|
||
import cn.yinlihupo.service.project.ProjectService;
|
||
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.ai.chat.client.ChatClient;
|
||
import org.springframework.stereotype.Service;
|
||
import org.springframework.transaction.annotation.Transactional;
|
||
import org.springframework.web.multipart.MultipartFile;
|
||
|
||
import java.math.BigDecimal;
|
||
import java.math.RoundingMode;
|
||
import java.time.LocalDate;
|
||
import java.time.LocalDateTime;
|
||
import java.time.YearMonth;
|
||
import java.util.*;
|
||
import java.util.stream.Collectors;
|
||
|
||
/**
|
||
* AI项目初始化服务实现类
|
||
* 使用Spring AI结构化输出能力解析项目文档
|
||
*/
|
||
@Slf4j
|
||
@Service
|
||
@RequiredArgsConstructor
|
||
public class ProjectServiceImpl implements ProjectService {
|
||
|
||
private final ChatClient chatClient;
|
||
private final OssService ossService;
|
||
|
||
// Mapper依赖
|
||
private final ProjectMapper projectMapper;
|
||
private final ProjectMilestoneMapper projectMilestoneMapper;
|
||
private final TaskMapper taskMapper;
|
||
private final TaskDependencyMapper taskDependencyMapper;
|
||
private final ProjectMemberMapper projectMemberMapper;
|
||
private final ResourceMapper resourceMapper;
|
||
private final RiskMapper riskMapper;
|
||
private final ProjectTimelineMapper projectTimelineMapper;
|
||
private final SysUserMapper sysUserMapper;
|
||
|
||
/**
|
||
* 项目初始化系统提示词模板
|
||
*/
|
||
private static final String PROJECT_INIT_SYSTEM_PROMPT = """
|
||
# 角色
|
||
|
||
你是一个专业的项目管理助手,擅长从项目文档中提取结构化信息,自动生成项目计划。
|
||
|
||
# 任务
|
||
|
||
根据用户提供的项目资料,解析并生成以下结构化数据:
|
||
|
||
1. 项目基本信息(名称、周期、预算、目标)
|
||
2. 项目里程碑(关键节点)
|
||
3. 任务清单(WBS工作分解结构)
|
||
4. 项目成员及角色
|
||
5. 资源需求
|
||
6. 风险识别
|
||
|
||
# 输出格式
|
||
|
||
请严格按照以下JSON格式输出:
|
||
|
||
```json
|
||
{
|
||
"project": {
|
||
"project_name": "项目名称",
|
||
"project_type": "工程项目/研发项目/运营项目",
|
||
"description": "项目描述",
|
||
"objectives": "项目目标",
|
||
"plan_start_date": "2024-01-01",
|
||
"plan_end_date": "2024-12-31",
|
||
"budget": 1000000,
|
||
"currency": "CNY",
|
||
"priority": "high/medium/low",
|
||
"tags": ["标签1", "标签2"]
|
||
},
|
||
"milestones": [
|
||
{
|
||
"milestone_name": "里程碑名称",
|
||
"description": "描述",
|
||
"plan_date": "2024-03-01",
|
||
"deliverables": "交付物",
|
||
"owner_role": "负责人角色"
|
||
}
|
||
],
|
||
"tasks": [
|
||
{
|
||
"task_id": "T001",
|
||
"task_name": "任务名称",
|
||
"parent_task_id": null,
|
||
"description": "任务描述",
|
||
"plan_start_date": "2024-01-15",
|
||
"plan_end_date": "2024-02-15",
|
||
"estimated_hours": 80,
|
||
"priority": "high",
|
||
"assignee_role": "执行者角色",
|
||
"dependencies": ["T001"],
|
||
"deliverables": "交付物"
|
||
}
|
||
],
|
||
"members": [
|
||
{
|
||
"name": "姓名",
|
||
"role_code": "manager/leader/member",
|
||
"responsibility": "职责描述",
|
||
"department": "部门",
|
||
"weekly_hours": 40
|
||
}
|
||
],
|
||
"resources": [
|
||
{
|
||
"resource_name": "资源名称",
|
||
"resource_type": "human/material/equipment/software/finance",
|
||
"quantity": 1,
|
||
"unit": "单位",
|
||
"unit_price": 1000,
|
||
"supplier": "供应商"
|
||
}
|
||
],
|
||
"risks": [
|
||
{
|
||
"risk_name": "风险名称",
|
||
"category": "technical/schedule/cost/quality/resource/external",
|
||
"description": "风险描述",
|
||
"probability": 60,
|
||
"impact": 4,
|
||
"mitigation_plan": "缓解措施"
|
||
}
|
||
],
|
||
"timeline_nodes": [
|
||
{
|
||
"node_name": "时间节点名称",
|
||
"node_type": "milestone/phase/event",
|
||
"plan_date": "2024-06-01",
|
||
"description": "描述",
|
||
"kb_scope": ["report", "file", "risk", "ticket"]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
# 注意事项
|
||
|
||
1. 日期格式统一使用 YYYY-MM-DD
|
||
2. 任务之间要建立合理的依赖关系
|
||
3. 里程碑应该是关键节点,不宜过多
|
||
4. 资源要考虑人力、物料、设备等
|
||
5. 风险识别要基于项目特点
|
||
6. 如果文档信息不完整,根据项目类型合理推断
|
||
""";
|
||
|
||
/**
|
||
* 用户提示词模板
|
||
*/
|
||
private static final String USER_PROMPT_TEMPLATE = """
|
||
请根据以下项目资料,生成完整的项目初始化结构化数据:
|
||
|
||
{content}
|
||
|
||
请严格按照系统提示词中的JSON格式输出,确保所有字段都包含合理的值。
|
||
""";
|
||
|
||
@Override
|
||
public ProjectInitResult generateProjectFromContent(String content) {
|
||
log.info("开始根据内容生成项目初始化数据");
|
||
|
||
// 构建用户提示词,直接将内容嵌入
|
||
String userPrompt = "请根据以下项目资料,生成完整的项目初始化结构化数据:\n\n" +
|
||
content + "\n\n" +
|
||
"请严格按照系统提示词中的JSON格式输出,确保所有字段都包含合理的值。";
|
||
|
||
return chatClient.prompt()
|
||
.system(PROJECT_INIT_SYSTEM_PROMPT)
|
||
.user(userPrompt)
|
||
.call()
|
||
.entity(ProjectInitResult.class);
|
||
}
|
||
|
||
@Override
|
||
public ProjectInitResult generateProjectFromFile(String fileUrl, String fileType) {
|
||
log.info("开始根据文件生成项目初始化数据, fileUrl: {}, fileType: {}", fileUrl, fileType);
|
||
|
||
// 从OSS下载文件内容
|
||
String content = ossService.readFileAsString(fileUrl);
|
||
|
||
if (content == null || content.isEmpty()) {
|
||
throw new RuntimeException("无法读取文件内容: " + fileUrl);
|
||
}
|
||
|
||
return generateProjectFromContent(content);
|
||
}
|
||
|
||
@Override
|
||
public ProjectInitResult generateProjectPreview(MultipartFile file) {
|
||
log.info("开始上传文件并生成项目初始化预览数据, 文件名: {}", file.getOriginalFilename());
|
||
|
||
try {
|
||
// 1. 上传文件到OSS
|
||
String fileUrl = ossService.uploadFile(file, file.getOriginalFilename());
|
||
log.info("文件上传成功, URL: {}", fileUrl);
|
||
|
||
// 2. 读取文件内容
|
||
String content = ossService.readFileAsString(fileUrl);
|
||
if (content == null || content.isEmpty()) {
|
||
throw new RuntimeException("无法读取文件内容: " + fileUrl);
|
||
}
|
||
|
||
// 3. 调用AI生成项目预览数据(不入库)
|
||
return generateProjectFromContent(content);
|
||
|
||
} catch (Exception e) {
|
||
log.error("项目预览生成失败: {}", e.getMessage(), e);
|
||
throw new RuntimeException("项目预览生成失败: " + e.getMessage(), e);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public ProjectInitResult saveProjectData(ProjectInitResult result) {
|
||
log.info("开始保存项目初始化数据");
|
||
|
||
if (result == null || result.getProject() == null) {
|
||
throw new RuntimeException("项目数据为空");
|
||
}
|
||
|
||
try {
|
||
// 保存项目数据到数据库
|
||
Long projectId = saveProjectDataToDb(result);
|
||
|
||
log.info("项目初始化数据保存成功, projectId: {}", projectId);
|
||
return result;
|
||
|
||
} catch (Exception e) {
|
||
log.error("项目保存失败: {}", e.getMessage(), e);
|
||
throw new RuntimeException("项目保存失败: " + e.getMessage(), e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 保存项目数据到数据库
|
||
*
|
||
* @param result AI生成的项目初始化结果
|
||
* @return 项目ID
|
||
*/
|
||
private Long saveProjectDataToDb(ProjectInitResult result) {
|
||
if (result == null || result.getProject() == null) {
|
||
throw new RuntimeException("项目数据为空");
|
||
}
|
||
|
||
// 1. 保存项目基本信息
|
||
Project project = convertToProject(result.getProject());
|
||
project.setProjectCode(generateProjectCode());
|
||
project.setStatus("planning"); // 初始化后状态为规划中
|
||
project.setProgress(0);
|
||
project.setVisibility(1);
|
||
projectMapper.insert(project);
|
||
Long projectId = project.getId();
|
||
log.info("项目基本信息保存成功, projectId: {}", projectId);
|
||
|
||
// 2. 保存里程碑
|
||
Map<Integer, Long> milestoneIndexToId = new HashMap<>();
|
||
if (result.getMilestones() != null && !result.getMilestones().isEmpty()) {
|
||
for (int i = 0; i < result.getMilestones().size(); i++) {
|
||
ProjectInitResult.MilestoneInfo milestoneInfo = result.getMilestones().get(i);
|
||
ProjectMilestone milestone = convertToMilestone(milestoneInfo, projectId);
|
||
milestone.setSortOrder(i);
|
||
projectMilestoneMapper.insert(milestone);
|
||
milestoneIndexToId.put(i, milestone.getId());
|
||
}
|
||
log.info("保存了 {} 个里程碑", result.getMilestones().size());
|
||
}
|
||
|
||
// 3. 保存任务(支持层级关系)
|
||
Map<String, Long> taskTempIdToId = new HashMap<>();
|
||
if (result.getTasks() != null && !result.getTasks().isEmpty()) {
|
||
// 第一轮:保存所有任务,建立临时ID到真实ID的映射
|
||
for (int i = 0; i < result.getTasks().size(); i++) {
|
||
ProjectInitResult.TaskInfo taskInfo = result.getTasks().get(i);
|
||
Task task = convertToTask(taskInfo, projectId);
|
||
|
||
// 设置里程碑关联(按索引匹配)
|
||
// 这里简化处理,可以根据任务时间范围匹配里程碑
|
||
if (!milestoneIndexToId.isEmpty()) {
|
||
// 简单策略:将任务平均分配到各个里程碑
|
||
int milestoneIndex = i % milestoneIndexToId.size();
|
||
task.setMilestoneId(milestoneIndexToId.get(milestoneIndex));
|
||
}
|
||
|
||
task.setSortOrder(i);
|
||
taskMapper.insert(task);
|
||
|
||
// 使用task_id作为临时ID进行映射
|
||
String tempId = taskInfo.getTaskId();
|
||
if (tempId != null && !tempId.isEmpty()) {
|
||
taskTempIdToId.put(tempId, task.getId());
|
||
} else {
|
||
// 兼容旧格式:如果没有task_id,使用task_name
|
||
taskTempIdToId.put(taskInfo.getTaskName(), task.getId());
|
||
}
|
||
}
|
||
|
||
// 第二轮:保存任务层级关系(parent_task_id)到 task_dependency 表
|
||
int hierarchyCount = 0;
|
||
for (int i = 0; i < result.getTasks().size(); i++) {
|
||
ProjectInitResult.TaskInfo taskInfo = result.getTasks().get(i);
|
||
if (taskInfo.getParentTaskId() != null && !taskInfo.getParentTaskId().isEmpty()) {
|
||
Long taskId = taskTempIdToId.get(taskInfo.getTaskId());
|
||
Long parentId = taskTempIdToId.get(taskInfo.getParentTaskId());
|
||
if (taskId != null && parentId != null) {
|
||
TaskDependency dependency = new TaskDependency();
|
||
dependency.setTaskId(taskId);
|
||
dependency.setDependsOnTaskId(parentId);
|
||
dependency.setDependencyType("hierarchy"); // 层级关系
|
||
dependency.setLagDays(0);
|
||
taskDependencyMapper.insert(dependency);
|
||
hierarchyCount++;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 第三轮:保存任务执行依赖关系(dependencies)到 task_dependency 表
|
||
int dependencyCount = 0;
|
||
for (int i = 0; i < result.getTasks().size(); i++) {
|
||
ProjectInitResult.TaskInfo taskInfo = result.getTasks().get(i);
|
||
if (taskInfo.getDependencies() != null && !taskInfo.getDependencies().isEmpty()) {
|
||
Long taskId = taskTempIdToId.get(taskInfo.getTaskId());
|
||
for (String dependsOnTaskId : taskInfo.getDependencies()) {
|
||
Long dependsOnId = taskTempIdToId.get(dependsOnTaskId);
|
||
if (dependsOnId != null && taskId != null) {
|
||
TaskDependency dependency = new TaskDependency();
|
||
dependency.setTaskId(taskId);
|
||
dependency.setDependsOnTaskId(dependsOnId);
|
||
dependency.setDependencyType("finish_to_start"); // 默认依赖类型
|
||
dependency.setLagDays(0);
|
||
taskDependencyMapper.insert(dependency);
|
||
dependencyCount++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
log.info("保存了 {} 个任务, {} 个层级关系, {} 个执行依赖关系",
|
||
result.getTasks().size(), hierarchyCount, dependencyCount);
|
||
}
|
||
|
||
// 4. 保存项目成员
|
||
if (result.getMembers() != null && !result.getMembers().isEmpty()) {
|
||
for (ProjectInitResult.MemberInfo memberInfo : result.getMembers()) {
|
||
ProjectMember member = convertToMember(memberInfo, projectId);
|
||
projectMemberMapper.insert(member);
|
||
}
|
||
log.info("保存了 {} 个成员", result.getMembers().size());
|
||
}
|
||
|
||
// 5. 保存资源
|
||
if (result.getResources() != null && !result.getResources().isEmpty()) {
|
||
for (ProjectInitResult.ResourceInfo resourceInfo : result.getResources()) {
|
||
Resource resource = convertToResource(resourceInfo, projectId);
|
||
resourceMapper.insert(resource);
|
||
}
|
||
log.info("保存了 {} 个资源", result.getResources().size());
|
||
}
|
||
|
||
// 6. 保存风险
|
||
if (result.getRisks() != null && !result.getRisks().isEmpty()) {
|
||
for (ProjectInitResult.RiskInfo riskInfo : result.getRisks()) {
|
||
Risk risk = convertToRisk(riskInfo, projectId);
|
||
riskMapper.insert(risk);
|
||
}
|
||
log.info("保存了 {} 个风险", result.getRisks().size());
|
||
}
|
||
|
||
// 7. 保存时间节点
|
||
if (result.getTimelineNodes() != null && !result.getTimelineNodes().isEmpty()) {
|
||
for (int i = 0; i < result.getTimelineNodes().size(); i++) {
|
||
ProjectInitResult.TimelineNodeInfo nodeInfo = result.getTimelineNodes().get(i);
|
||
ProjectTimeline timeline = convertToTimeline(nodeInfo, projectId);
|
||
timeline.setSortOrder(i);
|
||
projectTimelineMapper.insert(timeline);
|
||
}
|
||
log.info("保存了 {} 个时间节点", result.getTimelineNodes().size());
|
||
}
|
||
|
||
return projectId;
|
||
}
|
||
|
||
/**
|
||
* 生成项目编号
|
||
*/
|
||
private String generateProjectCode() {
|
||
return "PRJ" + IdUtil.fastSimpleUUID().substring(0, 12).toUpperCase();
|
||
}
|
||
|
||
/**
|
||
* 转换项目信息
|
||
*/
|
||
private Project convertToProject(ProjectInitResult.ProjectInfo info) {
|
||
Project project = new Project();
|
||
project.setProjectName(info.getProjectName());
|
||
project.setProjectType(info.getProjectType());
|
||
project.setDescription(info.getDescription());
|
||
project.setObjectives(info.getObjectives());
|
||
project.setPlanStartDate(info.getPlanStartDate());
|
||
project.setPlanEndDate(info.getPlanEndDate());
|
||
project.setBudget(info.getBudget());
|
||
project.setCurrency(info.getCurrency());
|
||
project.setPriority(info.getPriority());
|
||
project.setTags(info.getTags());
|
||
return project;
|
||
}
|
||
|
||
/**
|
||
* 转换里程碑信息
|
||
*/
|
||
private ProjectMilestone convertToMilestone(ProjectInitResult.MilestoneInfo info, Long projectId) {
|
||
ProjectMilestone milestone = new ProjectMilestone();
|
||
milestone.setProjectId(projectId);
|
||
milestone.setMilestoneName(info.getMilestoneName());
|
||
milestone.setDescription(info.getDescription());
|
||
milestone.setPlanDate(info.getPlanDate());
|
||
milestone.setStatus("pending");
|
||
milestone.setProgress(0);
|
||
milestone.setIsKey(0);
|
||
if (info.getDeliverables() != null && !info.getDeliverables().isEmpty()) {
|
||
milestone.setDeliverables(Arrays.asList(info.getDeliverables().split(",")));
|
||
}
|
||
return milestone;
|
||
}
|
||
|
||
/**
|
||
* 转换任务信息
|
||
*/
|
||
private Task convertToTask(ProjectInitResult.TaskInfo info, Long projectId) {
|
||
Task task = new Task();
|
||
task.setProjectId(projectId);
|
||
task.setTaskName(info.getTaskName());
|
||
task.setDescription(info.getDescription());
|
||
task.setPlanStartDate(info.getPlanStartDate());
|
||
task.setPlanEndDate(info.getPlanEndDate());
|
||
if (info.getEstimatedHours() != null) {
|
||
task.setPlanHours(BigDecimal.valueOf(info.getEstimatedHours()));
|
||
}
|
||
task.setPriority(info.getPriority());
|
||
task.setStatus("pending");
|
||
task.setProgress(0);
|
||
return task;
|
||
}
|
||
|
||
/**
|
||
* 转换成员信息
|
||
*/
|
||
private ProjectMember convertToMember(ProjectInitResult.MemberInfo info, Long projectId) {
|
||
ProjectMember member = new ProjectMember();
|
||
member.setProjectId(projectId);
|
||
member.setRoleCode(info.getRoleCode());
|
||
member.setResponsibility(info.getResponsibility());
|
||
if (info.getWeeklyHours() != null) {
|
||
member.setWeeklyHours(BigDecimal.valueOf(info.getWeeklyHours()));
|
||
}
|
||
member.setStatus(1);
|
||
|
||
// 根据成员名字匹配 sys_user 用户
|
||
if (info.getName() != null && !info.getName().isEmpty()) {
|
||
SysUser user = sysUserMapper.selectByRealName(info.getName());
|
||
if (user != null) {
|
||
member.setUserId(user.getId());
|
||
log.debug("成员 '{}' 匹配到用户 ID: {}", info.getName(), user.getId());
|
||
} else {
|
||
log.warn("成员 '{}' 未在系统中找到匹配的用户", info.getName());
|
||
}
|
||
}
|
||
|
||
return member;
|
||
}
|
||
|
||
/**
|
||
* 转换资源信息
|
||
*/
|
||
private Resource convertToResource(ProjectInitResult.ResourceInfo info, Long projectId) {
|
||
Resource resource = new Resource();
|
||
resource.setProjectId(projectId);
|
||
resource.setResourceName(info.getResourceName());
|
||
resource.setResourceType(info.getResourceType());
|
||
resource.setPlanQuantity(info.getQuantity());
|
||
resource.setUnit(info.getUnit());
|
||
resource.setUnitPrice(info.getUnitPrice());
|
||
resource.setSupplier(info.getSupplier());
|
||
resource.setStatus("planned");
|
||
resource.setCurrency("CNY");
|
||
return resource;
|
||
}
|
||
|
||
/**
|
||
* 转换风险信息
|
||
*/
|
||
private Risk convertToRisk(ProjectInitResult.RiskInfo info, Long projectId) {
|
||
Risk risk = new Risk();
|
||
risk.setProjectId(projectId);
|
||
risk.setRiskName(info.getRiskName());
|
||
risk.setCategory(info.getCategory());
|
||
risk.setDescription(info.getDescription());
|
||
risk.setRiskSource("ai_detection");
|
||
if (info.getProbability() != null) {
|
||
risk.setProbability(BigDecimal.valueOf(info.getProbability()));
|
||
}
|
||
if (info.getImpact() != null) {
|
||
risk.setImpact(BigDecimal.valueOf(info.getImpact()));
|
||
}
|
||
// 计算风险得分
|
||
if (risk.getProbability() != null && risk.getImpact() != null) {
|
||
BigDecimal score = risk.getProbability()
|
||
.multiply(risk.getImpact())
|
||
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
|
||
risk.setRiskScore(score);
|
||
}
|
||
// 根据概率和影响确定风险等级
|
||
risk.setRiskLevel(calculateRiskLevel(info.getProbability(), info.getImpact()));
|
||
risk.setStatus("identified");
|
||
risk.setMitigationPlan(info.getMitigationPlan());
|
||
risk.setDiscoverTime(LocalDateTime.now());
|
||
return risk;
|
||
}
|
||
|
||
/**
|
||
* 计算风险等级
|
||
*/
|
||
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";
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 转换时间节点信息
|
||
*/
|
||
private ProjectTimeline convertToTimeline(ProjectInitResult.TimelineNodeInfo info, Long projectId) {
|
||
ProjectTimeline timeline = new ProjectTimeline();
|
||
timeline.setProjectId(projectId);
|
||
timeline.setNodeName(info.getNodeName());
|
||
timeline.setNodeType(info.getNodeType());
|
||
timeline.setPlanDate(info.getPlanDate());
|
||
timeline.setDescription(info.getDescription());
|
||
timeline.setStatus("pending");
|
||
timeline.setKbScope(info.getKbScope());
|
||
return timeline;
|
||
}
|
||
|
||
// ==================== 项目查询相关实现 ====================
|
||
|
||
@Override
|
||
public TableDataInfo<ProjectListVO> getMyProjectList(Long userId, Integer pageNum, Integer pageSize,
|
||
String keyword, String status) {
|
||
log.info("查询我的项目列表, userId: {}, pageNum: {}, pageSize: {}", userId, pageNum, pageSize);
|
||
|
||
// 使用分页插件
|
||
Page<Project> page = new Page<>(pageNum, pageSize);
|
||
|
||
// 构建查询条件
|
||
LambdaQueryWrapper<Project> wrapper = new LambdaQueryWrapper<>();
|
||
wrapper.eq(Project::getDeleted, 0);
|
||
|
||
// 关键词搜索
|
||
if (keyword != null && !keyword.isEmpty()) {
|
||
wrapper.and(w -> w.like(Project::getProjectName, keyword)
|
||
.or()
|
||
.like(Project::getProjectCode, keyword));
|
||
}
|
||
|
||
// 状态筛选
|
||
if (status != null && !status.isEmpty()) {
|
||
wrapper.eq(Project::getStatus, status);
|
||
}
|
||
|
||
// 查询我管理或参与的项目
|
||
// 先查询我参与的项目ID列表
|
||
List<Long> myProjectIds = projectMemberMapper.selectList(
|
||
new LambdaQueryWrapper<ProjectMember>()
|
||
.eq(ProjectMember::getUserId, userId)
|
||
.eq(ProjectMember::getDeleted, 0)
|
||
.eq(ProjectMember::getStatus, 1)
|
||
).stream().map(ProjectMember::getProjectId).collect(Collectors.toList());
|
||
|
||
// 查询条件:我管理的项目 或 我参与的项目
|
||
wrapper.and(w -> w.eq(Project::getManagerId, userId)
|
||
.or()
|
||
.in(!myProjectIds.isEmpty(), Project::getId, myProjectIds));
|
||
|
||
wrapper.orderByDesc(Project::getCreateTime);
|
||
|
||
Page<Project> projectPage = projectMapper.selectPage(page, wrapper);
|
||
|
||
// 转换为VO
|
||
List<ProjectListVO> voList = projectPage.getRecords().stream()
|
||
.map(this::convertToProjectListVO)
|
||
.collect(Collectors.toList());
|
||
|
||
return TableDataInfo.build(new Page<ProjectListVO>(projectPage.getCurrent(), projectPage.getSize(), projectPage.getTotal())
|
||
.setRecords(voList));
|
||
}
|
||
|
||
@Override
|
||
public ProjectGanttVO getProjectGantt(Long projectId) {
|
||
log.info("查询项目甘特图数据, projectId: {}", projectId);
|
||
|
||
// 1. 查询项目基本信息
|
||
Project project = projectMapper.selectById(projectId);
|
||
if (project == null || project.getDeleted() == 1) {
|
||
throw new RuntimeException("项目不存在");
|
||
}
|
||
|
||
ProjectGanttVO ganttVO = new ProjectGanttVO();
|
||
ganttVO.setProjectId(project.getId());
|
||
ganttVO.setProjectName(project.getProjectName());
|
||
ganttVO.setProjectStatus(project.getStatus());
|
||
ganttVO.setProjectStartDate(project.getPlanStartDate());
|
||
ganttVO.setProjectEndDate(project.getPlanEndDate());
|
||
ganttVO.setProjectProgress(project.getProgress());
|
||
|
||
// 2. 查询里程碑
|
||
List<ProjectMilestone> milestones = projectMilestoneMapper.selectList(
|
||
new LambdaQueryWrapper<ProjectMilestone>()
|
||
.eq(ProjectMilestone::getProjectId, projectId)
|
||
.eq(ProjectMilestone::getDeleted, 0)
|
||
.orderByAsc(ProjectMilestone::getSortOrder)
|
||
);
|
||
|
||
List<GanttTaskVO> milestoneVOList = milestones.stream()
|
||
.map(this::convertMilestoneToGanttTask)
|
||
.collect(Collectors.toList());
|
||
ganttVO.setMilestones(milestoneVOList);
|
||
|
||
// 3. 查询任务
|
||
List<Task> tasks = taskMapper.selectList(
|
||
new LambdaQueryWrapper<Task>()
|
||
.eq(Task::getProjectId, projectId)
|
||
.eq(Task::getDeleted, 0)
|
||
.orderByAsc(Task::getSortOrder)
|
||
);
|
||
|
||
List<GanttTaskVO> taskVOList = tasks.stream()
|
||
.map(this::convertTaskToGanttTask)
|
||
.collect(Collectors.toList());
|
||
ganttVO.setTasks(taskVOList);
|
||
|
||
return ganttVO;
|
||
}
|
||
|
||
@Override
|
||
public ProjectStatisticsVO getProjectStatistics(Long userId) {
|
||
log.info("查询项目统计信息, userId: {}", userId);
|
||
|
||
ProjectStatisticsVO statistics = new ProjectStatisticsVO();
|
||
|
||
// 构建基础查询条件
|
||
LambdaQueryWrapper<Project> wrapper = new LambdaQueryWrapper<>();
|
||
wrapper.eq(Project::getDeleted, 0);
|
||
|
||
// 如果指定了用户ID,只统计该用户的项目
|
||
if (userId != null) {
|
||
List<Long> myProjectIds = projectMemberMapper.selectList(
|
||
new LambdaQueryWrapper<ProjectMember>()
|
||
.eq(ProjectMember::getUserId, userId)
|
||
.eq(ProjectMember::getDeleted, 0)
|
||
.eq(ProjectMember::getStatus, 1)
|
||
).stream().map(ProjectMember::getProjectId).collect(Collectors.toList());
|
||
|
||
wrapper.and(w -> w.eq(Project::getManagerId, userId)
|
||
.or()
|
||
.in(!myProjectIds.isEmpty(), Project::getId, myProjectIds));
|
||
}
|
||
|
||
// 1. 查询所有符合条件的项目
|
||
List<Project> projects = projectMapper.selectList(wrapper);
|
||
|
||
// 2. 统计总数
|
||
statistics.setTotalCount(projects.size());
|
||
|
||
// 3. 按状态统计
|
||
Map<String, Integer> statusCountMap = projects.stream()
|
||
.collect(Collectors.groupingBy(
|
||
Project::getStatus,
|
||
Collectors.collectingAndThen(Collectors.counting(), Long::intValue)
|
||
));
|
||
statistics.setStatusCountMap(statusCountMap);
|
||
|
||
// 设置各状态数量
|
||
statistics.setDraftCount(statusCountMap.getOrDefault("draft", 0));
|
||
statistics.setPlanningCount(statusCountMap.getOrDefault("planning", 0));
|
||
statistics.setOngoingCount(statusCountMap.getOrDefault("ongoing", 0));
|
||
statistics.setPausedCount(statusCountMap.getOrDefault("paused", 0));
|
||
statistics.setCompletedCount(statusCountMap.getOrDefault("completed", 0));
|
||
statistics.setCancelledCount(statusCountMap.getOrDefault("cancelled", 0));
|
||
|
||
// 4. 统计本月新增
|
||
YearMonth thisMonth = YearMonth.now();
|
||
int newThisMonth = (int) projects.stream()
|
||
.filter(p -> p.getCreateTime() != null &&
|
||
YearMonth.from(p.getCreateTime()).equals(thisMonth))
|
||
.count();
|
||
statistics.setNewThisMonth(newThisMonth);
|
||
|
||
// 5. 统计即将到期(7天内)
|
||
LocalDate now = LocalDate.now();
|
||
LocalDate sevenDaysLater = now.plusDays(7);
|
||
int aboutToExpire = (int) projects.stream()
|
||
.filter(p -> !"completed".equals(p.getStatus()) && !"cancelled".equals(p.getStatus()))
|
||
.filter(p -> p.getPlanEndDate() != null &&
|
||
!p.getPlanEndDate().isBefore(now) &&
|
||
!p.getPlanEndDate().isAfter(sevenDaysLater))
|
||
.count();
|
||
statistics.setAboutToExpireCount(aboutToExpire);
|
||
|
||
// 6. 计算平均进度
|
||
double avgProgress = projects.stream()
|
||
.filter(p -> p.getProgress() != null)
|
||
.mapToInt(Project::getProgress)
|
||
.average()
|
||
.orElse(0.0);
|
||
statistics.setAverageProgress(BigDecimal.valueOf(avgProgress).setScale(2, RoundingMode.HALF_UP));
|
||
|
||
// 7. 统计高风险项目
|
||
int highRiskCount = (int) projects.stream()
|
||
.filter(p -> "high".equals(p.getRiskLevel()) || "critical".equals(p.getRiskLevel()))
|
||
.count();
|
||
statistics.setHighRiskCount(highRiskCount);
|
||
|
||
return statistics;
|
||
}
|
||
|
||
@Override
|
||
public TableDataInfo<ProjectListVO> getAllProjectList(Integer pageNum, Integer pageSize,
|
||
String keyword, String status) {
|
||
log.info("查询所有项目列表, pageNum: {}, pageSize: {}", pageNum, pageSize);
|
||
|
||
Page<Project> page = new Page<>(pageNum, pageSize);
|
||
|
||
LambdaQueryWrapper<Project> wrapper = new LambdaQueryWrapper<>();
|
||
wrapper.eq(Project::getDeleted, 0);
|
||
|
||
if (keyword != null && !keyword.isEmpty()) {
|
||
wrapper.and(w -> w.like(Project::getProjectName, keyword)
|
||
.or()
|
||
.like(Project::getProjectCode, keyword));
|
||
}
|
||
|
||
if (status != null && !status.isEmpty()) {
|
||
wrapper.eq(Project::getStatus, status);
|
||
}
|
||
|
||
wrapper.orderByDesc(Project::getCreateTime);
|
||
|
||
Page<Project> projectPage = projectMapper.selectPage(page, wrapper);
|
||
|
||
List<ProjectListVO> voList = projectPage.getRecords().stream()
|
||
.map(this::convertToProjectListVO)
|
||
.collect(Collectors.toList());
|
||
|
||
return TableDataInfo.build(new Page<ProjectListVO>(projectPage.getCurrent(), projectPage.getSize(), projectPage.getTotal())
|
||
.setRecords(voList));
|
||
}
|
||
|
||
/**
|
||
* 转换为项目列表 VO(自动填充项目经理姓名)
|
||
*/
|
||
private ProjectListVO convertToProjectListVO(Project project) {
|
||
ProjectListVO vo = new ProjectListVO();
|
||
vo.setId(project.getId());
|
||
vo.setProjectCode(project.getProjectCode());
|
||
vo.setProjectName(project.getProjectName());
|
||
vo.setProjectType(project.getProjectType());
|
||
vo.setManagerId(project.getManagerId());
|
||
vo.setPlanStartDate(project.getPlanStartDate());
|
||
vo.setPlanEndDate(project.getPlanEndDate());
|
||
vo.setProgress(project.getProgress());
|
||
vo.setStatus(project.getStatus());
|
||
vo.setPriority(project.getPriority());
|
||
vo.setRiskLevel(project.getRiskLevel());
|
||
vo.setTags(project.getTags());
|
||
vo.setBudget(project.getBudget());
|
||
vo.setCost(project.getCost());
|
||
vo.setCreateTime(project.getCreateTime());
|
||
|
||
// 根据 managerId 查询项目经理姓名和头像
|
||
if (project.getManagerId() != null) {
|
||
SysUser manager = sysUserMapper.selectById(project.getManagerId());
|
||
if (manager != null && manager.getDeleted() == 0) {
|
||
vo.setManagerName(manager.getRealName());
|
||
vo.setManagerAvatar(manager.getAvatar());
|
||
}
|
||
}
|
||
|
||
return vo;
|
||
}
|
||
|
||
/**
|
||
* 转换为甘特图任务VO(里程碑)
|
||
*/
|
||
private GanttTaskVO convertMilestoneToGanttTask(ProjectMilestone milestone) {
|
||
GanttTaskVO vo = new GanttTaskVO();
|
||
vo.setId(milestone.getId());
|
||
vo.setTaskName(milestone.getMilestoneName());
|
||
vo.setType("milestone");
|
||
vo.setStartDate(milestone.getPlanDate());
|
||
vo.setEndDate(milestone.getPlanDate());
|
||
vo.setActualStartDate(milestone.getActualDate());
|
||
vo.setActualEndDate(milestone.getActualDate());
|
||
vo.setProgress(milestone.getProgress());
|
||
vo.setStatus(milestone.getStatus());
|
||
vo.setSortOrder(milestone.getSortOrder());
|
||
return vo;
|
||
}
|
||
|
||
/**
|
||
* 转换为甘特图任务VO(任务)
|
||
*/
|
||
private GanttTaskVO convertTaskToGanttTask(Task task) {
|
||
GanttTaskVO vo = new GanttTaskVO();
|
||
vo.setId(task.getId());
|
||
vo.setTaskName(task.getTaskName());
|
||
vo.setType("task");
|
||
vo.setStartDate(task.getPlanStartDate());
|
||
vo.setEndDate(task.getPlanEndDate());
|
||
vo.setActualStartDate(task.getActualStartDate());
|
||
vo.setActualEndDate(task.getActualEndDate());
|
||
vo.setProgress(task.getProgress());
|
||
vo.setStatus(task.getStatus());
|
||
vo.setPriority(task.getPriority());
|
||
vo.setSortOrder(task.getSortOrder());
|
||
return vo;
|
||
}
|
||
|
||
// ==================== 项目详情相关实现 ====================
|
||
|
||
@Override
|
||
public ProjectDetailVO getProjectDetail(Long projectId) {
|
||
log.info("查询项目详情, projectId: {}", projectId);
|
||
|
||
// 1. 查询项目基本信息
|
||
Project project = projectMapper.selectById(projectId);
|
||
if (project == null || project.getDeleted() == 1) {
|
||
throw new RuntimeException("项目不存在");
|
||
}
|
||
|
||
ProjectDetailVO detailVO = new ProjectDetailVO();
|
||
|
||
// 2. 设置项目基本信息
|
||
detailVO.setId(project.getId());
|
||
detailVO.setProjectCode(project.getProjectCode());
|
||
detailVO.setProjectName(project.getProjectName());
|
||
detailVO.setProjectType(project.getProjectType());
|
||
detailVO.setDescription(project.getDescription());
|
||
detailVO.setObjectives(project.getObjectives());
|
||
detailVO.setManagerId(project.getManagerId());
|
||
detailVO.setSponsorId(project.getSponsorId());
|
||
detailVO.setPlanStartDate(project.getPlanStartDate());
|
||
detailVO.setPlanEndDate(project.getPlanEndDate());
|
||
detailVO.setActualStartDate(project.getActualStartDate());
|
||
detailVO.setActualEndDate(project.getActualEndDate());
|
||
detailVO.setBudget(project.getBudget());
|
||
detailVO.setCost(project.getCost());
|
||
detailVO.setCurrency(project.getCurrency());
|
||
detailVO.setProgress(project.getProgress());
|
||
detailVO.setStatus(project.getStatus());
|
||
detailVO.setPriority(project.getPriority());
|
||
detailVO.setRiskLevel(project.getRiskLevel());
|
||
detailVO.setVisibility(project.getVisibility());
|
||
detailVO.setTags(project.getTags());
|
||
detailVO.setCreateTime(project.getCreateTime());
|
||
detailVO.setUpdateTime(project.getUpdateTime());
|
||
|
||
// 查询项目经理名称
|
||
if (project.getManagerId() != null) {
|
||
SysUser manager = sysUserMapper.selectById(project.getManagerId());
|
||
if (manager != null) {
|
||
detailVO.setManagerName(manager.getRealName());
|
||
}
|
||
}
|
||
|
||
// 3. 查询项目成员
|
||
List<ProjectMember> members = projectMemberMapper.selectList(
|
||
new LambdaQueryWrapper<ProjectMember>()
|
||
.eq(ProjectMember::getProjectId, projectId)
|
||
.eq(ProjectMember::getDeleted, 0)
|
||
.orderByAsc(ProjectMember::getId)
|
||
);
|
||
|
||
// 查询成员用户信息
|
||
List<Long> userIds = members.stream()
|
||
.map(ProjectMember::getUserId)
|
||
.filter(Objects::nonNull)
|
||
.collect(Collectors.toList());
|
||
Map<Long, SysUser> userMap = new HashMap<>();
|
||
if (!userIds.isEmpty()) {
|
||
List<SysUser> users = sysUserMapper.selectBatchIds(userIds);
|
||
userMap = users.stream()
|
||
.collect(Collectors.toMap(SysUser::getId, u -> u, (a, b) -> a));
|
||
}
|
||
|
||
// 转换成员信息
|
||
final Map<Long, SysUser> finalUserMap = userMap;
|
||
List<ProjectDetailVO.MemberInfo> memberVOList = members.stream()
|
||
.map(member -> {
|
||
ProjectDetailVO.MemberInfo memberInfo = new ProjectDetailVO.MemberInfo();
|
||
memberInfo.setId(member.getId());
|
||
memberInfo.setUserId(member.getUserId());
|
||
memberInfo.setRoleCode(member.getRoleCode());
|
||
memberInfo.setResponsibility(member.getResponsibility());
|
||
memberInfo.setWeeklyHours(member.getWeeklyHours());
|
||
memberInfo.setJoinDate(member.getJoinDate());
|
||
memberInfo.setStatus(member.getStatus());
|
||
|
||
// 填充用户信息
|
||
if (member.getUserId() != null) {
|
||
SysUser user = finalUserMap.get(member.getUserId());
|
||
if (user != null) {
|
||
memberInfo.setUserName(user.getUsername());
|
||
memberInfo.setRealName(user.getRealName());
|
||
memberInfo.setAvatar(user.getAvatar());
|
||
}
|
||
}
|
||
return memberInfo;
|
||
})
|
||
.collect(Collectors.toList());
|
||
detailVO.setMembers(memberVOList);
|
||
detailVO.setMemberCount(memberVOList.size());
|
||
|
||
// 4. 查询里程碑
|
||
List<ProjectMilestone> milestones = projectMilestoneMapper.selectList(
|
||
new LambdaQueryWrapper<ProjectMilestone>()
|
||
.eq(ProjectMilestone::getProjectId, projectId)
|
||
.eq(ProjectMilestone::getDeleted, 0)
|
||
.orderByAsc(ProjectMilestone::getSortOrder)
|
||
);
|
||
|
||
List<ProjectDetailVO.MilestoneInfo> milestoneVOList = milestones.stream()
|
||
.map(milestone -> {
|
||
ProjectDetailVO.MilestoneInfo milestoneInfo = new ProjectDetailVO.MilestoneInfo();
|
||
milestoneInfo.setId(milestone.getId());
|
||
milestoneInfo.setMilestoneName(milestone.getMilestoneName());
|
||
milestoneInfo.setDescription(milestone.getDescription());
|
||
milestoneInfo.setPlanDate(milestone.getPlanDate());
|
||
milestoneInfo.setActualDate(milestone.getActualDate());
|
||
milestoneInfo.setStatus(milestone.getStatus());
|
||
milestoneInfo.setProgress(milestone.getProgress());
|
||
milestoneInfo.setIsKey(milestone.getIsKey());
|
||
milestoneInfo.setDeliverables(milestone.getDeliverables());
|
||
milestoneInfo.setSortOrder(milestone.getSortOrder());
|
||
return milestoneInfo;
|
||
})
|
||
.collect(Collectors.toList());
|
||
detailVO.setMilestones(milestoneVOList);
|
||
detailVO.setMilestoneCount(milestoneVOList.size());
|
||
|
||
// 5. 查询任务
|
||
List<Task> tasks = taskMapper.selectList(
|
||
new LambdaQueryWrapper<Task>()
|
||
.eq(Task::getProjectId, projectId)
|
||
.eq(Task::getDeleted, 0)
|
||
.orderByAsc(Task::getSortOrder)
|
||
);
|
||
|
||
// 查询任务执行人信息
|
||
List<Long> assigneeIds = tasks.stream()
|
||
.map(Task::getAssigneeId)
|
||
.filter(Objects::nonNull)
|
||
.distinct()
|
||
.collect(Collectors.toList());
|
||
Map<Long, SysUser> assigneeMap = new HashMap<>();
|
||
if (!assigneeIds.isEmpty()) {
|
||
List<SysUser> assignees = sysUserMapper.selectBatchIds(assigneeIds);
|
||
assigneeMap = assignees.stream()
|
||
.collect(Collectors.toMap(SysUser::getId, u -> u, (a, b) -> a));
|
||
}
|
||
|
||
// 转换任务信息
|
||
final Map<Long, SysUser> finalAssigneeMap = assigneeMap;
|
||
List<ProjectDetailVO.TaskInfo> taskVOList = tasks.stream()
|
||
.map(task -> {
|
||
ProjectDetailVO.TaskInfo taskInfo = new ProjectDetailVO.TaskInfo();
|
||
taskInfo.setId(task.getId());
|
||
taskInfo.setTaskCode(task.getTaskCode());
|
||
taskInfo.setTaskName(task.getTaskName());
|
||
taskInfo.setDescription(task.getDescription());
|
||
taskInfo.setTaskType(task.getTaskType());
|
||
taskInfo.setMilestoneId(task.getMilestoneId());
|
||
taskInfo.setAssigneeId(task.getAssigneeId());
|
||
taskInfo.setPlanStartDate(task.getPlanStartDate());
|
||
taskInfo.setPlanEndDate(task.getPlanEndDate());
|
||
taskInfo.setActualStartDate(task.getActualStartDate());
|
||
taskInfo.setActualEndDate(task.getActualEndDate());
|
||
taskInfo.setPlanHours(task.getPlanHours());
|
||
taskInfo.setActualHours(task.getActualHours());
|
||
taskInfo.setProgress(task.getProgress());
|
||
taskInfo.setPriority(task.getPriority());
|
||
taskInfo.setStatus(task.getStatus());
|
||
taskInfo.setSortOrder(task.getSortOrder());
|
||
|
||
// 填充执行人姓名
|
||
if (task.getAssigneeId() != null) {
|
||
SysUser assignee = finalAssigneeMap.get(task.getAssigneeId());
|
||
if (assignee != null) {
|
||
taskInfo.setAssigneeName(assignee.getRealName());
|
||
}
|
||
}
|
||
return taskInfo;
|
||
})
|
||
.collect(Collectors.toList());
|
||
detailVO.setTasks(taskVOList);
|
||
detailVO.setTaskCount(taskVOList.size());
|
||
|
||
// 统计已完成任务数
|
||
int completedCount = (int) tasks.stream()
|
||
.filter(t -> "completed".equals(t.getStatus()))
|
||
.count();
|
||
detailVO.setCompletedTaskCount(completedCount);
|
||
|
||
// 6. 查询资源
|
||
List<Resource> resources = resourceMapper.selectList(
|
||
new LambdaQueryWrapper<Resource>()
|
||
.eq(Resource::getProjectId, projectId)
|
||
.eq(Resource::getDeleted, 0)
|
||
.orderByAsc(Resource::getId)
|
||
);
|
||
|
||
List<ProjectDetailVO.ResourceInfo> resourceVOList = resources.stream()
|
||
.map(resource -> {
|
||
ProjectDetailVO.ResourceInfo resourceInfo = new ProjectDetailVO.ResourceInfo();
|
||
resourceInfo.setId(resource.getId());
|
||
resourceInfo.setResourceCode(resource.getResourceCode());
|
||
resourceInfo.setResourceType(resource.getResourceType());
|
||
resourceInfo.setResourceName(resource.getResourceName());
|
||
resourceInfo.setDescription(resource.getDescription());
|
||
resourceInfo.setSpecification(resource.getSpecification());
|
||
resourceInfo.setUnit(resource.getUnit());
|
||
resourceInfo.setPlanQuantity(resource.getPlanQuantity());
|
||
resourceInfo.setActualQuantity(resource.getActualQuantity());
|
||
resourceInfo.setUnitPrice(resource.getUnitPrice());
|
||
resourceInfo.setCurrency(resource.getCurrency());
|
||
resourceInfo.setSupplier(resource.getSupplier());
|
||
resourceInfo.setStatus(resource.getStatus());
|
||
resourceInfo.setPlanArriveDate(resource.getPlanArriveDate());
|
||
resourceInfo.setActualArriveDate(resource.getActualArriveDate());
|
||
resourceInfo.setResponsibleId(resource.getResponsibleId());
|
||
resourceInfo.setLocation(resource.getLocation());
|
||
resourceInfo.setTags(resource.getTags());
|
||
return resourceInfo;
|
||
})
|
||
.collect(Collectors.toList());
|
||
detailVO.setResources(resourceVOList);
|
||
detailVO.setResourceCount(resourceVOList.size());
|
||
|
||
// 7. 查询风险
|
||
List<Risk> risks = riskMapper.selectList(
|
||
new LambdaQueryWrapper<Risk>()
|
||
.eq(Risk::getProjectId, projectId)
|
||
.eq(Risk::getDeleted, 0)
|
||
.orderByDesc(Risk::getRiskScore)
|
||
);
|
||
|
||
List<ProjectDetailVO.RiskInfo> riskVOList = risks.stream()
|
||
.map(risk -> {
|
||
ProjectDetailVO.RiskInfo riskInfo = new ProjectDetailVO.RiskInfo();
|
||
riskInfo.setId(risk.getId());
|
||
riskInfo.setRiskCode(risk.getRiskCode());
|
||
riskInfo.setCategory(risk.getCategory());
|
||
riskInfo.setRiskName(risk.getRiskName());
|
||
riskInfo.setDescription(risk.getDescription());
|
||
riskInfo.setProbability(risk.getProbability());
|
||
riskInfo.setImpact(risk.getImpact());
|
||
riskInfo.setRiskScore(risk.getRiskScore());
|
||
riskInfo.setRiskLevel(risk.getRiskLevel());
|
||
riskInfo.setStatus(risk.getStatus());
|
||
riskInfo.setOwnerId(risk.getOwnerId());
|
||
riskInfo.setMitigationPlan(risk.getMitigationPlan());
|
||
riskInfo.setDueDate(risk.getDueDate());
|
||
riskInfo.setDiscoverTime(risk.getDiscoverTime());
|
||
return riskInfo;
|
||
})
|
||
.collect(Collectors.toList());
|
||
detailVO.setRisks(riskVOList);
|
||
detailVO.setRiskCount(riskVOList.size());
|
||
|
||
// 统计高风险数量
|
||
int highRiskCount = (int) risks.stream()
|
||
.filter(r -> "high".equals(r.getRiskLevel()) || "critical".equals(r.getRiskLevel()))
|
||
.count();
|
||
detailVO.setHighRiskCount(highRiskCount);
|
||
|
||
// 8. 查询时间节点
|
||
List<ProjectTimeline> timelines = projectTimelineMapper.selectList(
|
||
new LambdaQueryWrapper<ProjectTimeline>()
|
||
.eq(ProjectTimeline::getProjectId, projectId)
|
||
.eq(ProjectTimeline::getDeleted, 0)
|
||
.orderByAsc(ProjectTimeline::getSortOrder)
|
||
);
|
||
|
||
List<ProjectDetailVO.TimelineInfo> timelineVOList = timelines.stream()
|
||
.map(timeline -> {
|
||
ProjectDetailVO.TimelineInfo timelineInfo = new ProjectDetailVO.TimelineInfo();
|
||
timelineInfo.setId(timeline.getId());
|
||
timelineInfo.setNodeName(timeline.getNodeName());
|
||
timelineInfo.setNodeType(timeline.getNodeType());
|
||
timelineInfo.setPlanDate(timeline.getPlanDate());
|
||
timelineInfo.setActualDate(timeline.getActualDate());
|
||
timelineInfo.setDescription(timeline.getDescription());
|
||
timelineInfo.setStatus(timeline.getStatus());
|
||
timelineInfo.setSortOrder(timeline.getSortOrder());
|
||
timelineInfo.setKbScope(timeline.getKbScope());
|
||
return timelineInfo;
|
||
})
|
||
.collect(Collectors.toList());
|
||
detailVO.setTimelineNodes(timelineVOList);
|
||
|
||
return detailVO;
|
||
}
|
||
}
|