feat(project): 新增AI项目初始化及数据持久化功能
- 新增ProjectInitResult DTO,定义项目初始化的结构化数据格式 - 实现ProjectServiceImpl,支持从文本或文件生成项目数据并保存 - 集成Spring AI结构化输出能力,解析项目文档生成计划数据 - 实现项目、里程碑、任务、成员、资源、风险、时间节点数据转换及数据库保存 - 支持任务的层级关系和执行依赖关系持久化 - 新增Task实体,完善任务相关字段及数据库映射 - 添加详细日志,支持事务回滚保障数据一致性 - 新增数据库设计SQL文档,定义项目管理相关表结构及索引
This commit is contained in:
@@ -405,7 +405,6 @@ CREATE TABLE task (
|
||||
task_code VARCHAR(50),
|
||||
project_id BIGINT NOT NULL,
|
||||
milestone_id BIGINT,
|
||||
parent_id BIGINT,
|
||||
task_name VARCHAR(200) NOT NULL,
|
||||
description TEXT,
|
||||
task_type VARCHAR(50),
|
||||
@@ -431,13 +430,11 @@ CREATE TABLE task (
|
||||
|
||||
CONSTRAINT fk_task_project FOREIGN KEY (project_id) REFERENCES project(id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_task_milestone FOREIGN KEY (milestone_id) REFERENCES project_milestone(id) ON DELETE SET NULL,
|
||||
CONSTRAINT fk_task_parent FOREIGN KEY (parent_id) REFERENCES task(id) ON DELETE SET NULL,
|
||||
CONSTRAINT fk_task_assignee FOREIGN KEY (assignee_id) REFERENCES sys_user(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_task_project ON task(project_id);
|
||||
CREATE INDEX idx_task_milestone ON task(milestone_id);
|
||||
CREATE INDEX idx_task_parent ON task(parent_id);
|
||||
CREATE INDEX idx_task_assignee ON task(assignee_id);
|
||||
CREATE INDEX idx_task_status ON task(status);
|
||||
|
||||
@@ -445,7 +442,6 @@ COMMENT ON TABLE task IS '任务表';
|
||||
COMMENT ON COLUMN task.task_code IS '任务编号';
|
||||
COMMENT ON COLUMN task.project_id IS '项目ID';
|
||||
COMMENT ON COLUMN task.milestone_id IS '所属里程碑ID';
|
||||
COMMENT ON COLUMN task.parent_id IS '父任务ID';
|
||||
COMMENT ON COLUMN task.task_name IS '任务名称';
|
||||
COMMENT ON COLUMN task.description IS '任务描述';
|
||||
COMMENT ON COLUMN task.task_type IS '任务类型';
|
||||
@@ -475,8 +471,10 @@ CREATE TABLE task_dependency (
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
UNIQUE(task_id, depends_on_task_id),
|
||||
|
||||
-- 修正了下面的 fk_td_depends 约束
|
||||
CONSTRAINT fk_td_task FOREIGN KEY (task_id) REFERENCES task(id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_td_depends ON DELETE CASCADE
|
||||
CONSTRAINT fk_td_depends FOREIGN KEY (depends_on_task_id) REFERENCES task(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_td_depends ON task_dependency(depends_on_task_id);
|
||||
|
||||
@@ -35,11 +35,6 @@ public class Task {
|
||||
*/
|
||||
private Long milestoneId;
|
||||
|
||||
/**
|
||||
* 父任务ID
|
||||
*/
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 任务名称
|
||||
*/
|
||||
|
||||
44
src/main/java/cn/yinlihupo/domain/entity/TaskDependency.java
Normal file
44
src/main/java/cn/yinlihupo/domain/entity/TaskDependency.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package cn.yinlihupo.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 任务依赖关系实体类
|
||||
* 对应数据库表: task_dependency
|
||||
*/
|
||||
@Data
|
||||
@TableName("task_dependency")
|
||||
public class TaskDependency {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 任务ID
|
||||
*/
|
||||
private Long taskId;
|
||||
|
||||
/**
|
||||
* 依赖的任务ID
|
||||
*/
|
||||
private Long dependsOnTaskId;
|
||||
|
||||
/**
|
||||
* 依赖类型: finish_to_start-完成-开始, start_to_start-开始-开始, finish_to_finish-完成-完成, start_to_finish-开始-完成
|
||||
*/
|
||||
private String dependencyType;
|
||||
|
||||
/**
|
||||
* 滞后天数
|
||||
*/
|
||||
private Integer lagDays;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -111,6 +111,9 @@ public class ProjectInitResult {
|
||||
|
||||
@Data
|
||||
public static class TaskInfo {
|
||||
@JsonProperty("task_id")
|
||||
private String taskId;
|
||||
|
||||
@JsonProperty("task_name")
|
||||
private String taskName;
|
||||
|
||||
|
||||
12
src/main/java/cn/yinlihupo/mapper/TaskDependencyMapper.java
Normal file
12
src/main/java/cn/yinlihupo/mapper/TaskDependencyMapper.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package cn.yinlihupo.mapper;
|
||||
|
||||
import cn.yinlihupo.domain.entity.TaskDependency;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 任务依赖关系Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface TaskDependencyMapper extends BaseMapper<TaskDependency> {
|
||||
}
|
||||
@@ -40,6 +40,7 @@ public class ProjectServiceImpl implements ProjectService {
|
||||
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;
|
||||
@@ -93,6 +94,7 @@ public class ProjectServiceImpl implements ProjectService {
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"task_id": "T001",
|
||||
"task_name": "任务名称",
|
||||
"parent_task_id": null,
|
||||
"description": "任务描述",
|
||||
@@ -101,7 +103,7 @@ public class ProjectServiceImpl implements ProjectService {
|
||||
"estimated_hours": 80,
|
||||
"priority": "high",
|
||||
"assignee_role": "执行者角色",
|
||||
"dependencies": ["前置任务ID"],
|
||||
"dependencies": ["T001"],
|
||||
"deliverables": "交付物"
|
||||
}
|
||||
],
|
||||
@@ -313,26 +315,57 @@ public class ProjectServiceImpl implements ProjectService {
|
||||
task.setSortOrder(i);
|
||||
taskMapper.insert(task);
|
||||
|
||||
// 使用任务名称作为临时ID进行映射
|
||||
String tempId = taskInfo.getTaskName();
|
||||
taskTempIdToId.put(tempId, task.getId());
|
||||
// 使用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()) {
|
||||
Task task = new Task();
|
||||
task.setId(taskTempIdToId.get(taskInfo.getTaskName()));
|
||||
// 查找父任务ID
|
||||
Long taskId = taskTempIdToId.get(taskInfo.getTaskId());
|
||||
Long parentId = taskTempIdToId.get(taskInfo.getParentTaskId());
|
||||
if (parentId != null) {
|
||||
task.setParentId(parentId);
|
||||
taskMapper.updateById(task);
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("保存了 {} 个任务", result.getTasks().size());
|
||||
|
||||
// 第三轮:保存任务执行依赖关系(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. 保存项目成员
|
||||
|
||||
Reference in New Issue
Block a user