feat(报告):AI 聚合残片生成报告草稿

- AiServiceClient:调用 lpt-ai 独立服务,未配置/失败时返回 empty
- GET /study-sessions/{sessionNum}/report-draft:AI 聚合(含预期对比),降级为残片拼接
- 草稿仅作为编辑起点,最终报告仍由用户确认后保存
- application.yml 增加 lpt.ai-service.url 配置
This commit is contained in:
2026-07-03 23:52:32 +08:00
parent 3c4db2fead
commit 21c5b8f761
4 changed files with 134 additions and 1 deletions
@@ -16,6 +16,7 @@ import com.guo.learningprogresstracker.mapStruct.StudySessionConvert;
import com.guo.learningprogresstracker.mapper.StudyReportFragmentsMapper;
import com.guo.learningprogresstracker.mapper.StudyReportsMapper;
import com.guo.learningprogresstracker.mapper.StudySessionsMapper;
import com.guo.learningprogresstracker.service.StudyExpectationsService;
import com.guo.learningprogresstracker.service.StudySessionsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -41,6 +42,8 @@ public class StudySessionsServiceImpl extends ServiceImpl<StudySessionsMapper, S
private final StudyReportFragmentsServiceImpl studyReportFragmentsServiceImpl;
private final StudyReportFragmentsMapper studyReportFragmentsMapper;
private final StudyReportsMapper studyReportsMapper;
private final AiServiceClient aiServiceClient;
private final StudyExpectationsService studyExpectationsService;
public StudySessionResponse startOrContinueStudySession(String taskNum) throws NotFindEntitiesException, ServiceException {
TaskEntity taskEntity = tasksServiceImpl.getOneOpt(
@@ -158,4 +161,30 @@ public class StudySessionsServiceImpl extends ServiceImpl<StudySessionsMapper, S
return StudySessionConvert.MAPPER.toStudySessionResponse(session);
}
/**
* 生成学习报告草稿:优先调用 lpt-ai 聚合残片,服务不可用时降级为按序拼接。
* 草稿仅作为编辑起点返回,不落库——最终报告由用户确认后经 endedStudySession 保存。
*/
public String generateReportDraft(String sessionNum) throws ErrorParameterException {
StudySessionsEntity session = this.getOneOpt(Wrappers.lambdaQuery(StudySessionsEntity.class)
.eq(StudySessionsEntity::getSessionNum, sessionNum))
.orElseThrow(() -> new ErrorParameterException("会话[" + sessionNum + "]不存在"));
ArrayList<String> fragments = getAllFragments(sessionNum);
if (fragments.isEmpty()) {
return "";
}
String taskName = tasksServiceImpl.getOneOpt(
Wrappers.lambdaQuery(TaskEntity.class).eq(TaskEntity::getTaskNum, session.getTaskNum()))
.map(TaskEntity::getTaskName)
.orElse("学习任务");
String expectation = Optional.ofNullable(studyExpectationsService.getBySessionNum(sessionNum))
.map(e -> e.getDescription())
.orElse(null);
return aiServiceClient.aggregateReport(taskName, fragments, expectation)
.orElseGet(() -> String.join("\n", fragments));
}
}