feat(project): 实现AI项目初始化及相关实体管理
- 新增通用返回类BaseResponse用于统一接口响应格式 - 新增业务异常BusinessException及全局异常处理GlobalExceptionHandler - 新增OSS文件上传控制器支持文件上传与删除接口 - 添加项目核心实体类Project、ProjectMember、ProjectMilestone、ProjectTimeline和Resource - 实现ProjectService接口及其实现类,使用AI能力从项目文档生成结构化项目数据 - 在ProjectServiceImpl中实现项目数据解析、保存及业务逻辑,包括项目、里程碑、任务、成员、资源、风险等 - 项目初始化控制器ProjectController提供文件上传触发项目初始化功能 - 设计了详细的系统提示词和用户提示词,用于AI模型指导生成严格格式的结构化项目数据 - 设计项目数据持久化流程,确保生成的数据正确保存至数据库,支持事务回滚 - 增强日志记录,便于追踪项目初始化全过程及错误调试
This commit is contained in:
35
src/main/java/cn/yinlihupo/common/core/BaseResponse.java
Normal file
35
src/main/java/cn/yinlihupo/common/core/BaseResponse.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package cn.yinlihupo.common.core;
|
||||
|
||||
import cn.yinlihupo.common.enums.ErrorCode;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 通用返回类
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
@Data
|
||||
public class BaseResponse<T> implements Serializable {
|
||||
|
||||
private int code;
|
||||
|
||||
private T data;
|
||||
|
||||
private String message;
|
||||
|
||||
public BaseResponse(int code, T data, String message) {
|
||||
this.code = code;
|
||||
this.data = data;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public BaseResponse(int code, T data) {
|
||||
this(code, data, "");
|
||||
}
|
||||
|
||||
public BaseResponse(ErrorCode errorCode) {
|
||||
this(errorCode.getCode(), null, errorCode.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cn.yinlihupo.common.exception;
|
||||
|
||||
|
||||
import cn.yinlihupo.common.enums.ErrorCode;
|
||||
|
||||
/**
|
||||
* 自定义异常类
|
||||
*/
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private final int code;
|
||||
|
||||
public BusinessException(ErrorCode errorCode) {
|
||||
super(errorCode.getMessage());
|
||||
this.code = errorCode.getCode();
|
||||
}
|
||||
|
||||
public BusinessException(ErrorCode errorCode, String message) {
|
||||
super(message);
|
||||
this.code = errorCode.getCode();
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package cn.yinlihupo.common.exception;
|
||||
|
||||
import cn.hutool.core.exceptions.UtilException;
|
||||
import cn.yinlihupo.common.core.BaseResponse;
|
||||
import cn.yinlihupo.common.enums.ErrorCode;
|
||||
import cn.yinlihupo.common.util.ResultUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
*
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public BaseResponse<?> businessExceptionHandler(BusinessException e) {
|
||||
log.error("BusinessException", e);
|
||||
return ResultUtils.error(e.getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(UtilException.class)
|
||||
public BaseResponse<?> utilExceptionHandler(UtilException e) {
|
||||
log.error("UtilException", e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public BaseResponse<?> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
|
||||
log.error("MethodArgumentNotValidException", e);
|
||||
String errorMsg = e.getBindingResult().getFieldErrors().stream()
|
||||
.map(FieldError::getDefaultMessage)
|
||||
.collect(Collectors.joining("; "));
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, errorMsg);
|
||||
}
|
||||
|
||||
@ExceptionHandler(BindException.class)
|
||||
public BaseResponse<?> bindExceptionHandler(BindException e) {
|
||||
log.error("BindException", e);
|
||||
String errorMsg = e.getBindingResult().getFieldErrors().stream()
|
||||
.map(FieldError::getDefaultMessage)
|
||||
.collect(Collectors.joining("; "));
|
||||
return ResultUtils.error(ErrorCode.PARAMS_ERROR, errorMsg);
|
||||
}
|
||||
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
public BaseResponse<?> runtimeExceptionHandler(RuntimeException e) {
|
||||
log.error("RuntimeException", e);
|
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "系统错误");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package cn.yinlihupo.common.result;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 统一响应结果封装
|
||||
*
|
||||
* @param <T> 数据类型
|
||||
*/
|
||||
@Data
|
||||
public class Result<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 响应消息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 响应数据
|
||||
*/
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
public Result() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public Result(Integer code, String message, T data) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应
|
||||
*/
|
||||
public static <T> Result<T> success() {
|
||||
return new Result<>(200, "操作成功", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应(带数据)
|
||||
*/
|
||||
public static <T> Result<T> success(T data) {
|
||||
return new Result<>(200, "操作成功", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应(带消息和数据)
|
||||
*/
|
||||
public static <T> Result<T> success(String message, T data) {
|
||||
return new Result<>(200, message, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应
|
||||
*/
|
||||
public static <T> Result<T> error() {
|
||||
return new Result<>(500, "操作失败", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应(带消息)
|
||||
*/
|
||||
public static <T> Result<T> error(String message) {
|
||||
return new Result<>(500, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败响应(带状态码和消息)
|
||||
*/
|
||||
public static <T> Result<T> error(Integer code, String message) {
|
||||
return new Result<>(code, message, null);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
* 统一响应模块
|
||||
* 包含统一响应结果封装
|
||||
*/
|
||||
package cn.yinlihupo.ylhpaiprojectmanager.common.result;
|
||||
77
src/main/java/cn/yinlihupo/common/util/ResultUtils.java
Normal file
77
src/main/java/cn/yinlihupo/common/util/ResultUtils.java
Normal file
@@ -0,0 +1,77 @@
|
||||
package cn.yinlihupo.common.util;
|
||||
|
||||
|
||||
import cn.yinlihupo.common.core.BaseResponse;
|
||||
import cn.yinlihupo.common.enums.ErrorCode;
|
||||
|
||||
/**
|
||||
* 返回工具类
|
||||
*
|
||||
*/
|
||||
public class ResultUtils {
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*
|
||||
* @param data
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> BaseResponse<T> success(T data) {
|
||||
return new BaseResponse<>(200, data, "ok");
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*
|
||||
* @param message 成功消息
|
||||
* @param data 数据
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> BaseResponse<T> success(String message, T data) {
|
||||
return new BaseResponse<>(200, data, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*
|
||||
* @param message 错误消息
|
||||
* @return
|
||||
*/
|
||||
public static BaseResponse error(String message) {
|
||||
return new BaseResponse(500, null, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*
|
||||
* @param errorCode
|
||||
* @return
|
||||
*/
|
||||
public static BaseResponse error(ErrorCode errorCode) {
|
||||
return new BaseResponse<>(errorCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*
|
||||
* @param code
|
||||
* @param message
|
||||
* @return
|
||||
*/
|
||||
public static BaseResponse error(int code, String message) {
|
||||
return new BaseResponse(code, null, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*
|
||||
* @param errorCode
|
||||
* @param message
|
||||
* @return
|
||||
*/
|
||||
public static BaseResponse error(ErrorCode errorCode, String message) {
|
||||
return new BaseResponse(errorCode.getCode(), null, message);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.yinlihupo.controller.oss;
|
||||
|
||||
import cn.yinlihupo.common.result.Result;
|
||||
import cn.yinlihupo.common.core.BaseResponse;
|
||||
import cn.yinlihupo.common.util.ResultUtils;
|
||||
import cn.yinlihupo.service.oss.OssService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -26,19 +27,19 @@ public class OssController {
|
||||
* @return 文件URL
|
||||
*/
|
||||
@PostMapping("/upload")
|
||||
public Result<String> uploadFile(@RequestParam("file") MultipartFile file) {
|
||||
public BaseResponse<String> uploadFile(@RequestParam("file") MultipartFile file) {
|
||||
log.info("收到文件上传请求, 文件名: {}", file.getOriginalFilename());
|
||||
|
||||
if (file.isEmpty()) {
|
||||
return Result.error("上传文件不能为空");
|
||||
return ResultUtils.error("上传文件不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
String fileUrl = ossService.uploadFile(file, file.getOriginalFilename());
|
||||
return Result.success("文件上传成功", fileUrl);
|
||||
return ResultUtils.success("文件上传成功", fileUrl);
|
||||
} catch (Exception e) {
|
||||
log.error("文件上传失败: {}", e.getMessage(), e);
|
||||
return Result.error("文件上传失败: " + e.getMessage());
|
||||
return ResultUtils.error("文件上传失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,21 +51,21 @@ public class OssController {
|
||||
* @return 文件URL
|
||||
*/
|
||||
@PostMapping("/upload/{bucketName}")
|
||||
public Result<String> uploadFileToBucket(
|
||||
public BaseResponse<String> uploadFileToBucket(
|
||||
@RequestParam("file") MultipartFile file,
|
||||
@PathVariable String bucketName) {
|
||||
log.info("收到文件上传请求, 文件名: {}, 存储桶: {}", file.getOriginalFilename(), bucketName);
|
||||
|
||||
if (file.isEmpty()) {
|
||||
return Result.error("上传文件不能为空");
|
||||
return ResultUtils.error("上传文件不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
String fileUrl = ossService.uploadFile(file, file.getOriginalFilename(), bucketName);
|
||||
return Result.success("文件上传成功", fileUrl);
|
||||
return ResultUtils.success("文件上传成功", fileUrl);
|
||||
} catch (Exception e) {
|
||||
log.error("文件上传失败: {}", e.getMessage(), e);
|
||||
return Result.error("文件上传失败: " + e.getMessage());
|
||||
return ResultUtils.error("文件上传失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,15 +76,15 @@ public class OssController {
|
||||
* @return 操作结果
|
||||
*/
|
||||
@DeleteMapping("/delete")
|
||||
public Result<Void> deleteFile(@RequestParam("fileUrl") String fileUrl) {
|
||||
public BaseResponse<Void> deleteFile(@RequestParam("fileUrl") String fileUrl) {
|
||||
log.info("收到文件删除请求, fileUrl: {}", fileUrl);
|
||||
|
||||
try {
|
||||
ossService.deleteFile(fileUrl);
|
||||
return Result.success("文件删除成功", null);
|
||||
return ResultUtils.success("文件删除成功", null);
|
||||
} catch (Exception e) {
|
||||
log.error("文件删除失败: {}", e.getMessage(), e);
|
||||
return Result.error("文件删除失败: " + e.getMessage());
|
||||
return ResultUtils.error("文件删除失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.yinlihupo.controller.project;
|
||||
|
||||
import cn.yinlihupo.common.result.Result;
|
||||
import cn.yinlihupo.common.core.BaseResponse;
|
||||
import cn.yinlihupo.common.util.ResultUtils;
|
||||
import cn.yinlihupo.domain.vo.ProjectInitResult;
|
||||
import cn.yinlihupo.service.oss.OssService;
|
||||
import cn.yinlihupo.service.project.ProjectService;
|
||||
@@ -29,20 +30,20 @@ public class ProjectController {
|
||||
* @return 项目初始化结构化数据
|
||||
*/
|
||||
@PostMapping("/from-file")
|
||||
public Result<ProjectInitResult> generateFromFile(@RequestParam("file") MultipartFile file) {
|
||||
public BaseResponse<ProjectInitResult> generateFromFile(@RequestParam("file") MultipartFile file) {
|
||||
log.info("收到项目初始化请求(文件上传), 文件名: {}", file.getOriginalFilename());
|
||||
|
||||
if (file.isEmpty()) {
|
||||
return Result.error("上传文件不能为空");
|
||||
return ResultUtils.error("上传文件不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
// 上传文件、生成项目初始化数据并保存到数据库
|
||||
ProjectInitResult result = projectService.generateAndSaveProject(file);
|
||||
return Result.success("项目初始化成功", result);
|
||||
return ResultUtils.success("项目初始化成功", result);
|
||||
} catch (Exception e) {
|
||||
log.error("项目初始化失败: {}", e.getMessage(), e);
|
||||
return Result.error("项目初始化失败: " + e.getMessage());
|
||||
return ResultUtils.error("项目初始化失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import java.util.List;
|
||||
@TableName("project")
|
||||
public class Project {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
package cn.yinlihupo.domain.entity;
|
||||
|
||||
import cn.yinlihupo.common.handler.JsonbTypeHandler;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 项目初始化记录实体类
|
||||
* 对应数据库表: project_init_record
|
||||
*/
|
||||
@Data
|
||||
@TableName("project_init_record")
|
||||
public class ProjectInitRecord {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 项目ID
|
||||
*/
|
||||
private Long projectId;
|
||||
|
||||
/**
|
||||
* 上传的文件列表[{name, path, type, size}]
|
||||
*/
|
||||
@TableField(typeHandler = JsonbTypeHandler.class)
|
||||
private List<InputFile> inputFiles;
|
||||
|
||||
/**
|
||||
* 用户输入的项目描述
|
||||
*/
|
||||
private String inputText;
|
||||
|
||||
/**
|
||||
* 状态: pending-待解析, processing-解析中, completed-已完成, failed-失败
|
||||
*/
|
||||
private String parseStatus;
|
||||
|
||||
/**
|
||||
* 解析结果(结构化数据)
|
||||
*/
|
||||
@TableField(typeHandler = JsonbTypeHandler.class)
|
||||
private Object parseResult;
|
||||
|
||||
/**
|
||||
* 生成的里程碑数量
|
||||
*/
|
||||
private Integer generatedMilestones;
|
||||
|
||||
/**
|
||||
* 生成的任务数量
|
||||
*/
|
||||
private Integer generatedTasks;
|
||||
|
||||
/**
|
||||
* 生成的成员数量
|
||||
*/
|
||||
private Integer generatedMembers;
|
||||
|
||||
/**
|
||||
* 生成的资源数量
|
||||
*/
|
||||
private Integer generatedResources;
|
||||
|
||||
/**
|
||||
* 使用的AI模型
|
||||
*/
|
||||
private String model;
|
||||
|
||||
/**
|
||||
* 消耗的Token数
|
||||
*/
|
||||
private Integer tokensUsed;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 输入文件信息内部类
|
||||
*/
|
||||
@Data
|
||||
public static class InputFile {
|
||||
private String name;
|
||||
private String path;
|
||||
private String type;
|
||||
private Long size;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import java.time.LocalDateTime;
|
||||
@TableName("project_member")
|
||||
public class ProjectMember {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.util.List;
|
||||
@TableName("project_milestone")
|
||||
public class ProjectMilestone {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.util.List;
|
||||
@TableName("project_timeline")
|
||||
public class ProjectTimeline {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,7 @@ import java.util.List;
|
||||
@TableName("resource")
|
||||
public class Resource {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,7 @@ import java.util.List;
|
||||
@TableName("risk")
|
||||
public class Risk {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,7 @@ import java.util.List;
|
||||
@TableName("task")
|
||||
public class Task {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package cn.yinlihupo.mapper;
|
||||
|
||||
import cn.yinlihupo.domain.entity.ProjectInitRecord;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 项目初始化记录Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface ProjectInitRecordMapper extends BaseMapper<ProjectInitRecord> {
|
||||
}
|
||||
@@ -3,8 +3,6 @@ package cn.yinlihupo.service.project;
|
||||
import cn.yinlihupo.domain.vo.ProjectInitResult;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AI项目初始化服务接口
|
||||
* 使用Spring AI结构化输出能力,从项目文档中提取结构化信息
|
||||
@@ -40,8 +38,7 @@ public interface ProjectService {
|
||||
* 根据项目资料内容生成并保存项目初始化数据
|
||||
*
|
||||
* @param content 项目资料文本内容
|
||||
* @param inputFiles 输入文件列表信息
|
||||
* @return 项目初始化结果
|
||||
*/
|
||||
ProjectInitResult generateAndSaveProjectFromContent(String content, List<cn.yinlihupo.domain.entity.ProjectInitRecord.InputFile> inputFiles);
|
||||
ProjectInitResult generateAndSaveProjectFromContent(String content);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ public class ProjectServiceImpl implements ProjectService {
|
||||
private final ResourceMapper resourceMapper;
|
||||
private final RiskMapper riskMapper;
|
||||
private final ProjectTimelineMapper projectTimelineMapper;
|
||||
private final ProjectInitRecordMapper projectInitRecordMapper;
|
||||
|
||||
/**
|
||||
* 项目初始化系统提示词模板
|
||||
@@ -214,17 +213,8 @@ public class ProjectServiceImpl implements ProjectService {
|
||||
throw new RuntimeException("无法读取文件内容: " + fileUrl);
|
||||
}
|
||||
|
||||
// 3. 构建输入文件信息
|
||||
List<ProjectInitRecord.InputFile> inputFiles = new ArrayList<>();
|
||||
ProjectInitRecord.InputFile inputFile = new ProjectInitRecord.InputFile();
|
||||
inputFile.setName(file.getOriginalFilename());
|
||||
inputFile.setPath(fileUrl);
|
||||
inputFile.setType(file.getContentType());
|
||||
inputFile.setSize(file.getSize());
|
||||
inputFiles.add(inputFile);
|
||||
|
||||
// 4. 生成并保存项目数据
|
||||
return generateAndSaveProjectFromContent(content, inputFiles);
|
||||
// 3. 生成并保存项目数据
|
||||
return generateAndSaveProjectFromContent(content);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("项目初始化失败: {}", e.getMessage(), e);
|
||||
@@ -234,22 +224,13 @@ public class ProjectServiceImpl implements ProjectService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ProjectInitResult generateAndSaveProjectFromContent(String content,
|
||||
List<ProjectInitRecord.InputFile> inputFiles) {
|
||||
public ProjectInitResult generateAndSaveProjectFromContent(String content) {
|
||||
log.info("开始生成并保存项目初始化数据");
|
||||
|
||||
// 1. 先创建初始化记录(状态为处理中)
|
||||
ProjectInitRecord initRecord = new ProjectInitRecord();
|
||||
initRecord.setInputFiles(inputFiles);
|
||||
initRecord.setInputText(content.length() > 5000 ? content.substring(0, 5000) + "..." : content);
|
||||
initRecord.setParseStatus("processing");
|
||||
projectInitRecordMapper.insert(initRecord);
|
||||
|
||||
ProjectInitResult result = null;
|
||||
Usage usage = null;
|
||||
ProjectInitResult result;
|
||||
|
||||
try {
|
||||
// 2. 调用AI生成项目数据
|
||||
// 1. 调用AI生成项目数据
|
||||
String userPrompt = "请根据以下项目资料,生成完整的项目初始化结构化数据:\n\n" +
|
||||
content + "\n\n" +
|
||||
"请严格按照系统提示词中的JSON格式输出,确保所有字段都包含合理的值。";
|
||||
@@ -266,38 +247,15 @@ public class ProjectServiceImpl implements ProjectService {
|
||||
// 使用 BeanOutputConverter 手动转换响应内容
|
||||
String responseContent = chatResponse.getResult().getOutput().getText();
|
||||
result = outputConverter.convert(responseContent);
|
||||
usage = chatResponse.getMetadata().getUsage();
|
||||
|
||||
// 3. 保存项目数据到数据库
|
||||
// 2. 保存项目数据到数据库
|
||||
Long projectId = saveProjectData(result);
|
||||
|
||||
// 4. 更新初始化记录为成功状态
|
||||
initRecord.setProjectId(projectId);
|
||||
initRecord.setParseStatus("completed");
|
||||
initRecord.setParseResult(result);
|
||||
initRecord.setGeneratedMilestones(result.getMilestones() != null ? result.getMilestones().size() : 0);
|
||||
initRecord.setGeneratedTasks(result.getTasks() != null ? result.getTasks().size() : 0);
|
||||
initRecord.setGeneratedMembers(result.getMembers() != null ? result.getMembers().size() : 0);
|
||||
initRecord.setGeneratedResources(result.getResources() != null ? result.getResources().size() : 0);
|
||||
if (usage != null) {
|
||||
initRecord.setTokensUsed(usage.getTotalTokens());
|
||||
}
|
||||
projectInitRecordMapper.updateById(initRecord);
|
||||
|
||||
log.info("项目初始化数据保存成功, projectId: {}", projectId);
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("项目初始化失败: {}", e.getMessage(), e);
|
||||
|
||||
// 更新初始化记录为失败状态
|
||||
initRecord.setParseStatus("failed");
|
||||
initRecord.setErrorMessage(e.getMessage());
|
||||
if (usage != null) {
|
||||
initRecord.setTokensUsed(usage.getTotalTokens());
|
||||
}
|
||||
projectInitRecordMapper.updateById(initRecord);
|
||||
|
||||
throw new RuntimeException("项目初始化失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user