Files
lablesys_backend/src/main/java/com/labelsys/backend/service/AnnotationTaskService.java

274 lines
13 KiB
Java
Raw Normal View History

2026-04-27 10:27:57 +08:00
package com.labelsys.backend.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
2026-04-27 16:25:39 +08:00
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
2026-04-27 10:27:57 +08:00
import com.labelsys.backend.common.ResultCode;
import com.labelsys.backend.common.exception.BusinessException;
import com.labelsys.backend.context.LoginUser;
import com.labelsys.backend.dto.common.PageResult;
import com.labelsys.backend.dto.request.AnnotationTaskPageQuery;
import com.labelsys.backend.dto.request.CreateAnnotationTaskRequest;
import com.labelsys.backend.dto.request.UpdateAnnotationTaskRequest;
import com.labelsys.backend.dto.response.AnnotationTaskResponse;
import com.labelsys.backend.entity.AnnotationTask;
import com.labelsys.backend.entity.AnnotationTaskResource;
import com.labelsys.backend.entity.SourceResource;
2026-04-28 20:14:14 +08:00
import com.labelsys.backend.enums.IndustryType;
2026-04-27 10:27:57 +08:00
import com.labelsys.backend.enums.TaskStatus;
2026-04-28 20:14:14 +08:00
import com.labelsys.backend.enums.TaskType;
2026-04-27 10:27:57 +08:00
import com.labelsys.backend.mapper.AnnotationTaskMapper;
import com.labelsys.backend.mapper.AnnotationTaskResourceMapper;
import com.labelsys.backend.mapper.SourceResourceMapper;
import com.labelsys.backend.util.IdGenerator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
2026-04-27 10:27:57 +08:00
@Slf4j
@Service
@RequiredArgsConstructor
public class AnnotationTaskService {
private final AnnotationTaskMapper annotationTaskMapper;
2026-04-27 10:27:57 +08:00
private final AnnotationTaskResourceMapper annotationTaskResourceMapper;
private final SourceResourceMapper sourceResourceMapper;
private final DataPermissionService dataPermissionService;
2026-04-27 10:27:57 +08:00
@Transactional
public AnnotationTaskResponse createTask(LoginUser currentUser, CreateAnnotationTaskRequest request) {
2026-05-08 16:07:12 +08:00
try {
List<SourceResource> resources = loadAndValidateResources(currentUser, request.resourceIds());
AnnotationTask task = AnnotationTask.builder()
.id(IdGenerator.nextId())
.companyId(currentUser.companyId())
.creatorId(currentUser.userId())
.creatorRole(currentUser.role())
.taskName(request.taskName())
.industryType(defaultIndustryType(request.industryType()))
.taskType(defaultTaskType(request.taskType()))
.taskStatus(TaskStatus.PENDING.name())
.isDeleted(false)
.build();
annotationTaskMapper.insert(task);
saveTaskBindings(task.getId(), currentUser.companyId(), resources);
log.info("created annotation task, companyId={}, userId={}, taskId={}, resourceCount={}",
currentUser.companyId(), currentUser.userId(), task.getId(), resources.size());
return buildTaskResponse(task, resourceIds(resources));
} catch (Exception e) {
log.error("createTask failed, companyId={}, userId={}, taskName={}, error={}",
currentUser.companyId(), currentUser.userId(), request.taskName(), e.getMessage(), e);
throw e;
}
2026-04-27 10:27:57 +08:00
}
@Transactional
public AnnotationTaskResponse updateTask(LoginUser currentUser, Long taskId, UpdateAnnotationTaskRequest request) {
2026-05-08 16:07:12 +08:00
try {
AnnotationTask task = annotationTaskMapper.findByIdAndCompanyId(taskId, currentUser.companyId());
if (task == null) {
throw new BusinessException(ResultCode.NOT_FOUND, "任务不存在");
}
assertTaskPermission(currentUser, task);
2026-04-27 10:27:57 +08:00
2026-05-08 16:07:12 +08:00
boolean resourcesChanged = false;
List<SourceResource> resources = null;
2026-04-28 20:14:14 +08:00
2026-05-08 16:07:12 +08:00
if (request.resourceIds() != null && !request.resourceIds().isEmpty()) {
List<Long> currentResourceIds = normalizeIds(
annotationTaskResourceMapper.listResourceIdsByTaskId(taskId));
List<Long> targetResourceIds = normalizeIds(request.resourceIds());
resourcesChanged = !currentResourceIds.equals(targetResourceIds);
2026-05-08 16:07:12 +08:00
if (TaskStatus.RUNNING.name().equals(task.getTaskStatus()) && resourcesChanged) {
throw new BusinessException(ResultCode.CONFLICT, "运行中的任务不允许修改资源");
}
resources = loadAndValidateResources(currentUser, request.resourceIds());
2026-04-28 20:14:14 +08:00
}
2026-05-08 16:07:12 +08:00
if (request.industryType() != null) {
task.setIndustryType(request.industryType());
}
2026-04-28 20:14:14 +08:00
2026-05-08 16:07:12 +08:00
if (request.taskType() != null) {
task.setTaskType(request.taskType());
}
2026-04-27 10:27:57 +08:00
2026-05-08 16:07:12 +08:00
annotationTaskMapper.updateById(task);
2026-04-27 10:27:57 +08:00
2026-05-08 16:07:12 +08:00
if (resourcesChanged && resources != null) {
annotationTaskResourceMapper.deleteByTaskId(taskId);
saveTaskBindings(taskId, currentUser.companyId(), resources);
}
2026-04-28 20:14:14 +08:00
2026-05-08 16:07:12 +08:00
log.info("updated annotation task, companyId={}, userId={}, taskId={}, resourcesChanged={}",
currentUser.companyId(), currentUser.userId(), taskId, resourcesChanged);
2026-04-28 20:14:14 +08:00
2026-05-08 16:07:12 +08:00
List<Long> finalResourceIds = resources != null ? resourceIds(resources)
: normalizeIds(annotationTaskResourceMapper.listResourceIdsByTaskId(taskId));
2026-04-28 20:14:14 +08:00
2026-05-08 16:07:12 +08:00
return buildTaskResponse(task, finalResourceIds);
} catch (Exception e) {
log.error("updateTask failed, companyId={}, userId={}, taskId={}, error={}",
currentUser.companyId(), currentUser.userId(), taskId, e.getMessage(), e);
throw e;
}
2026-04-27 10:27:57 +08:00
}
public AnnotationTaskResponse getTask(LoginUser currentUser, Long taskId) {
2026-05-08 16:07:12 +08:00
try {
AnnotationTask task = annotationTaskMapper.findByIdAndCompanyId(taskId, currentUser.companyId());
if (task == null) {
throw new BusinessException(ResultCode.NOT_FOUND, "任务不存在");
}
assertTaskPermission(currentUser, task);
return buildTaskResponse(task, normalizeIds(annotationTaskResourceMapper.listResourceIdsByTaskId(taskId)));
} catch (Exception e) {
log.error("getTask failed, companyId={}, userId={}, taskId={}, error={}",
currentUser.companyId(), currentUser.userId(), taskId, e.getMessage(), e);
throw e;
2026-04-27 10:27:57 +08:00
}
}
public PageResult<AnnotationTaskResponse> pageTasks(LoginUser currentUser, AnnotationTaskPageQuery query) {
2026-05-08 16:07:12 +08:00
try {
List<String> allowedRoles = dataPermissionService.getAllowedRoles(currentUser);
boolean shouldFilterByUserId = dataPermissionService.shouldFilterByUserId(currentUser);
LambdaQueryWrapper<AnnotationTask> wrapper = new LambdaQueryWrapper<AnnotationTask>()
.eq(AnnotationTask::getCompanyId, currentUser.companyId())
.eq(query.taskType() != null, AnnotationTask::getTaskType, query.taskType())
.eq(StringUtils.hasText(query.taskStatus()), AnnotationTask::getTaskStatus, query.taskStatus())
.eq(query.isDeleted() != null, AnnotationTask::getIsDeleted, query.isDeleted())
.like(StringUtils.hasText(query.keyword()), AnnotationTask::getTaskName, query.keyword());
if (shouldFilterByUserId) {
wrapper.eq(AnnotationTask::getCreatorId, currentUser.userId());
} else if (!allowedRoles.isEmpty()) {
wrapper.in(AnnotationTask::getCreatorRole, allowedRoles);
}
2026-04-27 16:25:39 +08:00
2026-05-08 16:07:12 +08:00
wrapper.orderByDesc(AnnotationTask::getCreatedAt);
Page<AnnotationTask> page = new Page<>(query.pageNo(), query.pageSize());
Page<AnnotationTask> resultPage = annotationTaskMapper.selectPage(page, wrapper);
List<AnnotationTaskResponse> records = resultPage.getRecords().stream()
.filter(task -> query.resourceId() == null
|| annotationTaskResourceMapper.listResourceIdsByTaskId(task.getId())
.contains(query.resourceId()))
.map(task -> buildTaskResponse(task,
normalizeIds(annotationTaskResourceMapper.listResourceIdsByTaskId(task.getId()))))
.toList();
return new PageResult<>(records, resultPage.getTotal(), (int) resultPage.getCurrent(),
(int) resultPage.getSize());
} catch (Exception e) {
log.error("pageTasks failed, companyId={}, userId={}, error={}",
currentUser.companyId(), currentUser.userId(), e.getMessage(), e);
throw e;
}
2026-04-27 10:27:57 +08:00
}
@Transactional
public void deleteTask(LoginUser currentUser, Long taskId) {
2026-05-08 16:07:12 +08:00
try {
AnnotationTask task = annotationTaskMapper.findByIdAndCompanyId(taskId, currentUser.companyId());
if (task == null) {
throw new BusinessException(ResultCode.NOT_FOUND, "任务不存在");
}
assertTaskPermission(currentUser, task);
if (TaskStatus.RUNNING.name().equals(task.getTaskStatus())) {
throw new BusinessException(ResultCode.CONFLICT, "运行中的任务不允许删除");
}
task.setIsDeleted(true);
annotationTaskMapper.updateById(task);
log.info("deleted annotation task logically, companyId={}, userId={}, taskId={}", currentUser.companyId(),
currentUser.userId(), taskId);
} catch (Exception e) {
log.error("deleteTask failed, companyId={}, userId={}, taskId={}, error={}",
currentUser.companyId(), currentUser.userId(), taskId, e.getMessage(), e);
throw e;
2026-04-27 10:27:57 +08:00
}
}
private List<SourceResource> loadAndValidateResources(LoginUser currentUser, List<Long> resourceIds) {
if (resourceIds == null || resourceIds.isEmpty()) {
throw new BusinessException(ResultCode.BAD_REQUEST, "任务资源不能为空");
}
List<Long> normalizedIds = normalizeIds(resourceIds);
2026-04-28 20:14:14 +08:00
List<SourceResource> resources = sourceResourceMapper.selectByCompanyIdAndIds(currentUser.companyId(),
normalizedIds);
2026-04-27 10:27:57 +08:00
if (resources.size() != normalizedIds.size()) {
throw new BusinessException(ResultCode.BAD_REQUEST, "存在无效资源");
}
for (SourceResource resource : resources) {
2026-04-28 20:14:14 +08:00
if (!dataPermissionService.canAccessCreator(currentUser, resource.getCreatorId(),
resource.getCreatorRole())) {
2026-04-27 10:27:57 +08:00
throw new BusinessException(ResultCode.FORBIDDEN, "无权访问资源");
}
}
resources.sort(Comparator.comparing(SourceResource::getId));
return resources;
}
private void saveTaskBindings(Long taskId, Long companyId, List<SourceResource> resources) {
for (SourceResource resource : resources) {
annotationTaskResourceMapper.insert(AnnotationTaskResource.builder()
.id(IdGenerator.nextId())
.companyId(companyId)
.taskId(taskId)
.resourceId(resource.getId())
.build());
2026-04-27 10:27:57 +08:00
}
}
private AnnotationTaskResponse buildTaskResponse(AnnotationTask task, List<Long> resourceIds) {
return new AnnotationTaskResponse(
task.getId(),
task.getTaskName(),
task.getIndustryType(),
task.getTaskType(),
task.getTaskStatus(),
resourceIds,
task.getCreatedAt(),
task.getUpdatedAt()
);
2026-04-27 10:27:57 +08:00
}
private List<Long> resourceIds(List<SourceResource> resources) {
return resources.stream().map(SourceResource::getId).sorted().toList();
}
private List<Long> normalizeIds(List<Long> resourceIds) {
Set<Long> uniqueIds = new HashSet<>(resourceIds);
List<Long> sortedIds = new ArrayList<>(uniqueIds);
sortedIds.sort(Long::compareTo);
return sortedIds;
}
private void assertTaskPermission(LoginUser currentUser, AnnotationTask task) {
if (!dataPermissionService.canAccessCreator(currentUser, task.getCreatorId(), task.getCreatorRole())) {
throw new BusinessException(ResultCode.FORBIDDEN, "无权操作任务");
}
}
2026-04-28 20:14:14 +08:00
private IndustryType defaultIndustryType(IndustryType industryType) {
return industryType != null ? industryType : IndustryType.TRANSPORT;
2026-04-27 10:27:57 +08:00
}
2026-04-28 20:14:14 +08:00
private TaskType defaultTaskType(TaskType taskType) {
return taskType != null ? taskType : TaskType.EXTRACT_QA;
2026-04-27 10:27:57 +08:00
}
}