feat(analysis): 新增日报及分析结果分页接口与数据聚合

- 添加DailyReportAnalysisController,包含获取建议及分页查询接口
- 创建DailyReportSuggestionService接口及实现类分页方法
- 实现分页查询项目日报及其对应分析记录和建议功能
- 将日报与最新分析记录及建议数据整合为DailyReportWithAnalysisVO返回
- 增加接口调用的权限校验和错误处理
- 新增ApplyDailyReportSuggestionsRequest请求体及对应逻辑处理
- 补充分页参数默认值及状态筛选逻辑
- 优化查询条件,按日期及创建时间倒序排序
This commit is contained in:
2026-04-01 15:04:38 +08:00
parent 3d9c1e524f
commit 9d1294d197
4 changed files with 191 additions and 0 deletions

View File

@@ -3,12 +3,14 @@ package cn.yinlihupo.controller.analysis;
import cn.yinlihupo.common.core.BaseResponse;
import cn.yinlihupo.common.enums.ErrorCode;
import cn.yinlihupo.common.exception.BusinessException;
import cn.yinlihupo.common.page.TableDataInfo;
import cn.yinlihupo.common.util.ResultUtils;
import cn.yinlihupo.common.util.SecurityUtils;
import cn.yinlihupo.domain.dto.ApplyDailyReportSuggestionsRequest;
import cn.yinlihupo.domain.entity.DailyReportUpdateSuggestion;
import cn.yinlihupo.domain.vo.DailyReportAnalysisSuggestionsVO;
import cn.yinlihupo.domain.vo.DailyReportUpdateSuggestionVO;
import cn.yinlihupo.domain.vo.DailyReportWithAnalysisVO;
import cn.yinlihupo.mapper.DailyReportUpdateSuggestionMapper;
import cn.yinlihupo.service.analysis.DailyReportSuggestionService;
import jakarta.validation.Valid;
@@ -57,6 +59,29 @@ public class DailyReportAnalysisController {
return ResultUtils.success("查询成功", vo);
}
/**
* 分页获取项目日报及分析结果
*/
@GetMapping("/reports")
public BaseResponse<TableDataInfo<DailyReportWithAnalysisVO>> pageDailyReportsWithAnalysis(
@RequestParam Long projectId,
@RequestParam(required = false) LocalDate reportDate,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String suggestionStatus) {
Long userId = SecurityUtils.getCurrentUserId();
if (userId == null) {
return ResultUtils.error(ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
}
try {
TableDataInfo<DailyReportWithAnalysisVO> result = dailyReportSuggestionService.pageDailyReportsWithAnalysis(projectId, reportDate, pageNum, pageSize, suggestionStatus);
return ResultUtils.success("查询成功", result);
} catch (Exception e) {
log.error("查询日报及分析结果失败: {}", e.getMessage(), e);
return ResultUtils.error("查询失败: " + e.getMessage());
}
}
/**
* 应用日报进度回写建议
*

View File

@@ -0,0 +1,42 @@
package cn.yinlihupo.domain.vo;
import cn.yinlihupo.domain.dto.DailyReportAnalysisResult;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
@Data
public class DailyReportWithAnalysisVO {
private Long reportId;
private Long projectId;
private LocalDate reportDate;
private String submitterUsername;
private Long submitterId;
private String workContent;
private String tomorrowPlan;
private Integer workIntensity;
private Boolean needHelp;
private String helpContent;
private LocalDateTime createTime;
private Long analysisId;
private String analysisStatus;
private DailyReportAnalysisResult analysisResult;
private List<DailyReportUpdateSuggestionVO> analysisSuggestions;
}

View File

@@ -2,6 +2,8 @@ package cn.yinlihupo.service.analysis;
import cn.yinlihupo.domain.vo.DailyReportAnalysisSuggestionsVO;
import cn.yinlihupo.domain.vo.DailyReportUpdateSuggestionVO;
import cn.yinlihupo.domain.vo.DailyReportWithAnalysisVO;
import cn.yinlihupo.common.page.TableDataInfo;
import java.time.LocalDate;
import java.util.List;
@@ -14,5 +16,7 @@ public interface DailyReportSuggestionService {
List<DailyReportUpdateSuggestionVO> listSuggestionsByProjectId(Long projectId, LocalDate reportDate, String status);
TableDataInfo<DailyReportWithAnalysisVO> pageDailyReportsWithAnalysis(Long projectId, LocalDate reportDate, Integer pageNum, Integer pageSize, String suggestionStatus);
int applySuggestions(Long projectId, List<Long> suggestionIds, Long appliedBy);
}

View File

@@ -2,6 +2,7 @@ package cn.yinlihupo.service.analysis.impl;
import cn.yinlihupo.common.enums.ErrorCode;
import cn.yinlihupo.common.exception.BusinessException;
import cn.yinlihupo.common.page.TableDataInfo;
import cn.yinlihupo.domain.dto.DailyReportAnalysisResult;
import cn.yinlihupo.domain.entity.DailyReportAnalysisRecord;
import cn.yinlihupo.domain.entity.DailyReportUpdateSuggestion;
@@ -10,12 +11,14 @@ import cn.yinlihupo.domain.entity.ProjectMilestone;
import cn.yinlihupo.domain.entity.Task;
import cn.yinlihupo.domain.vo.DailyReportAnalysisSuggestionsVO;
import cn.yinlihupo.domain.vo.DailyReportUpdateSuggestionVO;
import cn.yinlihupo.domain.vo.DailyReportWithAnalysisVO;
import cn.yinlihupo.mapper.DailyReportAnalysisRecordMapper;
import cn.yinlihupo.mapper.DailyReportUpdateSuggestionMapper;
import cn.yinlihupo.mapper.ProjectDailyReportMapper;
import cn.yinlihupo.mapper.ProjectMilestoneMapper;
import cn.yinlihupo.mapper.TaskMapper;
import cn.yinlihupo.service.analysis.DailyReportSuggestionService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
@@ -129,6 +132,123 @@ public class DailyReportSuggestionServiceImpl implements DailyReportSuggestionSe
return buildSuggestionVOList(projectId, suggestions);
}
@Override
public TableDataInfo<DailyReportWithAnalysisVO> pageDailyReportsWithAnalysis(Long projectId, LocalDate reportDate, Integer pageNum, Integer pageSize, String suggestionStatus) {
if (projectId == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "projectId不能为空");
}
int actualPageNum = pageNum == null || pageNum < 1 ? 1 : pageNum;
int actualPageSize = pageSize == null || pageSize < 1 ? 10 : pageSize;
String actualSuggestionStatus;
if (!StringUtils.hasText(suggestionStatus)) {
actualSuggestionStatus = "pending";
} else if ("all".equalsIgnoreCase(suggestionStatus)) {
actualSuggestionStatus = null;
} else {
actualSuggestionStatus = suggestionStatus;
}
Page<ProjectDailyReport> page = new Page<>(actualPageNum, actualPageSize);
LambdaQueryWrapper<ProjectDailyReport> reportWrapper = new LambdaQueryWrapper<ProjectDailyReport>()
.eq(ProjectDailyReport::getProjectId, projectId)
.eq(ProjectDailyReport::getDeleted, 0)
.eq(reportDate != null, ProjectDailyReport::getReportDate, reportDate)
.orderByDesc(ProjectDailyReport::getReportDate)
.orderByDesc(ProjectDailyReport::getCreateTime);
Page<ProjectDailyReport> reportPage = projectDailyReportMapper.selectPage(page, reportWrapper);
List<ProjectDailyReport> reports = reportPage.getRecords();
if (reports == null || reports.isEmpty()) {
return TableDataInfo.build(new Page<DailyReportWithAnalysisVO>(reportPage.getCurrent(), reportPage.getSize(), reportPage.getTotal())
.setRecords(new ArrayList<>()));
}
List<Long> reportIds = reports.stream()
.filter(Objects::nonNull)
.map(ProjectDailyReport::getId)
.filter(Objects::nonNull)
.toList();
Map<Long, DailyReportAnalysisRecord> latestAnalysisByReportId = new HashMap<>();
if (!reportIds.isEmpty()) {
List<DailyReportAnalysisRecord> analysisRecords = dailyReportAnalysisRecordMapper.selectList(
new LambdaQueryWrapper<DailyReportAnalysisRecord>()
.eq(DailyReportAnalysisRecord::getProjectId, projectId)
.in(DailyReportAnalysisRecord::getReportId, reportIds)
.eq(DailyReportAnalysisRecord::getDeleted, 0)
.orderByDesc(DailyReportAnalysisRecord::getCreateTime)
);
if (analysisRecords != null && !analysisRecords.isEmpty()) {
for (DailyReportAnalysisRecord record : analysisRecords) {
if (record == null || record.getReportId() == null) {
continue;
}
latestAnalysisByReportId.putIfAbsent(record.getReportId(), record);
}
}
}
List<Long> analysisIds = latestAnalysisByReportId.values().stream()
.filter(Objects::nonNull)
.map(DailyReportAnalysisRecord::getId)
.filter(Objects::nonNull)
.distinct()
.toList();
Map<Long, List<DailyReportUpdateSuggestionVO>> suggestionsByAnalysisId = Collections.emptyMap();
if (!analysisIds.isEmpty()) {
List<DailyReportUpdateSuggestion> suggestions = dailyReportUpdateSuggestionMapper.selectList(
new LambdaQueryWrapper<DailyReportUpdateSuggestion>()
.eq(DailyReportUpdateSuggestion::getProjectId, projectId)
.eq(DailyReportUpdateSuggestion::getDeleted, 0)
.eq(StringUtils.hasText(actualSuggestionStatus), DailyReportUpdateSuggestion::getStatus, actualSuggestionStatus)
.in(DailyReportUpdateSuggestion::getAnalysisId, analysisIds)
.orderByAsc(DailyReportUpdateSuggestion::getCreateTime)
);
List<DailyReportUpdateSuggestionVO> suggestionVOs = buildSuggestionVOList(projectId, suggestions);
suggestionsByAnalysisId = suggestionVOs.stream()
.filter(vo -> vo != null && vo.getAnalysisId() != null)
.collect(Collectors.groupingBy(DailyReportUpdateSuggestionVO::getAnalysisId));
}
List<DailyReportWithAnalysisVO> voList = new ArrayList<>();
for (ProjectDailyReport report : reports) {
if (report == null) {
continue;
}
DailyReportWithAnalysisVO vo = new DailyReportWithAnalysisVO();
vo.setReportId(report.getId());
vo.setProjectId(report.getProjectId());
vo.setReportDate(report.getReportDate());
vo.setSubmitterUsername(report.getSubmitterUsername());
vo.setSubmitterId(report.getSubmitterId());
vo.setWorkContent(report.getWorkContent());
vo.setTomorrowPlan(report.getTomorrowPlan());
vo.setWorkIntensity(report.getWorkIntensity());
vo.setNeedHelp(report.getNeedHelp());
vo.setHelpContent(report.getHelpContent());
vo.setCreateTime(report.getCreateTime());
DailyReportAnalysisRecord record = report.getId() != null ? latestAnalysisByReportId.get(report.getId()) : null;
if (record != null) {
vo.setAnalysisId(record.getId());
vo.setAnalysisStatus(record.getStatus());
if (record.getAnalysisResult() != null) {
DailyReportAnalysisResult analysisResult = OBJECT_MAPPER.convertValue(record.getAnalysisResult(), DailyReportAnalysisResult.class);
vo.setAnalysisResult(analysisResult);
}
vo.setAnalysisSuggestions(record.getId() != null ? suggestionsByAnalysisId.getOrDefault(record.getId(), List.of()) : List.of());
} else {
vo.setAnalysisSuggestions(List.of());
}
voList.add(vo);
}
return TableDataInfo.build(new Page<DailyReportWithAnalysisVO>(reportPage.getCurrent(), reportPage.getSize(), reportPage.getTotal())
.setRecords(voList));
}
@Override
@Transactional(rollbackFor = Exception.class)
public int applySuggestions(Long projectId, List<Long> suggestionIds, Long appliedBy) {