项目甘特图后端接口设计
This commit is contained in:
parent
f7b877aef8
commit
9585f6dd39
|
@ -0,0 +1,45 @@
|
|||
package com.dite.znpt.domain.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 甘特图查询请求类
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "GanttChartReq", description = "甘特图查询请求")
|
||||
public class GanttChartReq {
|
||||
|
||||
@ApiModelProperty("项目ID")
|
||||
private String projectId;
|
||||
|
||||
@ApiModelProperty("任务组ID")
|
||||
private String taskGroupId;
|
||||
|
||||
@ApiModelProperty("任务状态")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty("负责人ID")
|
||||
private String mainUserId;
|
||||
|
||||
@ApiModelProperty("开始时间范围-开始")
|
||||
private LocalDate startDateFrom;
|
||||
|
||||
@ApiModelProperty("开始时间范围-结束")
|
||||
private LocalDate startDateTo;
|
||||
|
||||
@ApiModelProperty("结束时间范围-开始")
|
||||
private LocalDate endDateFrom;
|
||||
|
||||
@ApiModelProperty("结束时间范围-结束")
|
||||
private LocalDate endDateTo;
|
||||
|
||||
@ApiModelProperty("是否包含已完成任务")
|
||||
private Boolean includeCompleted = true;
|
||||
|
||||
@ApiModelProperty("是否只显示逾期任务")
|
||||
private Boolean overdueOnly = false;
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package com.dite.znpt.domain.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 甘特图数据响应类
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "GanttChartResp", description = "甘特图数据响应")
|
||||
public class GanttChartResp {
|
||||
|
||||
@ApiModelProperty("任务ID")
|
||||
private String taskId;
|
||||
|
||||
@ApiModelProperty("任务名称")
|
||||
private String taskName;
|
||||
|
||||
@ApiModelProperty("任务编号")
|
||||
private String taskCode;
|
||||
|
||||
@ApiModelProperty("上级任务ID")
|
||||
private String parentTaskId;
|
||||
|
||||
@ApiModelProperty("任务组ID")
|
||||
private String taskGroupId;
|
||||
|
||||
@ApiModelProperty("任务组名称")
|
||||
private String taskGroupName;
|
||||
|
||||
@ApiModelProperty("计划开始时间")
|
||||
private LocalDate planStartDate;
|
||||
|
||||
@ApiModelProperty("计划结束时间")
|
||||
private LocalDate planEndDate;
|
||||
|
||||
@ApiModelProperty("实际开始时间")
|
||||
private LocalDate actualStartDate;
|
||||
|
||||
@ApiModelProperty("实际结束时间")
|
||||
private LocalDate actualEndDate;
|
||||
|
||||
@ApiModelProperty("任务状态:0未开始,1进行中,2已结束")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty("任务状态描述")
|
||||
private String statusDesc;
|
||||
|
||||
@ApiModelProperty("是否逾期:0未逾期,1已逾期")
|
||||
private Integer overdueStatus;
|
||||
|
||||
@ApiModelProperty("任务负责人ID")
|
||||
private String mainUserId;
|
||||
|
||||
@ApiModelProperty("任务负责人姓名")
|
||||
private String mainUserName;
|
||||
|
||||
@ApiModelProperty("任务参与人")
|
||||
private String userIds;
|
||||
|
||||
@ApiModelProperty("任务参与人姓名列表")
|
||||
private List<String> participantNames;
|
||||
|
||||
@ApiModelProperty("进度百分比")
|
||||
private Integer progress;
|
||||
|
||||
@ApiModelProperty("任务层级")
|
||||
private Integer level;
|
||||
|
||||
@ApiModelProperty("任务在时间轴上的位置(天数)")
|
||||
private Integer position;
|
||||
|
||||
@ApiModelProperty("任务持续时间(天数)")
|
||||
private Integer duration;
|
||||
|
||||
@ApiModelProperty("子任务列表")
|
||||
private List<GanttChartResp> children;
|
||||
|
||||
@ApiModelProperty("任务备注")
|
||||
private String remark;
|
||||
|
||||
@ApiModelProperty("项目ID")
|
||||
private String projectId;
|
||||
|
||||
@ApiModelProperty("项目名称")
|
||||
private String projectName;
|
||||
}
|
|
@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|||
import com.dite.znpt.domain.entity.ProjectTaskEntity;
|
||||
import com.dite.znpt.domain.vo.ProjectTaskListReq;
|
||||
import com.dite.znpt.domain.vo.ProjectTaskResp;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -13,6 +14,7 @@ import java.util.List;
|
|||
* @date 2025/06/24 16:44
|
||||
* @Description: 项目任务信息表数据库访问层
|
||||
*/
|
||||
@Mapper
|
||||
public interface ProjectTaskMapper extends BaseMapper<ProjectTaskEntity> {
|
||||
List<ProjectTaskResp> queryBySelective(ProjectTaskListReq projectTaskReq);
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package com.dite.znpt.service;
|
||||
|
||||
import com.dite.znpt.domain.vo.GanttChartReq;
|
||||
import com.dite.znpt.domain.vo.GanttChartResp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 甘特图服务接口
|
||||
*/
|
||||
public interface GanttChartService {
|
||||
|
||||
/**
|
||||
* 获取项目甘特图数据
|
||||
*
|
||||
* @param req 查询条件
|
||||
* @return 甘特图数据列表
|
||||
*/
|
||||
List<GanttChartResp> getGanttChartData(GanttChartReq req);
|
||||
|
||||
/**
|
||||
* 获取项目甘特图统计信息
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 统计信息
|
||||
*/
|
||||
Object getGanttChartStatistics(String projectId);
|
||||
|
||||
/**
|
||||
* 获取项目甘特图时间轴信息
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 时间轴信息
|
||||
*/
|
||||
Object getGanttChartTimeline(String projectId);
|
||||
|
||||
/**
|
||||
* 更新任务进度
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param progress 进度百分比
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean updateTaskProgress(String taskId, Integer progress);
|
||||
|
||||
/**
|
||||
* 拖拽更新任务时间
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param startDate 开始时间
|
||||
* @param endDate 结束时间
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean updateTaskTime(String taskId, String startDate, String endDate);
|
||||
}
|
|
@ -0,0 +1,486 @@
|
|||
package com.dite.znpt.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.dite.znpt.domain.entity.ProjectTaskEntity;
|
||||
import com.dite.znpt.domain.entity.UserEntity;
|
||||
import com.dite.znpt.domain.vo.GanttChartReq;
|
||||
import com.dite.znpt.domain.vo.GanttChartResp;
|
||||
import com.dite.znpt.mapper.ProjectTaskMapper;
|
||||
import com.dite.znpt.service.GanttChartService;
|
||||
import com.dite.znpt.service.UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 甘特图服务实现类
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GanttChartServiceImpl implements GanttChartService {
|
||||
|
||||
private final ProjectTaskMapper projectTaskMapper;
|
||||
private final UserService userService;
|
||||
|
||||
@Override
|
||||
public List<GanttChartResp> getGanttChartData(GanttChartReq req) {
|
||||
// 查询所有任务
|
||||
QueryWrapper<ProjectTaskEntity> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
if (StrUtil.isNotBlank(req.getProjectId())) {
|
||||
queryWrapper.eq("project_id", req.getProjectId());
|
||||
}
|
||||
if (StrUtil.isNotBlank(req.getTaskGroupId())) {
|
||||
queryWrapper.eq("task_group_id", req.getTaskGroupId());
|
||||
}
|
||||
if (req.getStatus() != null) {
|
||||
queryWrapper.eq("status", req.getStatus());
|
||||
}
|
||||
if (StrUtil.isNotBlank(req.getMainUserId())) {
|
||||
queryWrapper.eq("main_user_id", req.getMainUserId());
|
||||
}
|
||||
if (req.getStartDateFrom() != null) {
|
||||
queryWrapper.ge("plan_start_date", req.getStartDateFrom());
|
||||
}
|
||||
if (req.getStartDateTo() != null) {
|
||||
queryWrapper.le("plan_start_date", req.getStartDateTo());
|
||||
}
|
||||
if (req.getEndDateFrom() != null) {
|
||||
queryWrapper.ge("plan_end_date", req.getEndDateFrom());
|
||||
}
|
||||
if (req.getEndDateTo() != null) {
|
||||
queryWrapper.le("plan_end_date", req.getEndDateTo());
|
||||
}
|
||||
if (req.getOverdueOnly() != null && req.getOverdueOnly()) {
|
||||
queryWrapper.eq("overdue_status", 1);
|
||||
}
|
||||
if (req.getIncludeCompleted() != null && !req.getIncludeCompleted()) {
|
||||
queryWrapper.ne("status", 2);
|
||||
}
|
||||
|
||||
queryWrapper.orderByAsc("plan_start_date");
|
||||
|
||||
List<ProjectTaskEntity> taskList = projectTaskMapper.selectList(queryWrapper);
|
||||
|
||||
// 获取用户信息
|
||||
final Set<String> userIds = new HashSet<>();
|
||||
taskList.forEach(task -> {
|
||||
if (StrUtil.isNotBlank(task.getMainUserId())) {
|
||||
userIds.add(task.getMainUserId());
|
||||
}
|
||||
if (StrUtil.isNotBlank(task.getUserIds())) {
|
||||
userIds.addAll(Arrays.asList(task.getUserIds().split(",")));
|
||||
}
|
||||
});
|
||||
|
||||
final Map<String, UserEntity> userMap = new HashMap<>();
|
||||
if (CollUtil.isNotEmpty(userIds)) {
|
||||
List<UserEntity> users = userService.listByIds(userIds);
|
||||
userMap.putAll(users.stream().collect(Collectors.toMap(UserEntity::getUserId, user -> user)));
|
||||
}
|
||||
|
||||
// 批量获取项目时间范围,避免重复查询
|
||||
final Map<String, LocalDate> projectStartDates = new HashMap<>();
|
||||
if (StrUtil.isNotBlank(req.getProjectId())) {
|
||||
// 如果指定了项目ID,直接获取该项目的时间范围
|
||||
LocalDate projectStart = getProjectStartDate(req.getProjectId());
|
||||
projectStartDates.put(req.getProjectId(), projectStart);
|
||||
} else {
|
||||
// 如果没有指定项目ID,获取所有相关项目的时间范围
|
||||
Set<String> projectIds = taskList.stream()
|
||||
.map(ProjectTaskEntity::getProjectId)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
for (String projectId : projectIds) {
|
||||
LocalDate projectStart = getProjectStartDate(projectId);
|
||||
projectStartDates.put(projectId, projectStart);
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为甘特图数据
|
||||
List<GanttChartResp> ganttData = taskList.stream().map(task -> {
|
||||
GanttChartResp resp = BeanUtil.copyProperties(task, GanttChartResp.class);
|
||||
|
||||
// 设置状态描述
|
||||
switch (task.getStatus()) {
|
||||
case 0:
|
||||
resp.setStatusDesc("未开始");
|
||||
break;
|
||||
case 1:
|
||||
resp.setStatusDesc("进行中");
|
||||
break;
|
||||
case 2:
|
||||
resp.setStatusDesc("已结束");
|
||||
break;
|
||||
default:
|
||||
resp.setStatusDesc("未知");
|
||||
}
|
||||
|
||||
// 设置负责人姓名
|
||||
if (StrUtil.isNotBlank(task.getMainUserId()) && userMap.containsKey(task.getMainUserId())) {
|
||||
resp.setMainUserName(userMap.get(task.getMainUserId()).getName());
|
||||
}
|
||||
|
||||
// 设置参与人姓名列表
|
||||
if (StrUtil.isNotBlank(task.getUserIds())) {
|
||||
List<String> participantNames = Arrays.stream(task.getUserIds().split(","))
|
||||
.filter(userId -> userMap.containsKey(userId))
|
||||
.map(userId -> userMap.get(userId).getName())
|
||||
.collect(Collectors.toList());
|
||||
resp.setParticipantNames(participantNames);
|
||||
}
|
||||
|
||||
// 计算进度(这里可以根据实际业务逻辑调整)
|
||||
if (task.getStatus() == 2) {
|
||||
resp.setProgress(100);
|
||||
} else if (task.getStatus() == 1) {
|
||||
resp.setProgress(50); // 默认进度,实际应该从数据库字段获取
|
||||
} else {
|
||||
resp.setProgress(0);
|
||||
}
|
||||
|
||||
// 计算任务在时间轴上的位置和持续时间
|
||||
LocalDate projectStart = projectStartDates.get(task.getProjectId());
|
||||
if (projectStart != null && task.getPlanStartDate() != null) {
|
||||
int position = (int) ChronoUnit.DAYS.between(projectStart, task.getPlanStartDate());
|
||||
resp.setPosition(position);
|
||||
}
|
||||
|
||||
// 计算任务持续时间
|
||||
if (task.getPlanStartDate() != null && task.getPlanEndDate() != null) {
|
||||
int duration = (int) ChronoUnit.DAYS.between(task.getPlanStartDate(), task.getPlanEndDate()) + 1;
|
||||
resp.setDuration(duration);
|
||||
} else {
|
||||
resp.setDuration(1); // 默认持续1天
|
||||
}
|
||||
|
||||
return resp;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
// 构建树形结构
|
||||
return buildTreeStructure(ganttData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGanttChartStatistics(String projectId) {
|
||||
QueryWrapper<ProjectTaskEntity> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("project_id", projectId);
|
||||
|
||||
List<ProjectTaskEntity> taskList = projectTaskMapper.selectList(queryWrapper);
|
||||
|
||||
Map<String, Object> statistics = new HashMap<>();
|
||||
statistics.put("totalTasks", taskList.size());
|
||||
statistics.put("notStarted", (int) taskList.stream().filter(task -> task.getStatus() == 0).count());
|
||||
statistics.put("inProgress", (int) taskList.stream().filter(task -> task.getStatus() == 1).count());
|
||||
statistics.put("completed", (int) taskList.stream().filter(task -> task.getStatus() == 2).count());
|
||||
statistics.put("overdue", (int) taskList.stream().filter(task -> task.getOverdueStatus() == 1).count());
|
||||
|
||||
// 计算总体进度
|
||||
if (taskList.size() > 0) {
|
||||
final double avgProgress = taskList.stream()
|
||||
.mapToInt(task -> {
|
||||
if (task.getStatus() == 2) return 100;
|
||||
else if (task.getStatus() == 1) return 50;
|
||||
else return 0;
|
||||
})
|
||||
.average()
|
||||
.orElse(0.0);
|
||||
statistics.put("overallProgress", Math.round(avgProgress));
|
||||
} else {
|
||||
statistics.put("overallProgress", 0);
|
||||
}
|
||||
|
||||
return statistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGanttChartTimeline(String projectId) {
|
||||
if (StrUtil.isBlank(projectId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LocalDate projectStartDate = getProjectStartDate(projectId);
|
||||
LocalDate projectEndDate = getProjectEndDate(projectId);
|
||||
|
||||
Map<String, Object> timeline = new HashMap<>();
|
||||
timeline.put("projectId", projectId);
|
||||
timeline.put("startDate", projectStartDate);
|
||||
timeline.put("endDate", projectEndDate);
|
||||
timeline.put("totalDays", ChronoUnit.DAYS.between(projectStartDate, projectEndDate) + 1);
|
||||
|
||||
// 计算时间轴上的关键时间点(比如每周、每月)
|
||||
List<Map<String, Object>> timePoints = new ArrayList<>();
|
||||
LocalDate currentDate = projectStartDate;
|
||||
|
||||
while (!currentDate.isAfter(projectEndDate)) {
|
||||
Map<String, Object> timePoint = new HashMap<>();
|
||||
timePoint.put("date", currentDate);
|
||||
timePoint.put("dayOfWeek", currentDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.SHORT, java.util.Locale.CHINESE));
|
||||
timePoint.put("isWeekend", currentDate.getDayOfWeek().getValue() >= 6);
|
||||
timePoints.add(timePoint);
|
||||
|
||||
currentDate = currentDate.plusDays(1);
|
||||
}
|
||||
|
||||
timeline.put("timePoints", timePoints);
|
||||
|
||||
return timeline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateTaskProgress(String taskId, Integer progress) {
|
||||
ProjectTaskEntity task = projectTaskMapper.selectById(taskId);
|
||||
if (task == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 更新进度
|
||||
// 这里需要根据实际业务逻辑添加进度字段
|
||||
// task.setProgress(progress);
|
||||
|
||||
// 根据进度更新状态
|
||||
if (progress >= 100) {
|
||||
task.setStatus(2); // 已完成
|
||||
task.setActualEndDate(LocalDate.now());
|
||||
} else if (progress > 0) {
|
||||
task.setStatus(1); // 进行中
|
||||
if (task.getActualStartDate() == null) {
|
||||
task.setActualStartDate(LocalDate.now());
|
||||
}
|
||||
}
|
||||
|
||||
return projectTaskMapper.updateById(task) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateTaskTime(String taskId, String startDate, String endDate) {
|
||||
ProjectTaskEntity task = projectTaskMapper.selectById(taskId);
|
||||
if (task == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
LocalDate newStartDate = null;
|
||||
LocalDate newEndDate = null;
|
||||
|
||||
try {
|
||||
if (StrUtil.isNotBlank(startDate)) {
|
||||
newStartDate = LocalDate.parse(startDate, formatter);
|
||||
}
|
||||
if (StrUtil.isNotBlank(endDate)) {
|
||||
newEndDate = LocalDate.parse(endDate, formatter);
|
||||
}
|
||||
} catch (DateTimeParseException e) {
|
||||
return false; // 日期格式错误
|
||||
}
|
||||
|
||||
// 验证时间约束
|
||||
if (!validateTaskTimeConstraints(task, newStartDate, newEndDate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 更新任务时间
|
||||
if (newStartDate != null) {
|
||||
task.setPlanStartDate(newStartDate);
|
||||
}
|
||||
if (newEndDate != null) {
|
||||
task.setPlanEndDate(newEndDate);
|
||||
}
|
||||
|
||||
// 更新逾期状态
|
||||
updateOverdueStatus(task);
|
||||
|
||||
return projectTaskMapper.updateById(task) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建树形结构
|
||||
*/
|
||||
private List<GanttChartResp> buildTreeStructure(List<GanttChartResp> allTasks) {
|
||||
final Map<String, GanttChartResp> taskMap = allTasks.stream()
|
||||
.collect(Collectors.toMap(GanttChartResp::getTaskId, task -> task));
|
||||
|
||||
final List<GanttChartResp> rootTasks = new ArrayList<>();
|
||||
|
||||
for (GanttChartResp task : allTasks) {
|
||||
if (StrUtil.isBlank(task.getParentTaskId())) {
|
||||
// 根任务
|
||||
task.setLevel(0);
|
||||
rootTasks.add(task);
|
||||
} else {
|
||||
// 子任务
|
||||
GanttChartResp parentTask = taskMap.get(task.getParentTaskId());
|
||||
if (parentTask != null) {
|
||||
task.setLevel(parentTask.getLevel() + 1);
|
||||
if (parentTask.getChildren() == null) {
|
||||
parentTask.setChildren(new ArrayList<>());
|
||||
}
|
||||
parentTask.getChildren().add(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rootTasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目开始时间
|
||||
* 从项目任务中计算最早的计划开始时间作为项目开始时间
|
||||
*/
|
||||
private LocalDate getProjectStartDate(String projectId) {
|
||||
if (StrUtil.isBlank(projectId)) {
|
||||
return LocalDate.now().minusDays(30); // 默认值
|
||||
}
|
||||
|
||||
// 查询项目中所有任务的计划开始时间,取最早的时间
|
||||
QueryWrapper<ProjectTaskEntity> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("project_id", projectId)
|
||||
.isNotNull("plan_start_date")
|
||||
.orderByAsc("plan_start_date")
|
||||
.last("LIMIT 1");
|
||||
|
||||
ProjectTaskEntity earliestTask = projectTaskMapper.selectOne(queryWrapper);
|
||||
|
||||
if (earliestTask != null && earliestTask.getPlanStartDate() != null) {
|
||||
return earliestTask.getPlanStartDate();
|
||||
}
|
||||
|
||||
// 如果没有找到有效的开始时间,返回当前时间减去30天作为默认值
|
||||
return LocalDate.now().minusDays(30);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目结束时间
|
||||
* 从项目任务中计算最晚的计划结束时间作为项目结束时间
|
||||
*/
|
||||
private LocalDate getProjectEndDate(String projectId) {
|
||||
if (StrUtil.isBlank(projectId)) {
|
||||
return LocalDate.now().plusDays(30); // 默认值
|
||||
}
|
||||
|
||||
// 查询项目中所有任务的计划结束时间,取最晚的时间
|
||||
QueryWrapper<ProjectTaskEntity> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("project_id", projectId)
|
||||
.isNotNull("plan_end_date")
|
||||
.orderByDesc("plan_end_date")
|
||||
.last("LIMIT 1");
|
||||
|
||||
ProjectTaskEntity latestTask = projectTaskMapper.selectOne(queryWrapper);
|
||||
|
||||
if (latestTask != null && latestTask.getPlanEndDate() != null) {
|
||||
return latestTask.getPlanEndDate();
|
||||
}
|
||||
|
||||
// 如果没有找到有效的结束时间,返回当前时间加上30天作为默认值
|
||||
return LocalDate.now().plusDays(30);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目时间范围
|
||||
* 返回项目的开始时间和结束时间
|
||||
*/
|
||||
private Map<String, LocalDate> getProjectTimeRange(String projectId) {
|
||||
Map<String, LocalDate> timeRange = new HashMap<>();
|
||||
timeRange.put("startDate", getProjectStartDate(projectId));
|
||||
timeRange.put("endDate", getProjectEndDate(projectId));
|
||||
return timeRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取项目时间范围
|
||||
* 通过一次查询获取多个项目的时间范围,提高性能
|
||||
*/
|
||||
private Map<String, Map<String, LocalDate>> getBatchProjectTimeRanges(Set<String> projectIds) {
|
||||
Map<String, Map<String, LocalDate>> timeRanges = new HashMap<>();
|
||||
|
||||
for (String projectId : projectIds) {
|
||||
Map<String, LocalDate> timeRange = getProjectTimeRange(projectId);
|
||||
timeRanges.put(projectId, timeRange);
|
||||
}
|
||||
|
||||
return timeRanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算任务进度
|
||||
*/
|
||||
private Integer calculateProgress(ProjectTaskEntity task) {
|
||||
if (task.getStatus() == 2) return 100; // 已完成
|
||||
else if (task.getStatus() == 1) return 50; // 进行中
|
||||
else return 0; // 未开始
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算任务持续时间
|
||||
*/
|
||||
private Integer calculateDuration(ProjectTaskEntity task) {
|
||||
if (task.getPlanStartDate() != null && task.getPlanEndDate() != null) {
|
||||
return (int) ChronoUnit.DAYS.between(task.getPlanStartDate(), task.getPlanEndDate()) + 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算任务在时间轴上的位置
|
||||
*/
|
||||
private Integer calculatePosition(ProjectTaskEntity task) {
|
||||
LocalDate projectStart = getProjectStartDate(task.getProjectId());
|
||||
if (task.getPlanStartDate() != null && projectStart != null) {
|
||||
return (int) ChronoUnit.DAYS.between(projectStart, task.getPlanStartDate());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证任务时间约束
|
||||
*/
|
||||
private boolean validateTaskTimeConstraints(ProjectTaskEntity task,
|
||||
LocalDate newStartDate,
|
||||
LocalDate newEndDate) {
|
||||
// 验证子任务不能早于父任务开始
|
||||
if (StrUtil.isNotBlank(task.getParentTaskId())) {
|
||||
ProjectTaskEntity parentTask = projectTaskMapper.selectById(task.getParentTaskId());
|
||||
if (parentTask != null && newStartDate != null) {
|
||||
if (newStartDate.isBefore(parentTask.getPlanStartDate())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证结束时间不能早于开始时间
|
||||
if (newStartDate != null && newEndDate != null) {
|
||||
if (newEndDate.isBefore(newStartDate)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新逾期状态
|
||||
*/
|
||||
private void updateOverdueStatus(ProjectTaskEntity task) {
|
||||
if (task.getPlanEndDate() != null &&
|
||||
task.getPlanEndDate().isBefore(LocalDate.now()) &&
|
||||
task.getStatus() != 2) {
|
||||
task.setOverdueStatus(1); // 标记为逾期
|
||||
} else {
|
||||
task.setOverdueStatus(0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package com.dite.znpt.web.controller;
|
||||
|
||||
import com.dite.znpt.domain.Result;
|
||||
import com.dite.znpt.domain.vo.GanttChartReq;
|
||||
import com.dite.znpt.domain.vo.GanttChartResp;
|
||||
import com.dite.znpt.service.GanttChartService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 甘特图控制器
|
||||
*/
|
||||
@Api(tags = "甘特图管理")
|
||||
@RestController
|
||||
@RequestMapping("/gantt-chart")
|
||||
public class GanttChartController {
|
||||
|
||||
@Resource
|
||||
private GanttChartService ganttChartService;
|
||||
|
||||
@ApiOperation(value = "获取项目甘特图数据", httpMethod = "GET")
|
||||
@GetMapping("/data")
|
||||
public Result<List<GanttChartResp>> getGanttChartData(GanttChartReq req) {
|
||||
List<GanttChartResp> data = ganttChartService.getGanttChartData(req);
|
||||
return Result.ok(data);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "获取项目甘特图统计信息", httpMethod = "GET")
|
||||
@GetMapping("/statistics/{projectId}")
|
||||
public Result<Object> getGanttChartStatistics(@PathVariable String projectId) {
|
||||
Object statistics = ganttChartService.getGanttChartStatistics(projectId);
|
||||
return Result.ok(statistics);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "获取项目甘特图时间轴信息", httpMethod = "GET")
|
||||
@GetMapping("/timeline/{projectId}")
|
||||
public Result<Object> getGanttChartTimeline(@PathVariable String projectId) {
|
||||
Object timeline = ganttChartService.getGanttChartTimeline(projectId);
|
||||
return Result.ok(timeline);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "更新任务进度", httpMethod = "PUT")
|
||||
@PutMapping("/progress/{taskId}")
|
||||
public Result<Boolean> updateTaskProgress(@PathVariable String taskId, @RequestParam Integer progress) {
|
||||
boolean success = ganttChartService.updateTaskProgress(taskId, progress);
|
||||
return Result.ok(success);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "拖拽更新任务时间", httpMethod = "PUT")
|
||||
@PutMapping("/time/{taskId}")
|
||||
public Result<Boolean> updateTaskTime(@PathVariable String taskId,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate) {
|
||||
boolean success = ganttChartService.updateTaskTime(taskId, startDate, endDate);
|
||||
return Result.ok(success);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue