feat: new api

This commit is contained in:
2025-12-10 18:05:25 +08:00
parent f9eaa441a3
commit ab86729e33
11 changed files with 753 additions and 25 deletions

View File

@@ -13,6 +13,8 @@ import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.inspection.domain.bo.ArExecutionBo;
import org.dromara.inspection.domain.bo.ArExecutionSubmitBo;
import org.dromara.inspection.domain.vo.ArExecutionDetailVo;
import org.dromara.inspection.domain.vo.ArExecutionVo;
import org.dromara.inspection.service.IArExecutionService;
import lombok.RequiredArgsConstructor;
@@ -61,15 +63,15 @@ public class ArExecutionController extends BaseController {
}
/**
* 获取任务执行记录详细信息
* 获取任务执行记录详细信息(包含关联对象和步骤树)
*
* @param id 执行ID
*/
@SaCheckPermission("inspection:execution:query")
@GetMapping("/{id}")
public R<ArExecutionVo> getInfo(@NotNull(message = "执行ID不能为空")
@PathVariable("id") Long id) {
return R.ok(arExecutionService.queryById(id));
public R<ArExecutionDetailVo> getInfo(@NotNull(message = "执行ID不能为空")
@PathVariable("id") Long id) {
return R.ok(arExecutionService.queryDetailById(id));
}
/**
@@ -94,6 +96,17 @@ public class ArExecutionController extends BaseController {
return toAjax(arExecutionService.updateByBo(bo));
}
/**
* 批量提交任务执行结果
*/
@SaCheckPermission("inspection:execution:submit")
@Log(title = "提交任务执行结果", businessType = BusinessType.UPDATE)
@RepeatSubmit
@PostMapping("/submit")
public R<Void> submit(@Validated @RequestBody ArExecutionSubmitBo bo) {
return toAjax(arExecutionService.submitExecution(bo));
}
/**
* 删除任务执行记录
*

View File

@@ -1,6 +1,7 @@
package org.dromara.inspection.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaIgnore;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
@@ -40,9 +41,12 @@ public class ArStepRecordController extends BaseController {
private final IArStepRecordService arStepRecordService;
/**
/**
* 查询步骤执行记录列表
*/
@SaIgnore
@SaCheckPermission("inspection:stepRecord:list")
@GetMapping("/list")
public TableDataInfo<ArStepRecordVo> list(@Validated(QueryGroup.class) ArStepRecordBo bo, PageQuery pageQuery) {
@@ -67,6 +71,7 @@ public class ArStepRecordController extends BaseController {
*/
@SaCheckPermission("inspection:stepRecord:query")
@GetMapping("/{id}")
@SaIgnore
public R<ArStepRecordVo> getInfo(@NotNull(message = "记录ID不能为空")
@PathVariable("id") Long id) {
return R.ok(arStepRecordService.queryById(id));
@@ -90,6 +95,7 @@ public class ArStepRecordController extends BaseController {
@Log(title = "步骤执行记录", businessType = BusinessType.UPDATE)
@RepeatSubmit
@PutMapping()
@SaIgnore
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ArStepRecordBo bo) {
return toAjax(arStepRecordService.updateByBo(bo));
}

View File

@@ -0,0 +1,51 @@
package org.dromara.inspection.domain.bo;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 任务执行提交业务对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class ArExecutionSubmitBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 执行ID
*/
@NotNull(message = "执行ID不能为空")
private Long id;
/**
* 状态(pending/in_progress/completed/cancelled)
*/
private String status;
/**
* 开始时间
*/
private Date startTime;
/**
* 结束时间
*/
private Date endTime;
/**
* 步骤执行树
*/
@Valid
private List<ArStepRecordTreeBo> stepTree;
}

View File

@@ -0,0 +1,89 @@
package org.dromara.inspection.domain.bo;
import jakarta.validation.Valid;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 步骤执行记录树形业务对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class ArStepRecordTreeBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 步骤ID仅用于引用
*/
private Long id;
/**
* 执行记录ID用于更新现有记录
*/
private Long recordId;
/**
* 执行状态(pending/completed/skipped)
*/
private String recordStatus;
/**
* 是否完成(0否 1是)
*/
private String isDone;
/**
* 开始时间
*/
private Date startTime;
/**
* 完成时间
*/
private Date completionTime;
/**
* 耗时(秒)
*/
private Integer duration;
/**
* 文本反馈
*/
private String textFeedback;
/**
* 语音识别文本
*/
private String voiceText;
/**
* AI识别结果(JSON)
*/
private String aiResult;
/**
* 执行人ID
*/
private Long executorId;
/**
* 执行人姓名
*/
private String executorName;
/**
* 子步骤列表
*/
@Valid
private List<ArStepRecordTreeBo> children;
}

View File

@@ -0,0 +1,46 @@
package org.dromara.inspection.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 任务执行详情视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class ArExecutionDetailVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 执行基本信息
*/
private ArExecutionVo execution;
/**
* 任务模板信息
*/
private ArTaskVo task;
/**
* 区域信息
*/
private ArRegionVo region;
/**
* 设备信息
*/
private ArDeviceVo device;
/**
* 步骤执行树
*/
private List<ArStepRecordTreeVo> stepTree;
}

View File

@@ -0,0 +1,83 @@
package org.dromara.inspection.domain.vo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.util.Date;
import java.util.List;
/**
* 步骤执行记录树形视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ArStepRecordTreeVo extends ArStepTreeVo {
@Serial
private static final long serialVersionUID = 1L;
/**
* 执行记录ID
*/
private Long recordId;
/**
* 执行状态(pending/completed/skipped)
*/
private String recordStatus;
/**
* 是否完成(0否 1是)
*/
private String isDone;
/**
* 开始时间
*/
private Date startTime;
/**
* 完成时间
*/
private Date completionTime;
/**
* 耗时(秒)
*/
private Integer duration;
/**
* 文本反馈
*/
private String textFeedback;
/**
* 语音识别文本
*/
private String voiceText;
/**
* AI识别结果(JSON)
*/
private String aiResult;
/**
* 执行人ID
*/
private Long executorId;
/**
* 执行人姓名
*/
private String executorName;
/**
* 子步骤列表
*/
private List<ArStepRecordTreeVo> children;
}

