feat(日报分析): 新增项目维度的建议查询接口

- 在 DailyReportUpdateSuggestionVO 中添加 analysisId、reportId 等字段以支持项目维度查询
- 在 DailyReportSuggestionService 中新增 listSuggestionsByProjectId 方法
- 重构 DailyReportAnalysisController,将原建议查询接口拆分为项目维度和日报维度两个接口
- 在 DailyReportSuggestionServiceImpl 中实现项目维度建议查询逻辑,支持按日期和状态过滤
- 新增 buildSuggestionVOList 方法用于构建建议列表的视图对象,包含关联的任务和里程碑信息
This commit is contained in:
2026-04-01 11:28:42 +08:00
parent 3bca27254b
commit c8c95133d6
4 changed files with 164 additions and 18 deletions

View File

@@ -8,6 +8,7 @@ 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.mapper.DailyReportUpdateSuggestionMapper;
import cn.yinlihupo.service.analysis.DailyReportSuggestionService;
import jakarta.validation.Valid;
@@ -34,26 +35,25 @@ public class DailyReportAnalysisController {
private final DailyReportUpdateSuggestionMapper dailyReportUpdateSuggestionMapper;
/**
* 获取日报进度更新建议
*
* @param projectId 项目ID
* @param reportId 日报ID
* @param reportDate 日报日期
* @param submitterUsername 日报提交人用户名
* 根据项目ID获取日报进度更新建议(项目维度)
*/
@GetMapping("/suggestions")
public BaseResponse<DailyReportAnalysisSuggestionsVO> getSuggestions(
public BaseResponse<List<DailyReportUpdateSuggestionVO>> getSuggestions(
@RequestParam Long projectId,
@RequestParam(required = false) Long reportId,
@RequestParam(required = false) LocalDate reportDate,
@RequestParam(required = false) String submitterUsername) {
@RequestParam(required = false) String status) {
List<DailyReportUpdateSuggestionVO> list = dailyReportSuggestionService.listSuggestionsByProjectId(projectId, reportDate, status);
return ResultUtils.success("查询成功", list);
}
DailyReportAnalysisSuggestionsVO vo;
if (reportId != null) {
vo = dailyReportSuggestionService.getLatestSuggestionsByReportId(projectId, reportId);
} else {
vo = dailyReportSuggestionService.getLatestSuggestionsByUniqueKey(projectId, reportDate, submitterUsername);
}
/**
* 获取指定日报的进度更新建议(日报维度)
*/
@GetMapping("/suggestions/report")
public BaseResponse<DailyReportAnalysisSuggestionsVO> getReportSuggestions(
@RequestParam Long projectId,
@RequestParam Long reportId) {
DailyReportAnalysisSuggestionsVO vo = dailyReportSuggestionService.getLatestSuggestionsByReportId(projectId, reportId);
return ResultUtils.success("查询成功", vo);
}
@@ -94,4 +94,3 @@ public class DailyReportAnalysisController {
return ResultUtils.success("应用成功", applied);
}
}

View File

@@ -3,12 +3,21 @@ package cn.yinlihupo.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
@Data
public class DailyReportUpdateSuggestionVO {
private Long suggestionId;
private Long analysisId;
private Long reportId;
private LocalDate reportDate;
private String submitterUsername;
private String targetType;
private Long targetId;
@@ -29,4 +38,3 @@ public class DailyReportUpdateSuggestionVO {
private String status;
}

View File

@@ -1,6 +1,7 @@
package cn.yinlihupo.service.analysis;
import cn.yinlihupo.domain.vo.DailyReportAnalysisSuggestionsVO;
import cn.yinlihupo.domain.vo.DailyReportUpdateSuggestionVO;
import java.time.LocalDate;
import java.util.List;
@@ -11,6 +12,7 @@ public interface DailyReportSuggestionService {
DailyReportAnalysisSuggestionsVO getLatestSuggestionsByUniqueKey(Long projectId, LocalDate reportDate, String submitterUsername);
List<DailyReportUpdateSuggestionVO> listSuggestionsByProjectId(Long projectId, LocalDate reportDate, String status);
int applySuggestions(Long projectId, List<Long> suggestionIds, Long appliedBy);
}

View File

@@ -86,6 +86,49 @@ public class DailyReportSuggestionServiceImpl implements DailyReportSuggestionSe
return getLatestSuggestionsByReportId(projectId, report.getId());
}
@Override
public List<DailyReportUpdateSuggestionVO> listSuggestionsByProjectId(Long projectId, LocalDate reportDate, String status) {
if (projectId == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "projectId不能为空");
}
String actualStatus = StringUtils.hasText(status) ? status : "pending";
List<DailyReportUpdateSuggestion> suggestions;
if (reportDate != null) {
List<DailyReportAnalysisRecord> records = dailyReportAnalysisRecordMapper.selectList(
new LambdaQueryWrapper<DailyReportAnalysisRecord>()
.eq(DailyReportAnalysisRecord::getProjectId, projectId)
.eq(DailyReportAnalysisRecord::getReportDate, reportDate)
.eq(DailyReportAnalysisRecord::getDeleted, 0)
.orderByDesc(DailyReportAnalysisRecord::getCreateTime)
.last("LIMIT 200")
);
if (records == null || records.isEmpty()) {
return List.of();
}
List<Long> analysisIds = records.stream().map(DailyReportAnalysisRecord::getId).toList();
suggestions = dailyReportUpdateSuggestionMapper.selectList(
new LambdaQueryWrapper<DailyReportUpdateSuggestion>()
.eq(DailyReportUpdateSuggestion::getProjectId, projectId)
.eq(DailyReportUpdateSuggestion::getDeleted, 0)
.eq(DailyReportUpdateSuggestion::getStatus, actualStatus)
.in(DailyReportUpdateSuggestion::getAnalysisId, analysisIds)
.orderByDesc(DailyReportUpdateSuggestion::getCreateTime)
);
} else {
suggestions = dailyReportUpdateSuggestionMapper.selectList(
new LambdaQueryWrapper<DailyReportUpdateSuggestion>()
.eq(DailyReportUpdateSuggestion::getProjectId, projectId)
.eq(DailyReportUpdateSuggestion::getDeleted, 0)
.eq(DailyReportUpdateSuggestion::getStatus, actualStatus)
.orderByDesc(DailyReportUpdateSuggestion::getCreateTime)
);
}
return buildSuggestionVOList(projectId, suggestions);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int applySuggestions(Long projectId, List<Long> suggestionIds, Long appliedBy) {
@@ -305,4 +348,98 @@ public class DailyReportSuggestionServiceImpl implements DailyReportSuggestionSe
vo.setSuggestions(voList);
return vo;
}
private List<DailyReportUpdateSuggestionVO> buildSuggestionVOList(Long projectId, List<DailyReportUpdateSuggestion> suggestions) {
if (suggestions == null || suggestions.isEmpty()) {
return List.of();
}
List<Long> analysisIds = suggestions.stream()
.filter(Objects::nonNull)
.map(DailyReportUpdateSuggestion::getAnalysisId)
.filter(Objects::nonNull)
.distinct()
.toList();
Map<Long, DailyReportAnalysisRecord> recordMap = analysisIds.isEmpty()
? Collections.emptyMap()
: dailyReportAnalysisRecordMapper.selectBatchIds(analysisIds).stream()
.filter(r -> r != null && r.getDeleted() != null && r.getDeleted() == 0)
.collect(Collectors.toMap(DailyReportAnalysisRecord::getId, Function.identity(), (a, b) -> a));
List<Long> taskIds = new ArrayList<>();
List<Long> milestoneIds = new ArrayList<>();
for (DailyReportUpdateSuggestion suggestion : suggestions) {
if (suggestion == null || suggestion.getTargetId() == null) {
continue;
}
if ("task".equalsIgnoreCase(suggestion.getTargetType())) {
taskIds.add(suggestion.getTargetId());
} else if ("milestone".equalsIgnoreCase(suggestion.getTargetType())) {
milestoneIds.add(suggestion.getTargetId());
}
}
Map<Long, Task> taskMap = taskIds.isEmpty()
? Collections.emptyMap()
: taskMapper.selectBatchIds(taskIds).stream()
.filter(Objects::nonNull)
.filter(t -> t.getDeleted() != null && t.getDeleted() == 0)
.collect(Collectors.toMap(Task::getId, Function.identity(), (a, b) -> a));
Map<Long, ProjectMilestone> milestoneMap = milestoneIds.isEmpty()
? Collections.emptyMap()
: projectMilestoneMapper.selectBatchIds(milestoneIds).stream()
.filter(Objects::nonNull)
.filter(m -> m.getDeleted() != null && m.getDeleted() == 0)
.collect(Collectors.toMap(ProjectMilestone::getId, Function.identity(), (a, b) -> a));
List<DailyReportUpdateSuggestionVO> voList = new ArrayList<>();
for (DailyReportUpdateSuggestion suggestion : suggestions) {
if (suggestion == null) {
continue;
}
if (!Objects.equals(projectId, suggestion.getProjectId())) {
continue;
}
DailyReportUpdateSuggestionVO vo = new DailyReportUpdateSuggestionVO();
vo.setSuggestionId(suggestion.getId());
vo.setAnalysisId(suggestion.getAnalysisId());
vo.setReportId(suggestion.getReportId());
vo.setTargetType(suggestion.getTargetType());
vo.setTargetId(suggestion.getTargetId());
vo.setSuggestedStatus(suggestion.getSuggestedStatus());
vo.setSuggestedProgress(suggestion.getSuggestedProgress());
vo.setReason(suggestion.getReason());
vo.setConfidence(suggestion.getConfidence());
vo.setStatus(suggestion.getStatus());
DailyReportAnalysisRecord record = suggestion.getAnalysisId() != null ? recordMap.get(suggestion.getAnalysisId()) : null;
if (record != null) {
vo.setReportDate(record.getReportDate());
vo.setSubmitterUsername(record.getSubmitterUsername());
}
if ("task".equalsIgnoreCase(suggestion.getTargetType())) {
Task task = taskMap.get(suggestion.getTargetId());
if (task != null) {
vo.setTargetName(task.getTaskName());
vo.setCurrentStatus(task.getStatus());
vo.setCurrentProgress(task.getProgress());
}
} else if ("milestone".equalsIgnoreCase(suggestion.getTargetType())) {
ProjectMilestone milestone = milestoneMap.get(suggestion.getTargetId());
if (milestone != null) {
vo.setTargetName(milestone.getMilestoneName());
vo.setCurrentStatus(milestone.getStatus());
vo.setCurrentProgress(milestone.getProgress());
}
}
voList.add(vo);
}
return voList;
}
}