View File

@@ -3,6 +3,8 @@ package org.dromara.inspection.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.inspection.domain.bo.ArExecutionBo;
import org.dromara.inspection.domain.bo.ArExecutionSubmitBo;
import org.dromara.inspection.domain.vo.ArExecutionDetailVo;
import org.dromara.inspection.domain.vo.ArExecutionVo;
import java.util.Collection;
@@ -23,6 +25,22 @@ public interface IArExecutionService {
*/
ArExecutionVo queryById(Long id);
/**
* 查询任务执行详情(包含关联对象和步骤树)
*
* @param id 执行ID
* @return 任务执行详情VO
*/
ArExecutionDetailVo queryDetailById(Long id);
/**
* 批量提交任务执行结果
*
* @param bo 提交业务对象
* @return 是否成功
*/
Boolean submitExecution(ArExecutionSubmitBo bo);
/**
* 查询列表
*/

View File

@@ -10,16 +10,21 @@ import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.inspection.domain.ArExecution;
import org.dromara.inspection.domain.ArStep;
import org.dromara.inspection.domain.ArStepRecord;
import org.dromara.inspection.domain.bo.ArExecutionBo;
import org.dromara.inspection.domain.vo.ArExecutionVo;
import org.dromara.inspection.mapper.ArExecutionMapper;
import org.dromara.inspection.domain.bo.ArExecutionSubmitBo;
import org.dromara.inspection.domain.bo.ArStepRecordTreeBo;
import org.dromara.inspection.domain.vo.*;
import org.dromara.inspection.mapper.*;
import org.dromara.inspection.service.IArExecutionService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lombok.AllArgsConstructor;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
* 任务执行记录Service业务层处理
@@ -32,6 +37,11 @@ import java.util.Map;
public class ArExecutionServiceImpl implements IArExecutionService {
private final ArExecutionMapper baseMapper;
private final ArTaskMapper taskMapper;
private final ArRegionMapper regionMapper;
private final ArDeviceMapper deviceMapper;
private final ArStepMapper stepMapper;
private final ArStepRecordMapper stepRecordMapper;
@Override
public ArExecutionVo queryById(Long id) {
@@ -141,4 +151,253 @@ public class ArExecutionServiceImpl implements IArExecutionService {
}
return baseMapper.deleteByIds(ids) > 0;
}
@Override
public ArExecutionDetailVo queryDetailById(Long id) {
// 1. 查询执行记录
ArExecutionVo execution = baseMapper.selectVoById(id);
if (execution == null) {
throw new ServiceException("任务执行记录不存在");
}
// 2. 查询关联对象
ArTaskVo task = execution.getTaskId() != null
? taskMapper.selectVoById(execution.getTaskId()) : null;
ArRegionVo region = execution.getRegionId() != null
? regionMapper.selectVoById(execution.getRegionId()) : null;
ArDeviceVo device = execution.getDeviceId() != null
? deviceMapper.selectVoById(execution.getDeviceId()) : null;
// 3. 查询步骤模板
List<ArStepVo> allSteps = new ArrayList<>();
if (execution.getTaskId() != null) {
LambdaQueryWrapper<ArStep> stepWrapper = Wrappers.lambdaQuery();
stepWrapper.eq(ArStep::getTaskId, execution.getTaskId());
stepWrapper.orderByAsc(ArStep::getOrderNum);
allSteps = stepMapper.selectVoList(stepWrapper);
}
// 4. 查询执行记录
LambdaQueryWrapper<ArStepRecord> recordWrapper = Wrappers.lambdaQuery();
recordWrapper.eq(ArStepRecord::getExecutionId, id);
List<ArStepRecordVo> allRecords = stepRecordMapper.selectVoList(recordWrapper);
// 5. 构建步骤记录 Map
Map<Long, ArStepRecordVo> recordMap = allRecords.stream()
.collect(Collectors.toMap(ArStepRecordVo::getStepId, r -> r, (r1, r2) -> r1));
// 6. 构建步骤树
List<ArStepRecordTreeVo> stepTree = buildStepRecordTree(allSteps, recordMap, 0L);
// 7. 组装返回对象
ArExecutionDetailVo detail = new ArExecutionDetailVo();
detail.setExecution(execution);
detail.setTask(task);
detail.setRegion(region);
detail.setDevice(device);
detail.setStepTree(stepTree);
return detail;
}
/**
* 递归构建步骤执行记录树
*
* @param allSteps 所有步骤模板
* @param recordMap 执行记录Map (stepId -> record)
* @param parentId 父步骤ID
* @return 树形步骤执行记录列表
*/
private List<ArStepRecordTreeVo> buildStepRecordTree(
List<ArStepVo> allSteps,
Map<Long, ArStepRecordVo> recordMap,
Long parentId) {
List<ArStepRecordTreeVo> tree = new ArrayList<>();
for (ArStepVo step : allSteps) {
if (step.getParentId().equals(parentId)) {
ArStepRecordTreeVo treeNode = new ArStepRecordTreeVo();
// 复制步骤模板信息
BeanUtils.copyProperties(step, treeNode);
// 合并执行记录信息
ArStepRecordVo record = recordMap.get(step.getId());
if (record != null) {
treeNode.setRecordId(record.getId());
treeNode.setRecordStatus(record.getStatus());
treeNode.setIsDone(record.getIsDone());
treeNode.setStartTime(record.getStartTime());
treeNode.setCompletionTime(record.getCompletionTime());
treeNode.setDuration(record.getDuration());
treeNode.setTextFeedback(record.getTextFeedback());
treeNode.setVoiceText(record.getVoiceText());
treeNode.setAiResult(record.getAiResult());
treeNode.setExecutorId(record.getExecutorId());
treeNode.setExecutorName(record.getExecutorName());
}
// 递归查找子节点
List<ArStepRecordTreeVo> children = buildStepRecordTree(allSteps, recordMap, step.getId());
if (!children.isEmpty()) {
treeNode.setChildren(children);
}
tree.add(treeNode);
}
}
return tree;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean submitExecution(ArExecutionSubmitBo bo) {
// 1. 校验执行记录是否存在
ArExecution execution = baseMapper.selectById(bo.getId());
if (execution == null) {
throw new ServiceException("任务执行记录不存在");
}
// 2. 收集所有需要更新的步骤记录
List<ArStepRecord> recordsToUpdate = new ArrayList<>();
if (bo.getStepTree() != null && !bo.getStepTree().isEmpty()) {
collectRecordsToUpdate(bo.getStepTree(), bo.getId(), recordsToUpdate);
}
// 3. 批量更新步骤记录
if (!recordsToUpdate.isEmpty()) {
boolean updateSuccess = stepRecordMapper.updateBatchById(recordsToUpdate);
if (!updateSuccess) {
throw new ServiceException("步骤记录更新失败");
}
}
// 4. 计算总步骤数和已完成步骤数
StepCountResult countResult = countSteps(bo.getStepTree());
// 5. 更新执行记录
ArExecution updateExecution = new ArExecution();
updateExecution.setId(bo.getId());
// 设置状态
if (StringUtils.isNotBlank(bo.getStatus())) {
updateExecution.setStatus(bo.getStatus());
}
// 设置时间
if (bo.getStartTime() != null) {
updateExecution.setStartTime(bo.getStartTime());
}
if (bo.getEndTime() != null) {
updateExecution.setEndTime(bo.getEndTime());
}
// 自动设置开始时间
if ("in_progress".equals(updateExecution.getStatus()) && execution.getStartTime() == null) {
updateExecution.setStartTime(new Date());
}
// 自动设置结束时间
if (("completed".equals(updateExecution.getStatus()) || "cancelled".equals(updateExecution.getStatus()))
&& execution.getEndTime() == null) {
updateExecution.setEndTime(new Date());
}
// 设置步骤统计
updateExecution.setTotalSteps(countResult.getTotal());
updateExecution.setCompletedSteps(countResult.getCompleted());
return baseMapper.updateById(updateExecution) > 0;
}
/**
* 递归收集所有需要更新的步骤记录
*
* @param stepTree 步骤树
* @param executionId 执行ID
* @param result 结果列表
*/
private void collectRecordsToUpdate(List<ArStepRecordTreeBo> stepTree, Long executionId, List<ArStepRecord> result) {
if (stepTree == null || stepTree.isEmpty()) {
return;
}
for (ArStepRecordTreeBo stepBo : stepTree) {
// 只更新有 recordId 的记录
if (stepBo.getRecordId() != null) {
ArStepRecord record = new ArStepRecord();
record.setId(stepBo.getRecordId());
record.setExecutionId(executionId);
record.setStepId(stepBo.getId());
record.setStatus(stepBo.getRecordStatus());
record.setIsDone(stepBo.getIsDone());
record.setStartTime(stepBo.getStartTime());
record.setCompletionTime(stepBo.getCompletionTime());
record.setTextFeedback(stepBo.getTextFeedback());
record.setVoiceText(stepBo.getVoiceText());
record.setAiResult(stepBo.getAiResult());
record.setExecutorId(stepBo.getExecutorId());
record.setExecutorName(stepBo.getExecutorName());
// 自动计算耗时
if (stepBo.getStartTime() != null && stepBo.getCompletionTime() != null) {
long durationMillis = stepBo.getCompletionTime().getTime() - stepBo.getStartTime().getTime();
record.setDuration((int) (durationMillis / 1000));
} else if (stepBo.getDuration() != null) {
record.setDuration(stepBo.getDuration());
}
result.add(record);
}
// 递归处理子步骤
if (stepBo.getChildren() != null && !stepBo.getChildren().isEmpty()) {
collectRecordsToUpdate(stepBo.getChildren(), executionId, result);
}
}
}
/**
* 递归统计步骤数
*
* @param stepTree 步骤树
* @return 统计结果
*/
private StepCountResult countSteps(List<ArStepRecordTreeBo> stepTree) {
int totalCount = 0;
int completedCount = 0;
if (stepTree == null || stepTree.isEmpty()) {
return new StepCountResult(totalCount, completedCount);
}
for (ArStepRecordTreeBo stepBo : stepTree) {
// 只统计有 recordId 的步骤
if (stepBo.getRecordId() != null) {
totalCount++;
if ("1".equals(stepBo.getIsDone()) || "completed".equals(stepBo.getRecordStatus())) {
completedCount++;
}
}
// 递归统计子步骤
if (stepBo.getChildren() != null && !stepBo.getChildren().isEmpty()) {
StepCountResult childCounts = countSteps(stepBo.getChildren());
totalCount += childCounts.getTotal();
completedCount += childCounts.getCompleted();
}
}
return new StepCountResult(totalCount, completedCount);
}
/**
* 步骤统计结果内部类
*/
@lombok.Data
@AllArgsConstructor
private static class StepCountResult {
private int total;
private int completed;
}
}