feat(settings): add admin task-item dept redirect tool
This commit is contained in:
parent
f041543f02
commit
f484e17e73
@ -138,6 +138,23 @@ public class AdminTaskController {
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "任务节点调拨到指定接单部门")
|
||||
@PostMapping("/items/redirect-dept")
|
||||
public Result<Map<String, Object>> redirectTaskItemToDept(
|
||||
@RequestBody RedirectTaskItemDeptRequest request,
|
||||
@RequestHeader("Authorization") String authHeader
|
||||
) {
|
||||
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||
Long adminId = jwtUtil.getUserIdFromToken(token);
|
||||
Map<String, Object> result = adminTaskService.redirectTaskItemToDept(
|
||||
request.getItemNo(),
|
||||
request.getTargetDeptId(),
|
||||
adminId,
|
||||
request.getReason()
|
||||
);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
// DTO类
|
||||
public static class TransferRequest {
|
||||
private Long newDesignerId;
|
||||
@ -166,6 +183,13 @@ public class AdminTaskController {
|
||||
public void setReason(String reason) { this.reason = reason; }
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class RedirectTaskItemDeptRequest {
|
||||
private String itemNo;
|
||||
private Long targetDeptId;
|
||||
private String reason;
|
||||
}
|
||||
|
||||
// ==================== 任务排队解锁相关接口 ====================
|
||||
|
||||
@Operation(summary = "获取任务解锁状态统计")
|
||||
@ -202,4 +226,3 @@ public class AdminTaskController {
|
||||
return Result.success(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,13 +4,16 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.designhub.common.ResultCode;
|
||||
import com.designhub.entity.Department;
|
||||
import com.designhub.entity.Task;
|
||||
import com.designhub.entity.TaskTransferLog;
|
||||
import com.designhub.entity.User;
|
||||
import com.designhub.exception.BusinessException;
|
||||
import com.designhub.mapper.TaskTransferLogMapper;
|
||||
import com.designhub.mapper.TaskMapper;
|
||||
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.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
@ -37,6 +40,8 @@ public class AdminTaskService {
|
||||
private final DepartmentService departmentService;
|
||||
private final RoleService roleService;
|
||||
private final WorkHoursCalculator workHoursCalculator;
|
||||
private final TaskTransferLogMapper taskTransferLogMapper;
|
||||
private final TaskUnlockService taskUnlockService;
|
||||
|
||||
/**
|
||||
* 管理员分配任务
|
||||
@ -400,6 +405,119 @@ public class AdminTaskService {
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员将子任务直接切换到指定接单部门可接单
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Map<String, Object> redirectTaskItemToDept(String itemNo, Long targetDeptId, Long adminId, String reason) {
|
||||
if (!isAdminUser(adminId)) {
|
||||
throw new BusinessException(ResultCode.FORBIDDEN, "只有管理员可以操作任务节点调拨");
|
||||
}
|
||||
if (!StringUtils.hasText(itemNo)) {
|
||||
throw new BusinessException(ResultCode.PARAM_ERROR, "子任务编号不能为空");
|
||||
}
|
||||
if (targetDeptId == null) {
|
||||
throw new BusinessException(ResultCode.PARAM_ERROR, "目标部门不能为空");
|
||||
}
|
||||
|
||||
TaskItem item = taskItemMapper.selectOne(
|
||||
new LambdaQueryWrapper<TaskItem>()
|
||||
.eq(TaskItem::getItemNo, itemNo.trim())
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
if (item == null) {
|
||||
throw new BusinessException(ResultCode.NOT_FOUND, "子任务不存在");
|
||||
}
|
||||
|
||||
Task task = taskMapper.selectById(item.getTaskId());
|
||||
if (task == null || task.getDeleted() != null && task.getDeleted() == 1) {
|
||||
throw new BusinessException(ResultCode.NOT_FOUND, "主任务不存在");
|
||||
}
|
||||
|
||||
Department targetDept = departmentService.getDepartmentById(targetDeptId);
|
||||
if (targetDept == null) {
|
||||
throw new BusinessException(ResultCode.PARAM_ERROR, "目标部门不存在");
|
||||
}
|
||||
if (targetDept.getStatus() == null || targetDept.getStatus() != 1) {
|
||||
throw new BusinessException(ResultCode.PARAM_ERROR, "目标部门已禁用");
|
||||
}
|
||||
if (!"receiving".equals(targetDept.getDeptType())) {
|
||||
throw new BusinessException(ResultCode.PARAM_ERROR, "目标部门必须是接单部门");
|
||||
}
|
||||
|
||||
if ("completed".equals(item.getStatus())) {
|
||||
throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "已完成子任务不允许调拨");
|
||||
}
|
||||
if ("in_progress".equals(item.getStatus())) {
|
||||
throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "进行中子任务请先转移接单人");
|
||||
}
|
||||
|
||||
Long oldDeptId = item.getReceiverDeptId();
|
||||
Long oldReceiverId = item.getReceiverId();
|
||||
LocalDateTime oldDeadline = item.getDeadline();
|
||||
String oldStatus = item.getStatus();
|
||||
|
||||
item.setStatus("pending");
|
||||
item.setReceiverDeptId(targetDeptId);
|
||||
item.setReceiverId(null);
|
||||
item.setReceiverName(null);
|
||||
item.setClaimTime(null);
|
||||
item.setDeadline(null);
|
||||
item.setIsLocked(0);
|
||||
item.setUpdateTime(LocalDateTime.now());
|
||||
taskItemMapper.updateById(item);
|
||||
|
||||
// 排队记录重建到新部门,确保可见且可抢
|
||||
taskUnlockService.deleteUnlockStatus(item.getId());
|
||||
if (taskUnlockService.isUnlockEnabled() && !"assigned".equals(task.getReleaseType())) {
|
||||
taskUnlockService.onPipelineUnlock(item, targetDeptId);
|
||||
}
|
||||
|
||||
String transferReason = StringUtils.hasText(reason) ? reason.trim() : "管理员手动调拨子任务到指定接单部门";
|
||||
TaskTransferLog transferLog = new TaskTransferLog();
|
||||
transferLog.setTaskId(task.getId());
|
||||
transferLog.setTransferType("transfer");
|
||||
transferLog.setFromUserId(oldReceiverId);
|
||||
transferLog.setToUserId(null);
|
||||
transferLog.setOperatorId(adminId);
|
||||
transferLog.setOldDeadline(oldDeadline);
|
||||
transferLog.setNewDeadline(null);
|
||||
transferLog.setOldPriority(task.getPriority());
|
||||
transferLog.setNewPriority(task.getPriority());
|
||||
transferLog.setReason(transferReason);
|
||||
transferLog.setRemark(String.format("节点调拨:%s(%s)->%s(%s), itemNo=%s, oldStatus=%s",
|
||||
oldDeptId == null ? "-" : oldDeptId,
|
||||
resolveDeptName(oldDeptId),
|
||||
targetDeptId,
|
||||
targetDept.getDeptName(),
|
||||
item.getItemNo(),
|
||||
oldStatus
|
||||
));
|
||||
transferLog.setCreateTime(LocalDateTime.now());
|
||||
taskTransferLogMapper.insert(transferLog);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("taskId", task.getId());
|
||||
result.put("taskNo", task.getTaskNo());
|
||||
result.put("itemId", item.getId());
|
||||
result.put("itemNo", item.getItemNo());
|
||||
result.put("oldStatus", oldStatus);
|
||||
result.put("newStatus", item.getStatus());
|
||||
result.put("oldDeptId", oldDeptId);
|
||||
result.put("oldDeptName", resolveDeptName(oldDeptId));
|
||||
result.put("newDeptId", targetDeptId);
|
||||
result.put("newDeptName", targetDept.getDeptName());
|
||||
return result;
|
||||
}
|
||||
|
||||
private String resolveDeptName(Long deptId) {
|
||||
if (deptId == null) {
|
||||
return "";
|
||||
}
|
||||
Department department = departmentService.getDepartmentById(deptId);
|
||||
return department != null ? department.getDeptName() : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取优先级文本
|
||||
*/
|
||||
@ -517,4 +635,3 @@ public class AdminTaskService {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -71,3 +71,13 @@ export const cancelTask = (taskId: number, reason: string) => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务节点调拨到指定接单部门
|
||||
*/
|
||||
export const redirectTaskItemToDept = (itemNo: string, targetDeptId: number, reason?: string) => {
|
||||
return request.post<Record<string, any>>('/admin/tasks/items/redirect-dept', {
|
||||
itemNo,
|
||||
targetDeptId,
|
||||
reason
|
||||
})
|
||||
}
|
||||
|
||||
@ -2037,6 +2037,78 @@
|
||||
/>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<a-card title="任务节点调拨(管理员)" class="task-delete-card" style="margin-bottom: 24px;">
|
||||
<a-alert
|
||||
type="info"
|
||||
show-icon
|
||||
message="适用场景"
|
||||
description="当最后一个节点卡在待审核/待分配时,可将该子任务直接调拨到目标接单部门,并转为可接单状态。"
|
||||
style="margin-bottom: 16px;"
|
||||
/>
|
||||
<a-form layout="vertical">
|
||||
<a-row :gutter="16">
|
||||
<a-col :xs="24" :md="10">
|
||||
<a-form-item label="子任务编号">
|
||||
<a-input
|
||||
v-model:value="taskItemDispatchForm.itemNo"
|
||||
placeholder="例如:TASK202601161454431928-03-01"
|
||||
size="large"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :xs="24" :md="8">
|
||||
<a-form-item label="目标接单部门">
|
||||
<a-select
|
||||
v-model:value="taskItemDispatchForm.targetDeptId"
|
||||
placeholder="请选择接单部门"
|
||||
size="large"
|
||||
show-search
|
||||
:filter-option="(input: string, option: any) => (option?.children || '').toLowerCase().includes(input.toLowerCase())"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="dept in receivingDepartments"
|
||||
:key="dept.id"
|
||||
:value="dept.id"
|
||||
>
|
||||
{{ dept.deptName }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :xs="24" :md="6">
|
||||
<a-form-item label="操作">
|
||||
<a-popconfirm
|
||||
title="确认执行节点调拨?"
|
||||
description="将清空当前接单人并改为可接单状态"
|
||||
ok-text="确认调拨"
|
||||
cancel-text="取消"
|
||||
@confirm="handleRedirectTaskItemToDept"
|
||||
>
|
||||
<a-button
|
||||
type="primary"
|
||||
danger
|
||||
block
|
||||
size="large"
|
||||
:loading="redirectingTaskItem"
|
||||
:disabled="!taskItemDispatchForm.itemNo || !taskItemDispatchForm.targetDeptId"
|
||||
>
|
||||
执行调拨
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item label="调拨原因(可选)">
|
||||
<a-input
|
||||
v-model:value="taskItemDispatchForm.reason"
|
||||
maxlength="200"
|
||||
show-count
|
||||
placeholder="例如:原接单部门无人处理,转平面视觉部抢单"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
|
||||
<a-alert
|
||||
message="危险操作"
|
||||
@ -3146,6 +3218,7 @@ import {
|
||||
type ThirdPartyDbConfig
|
||||
} from '@/api/thirdPartyDb'
|
||||
import { deleteTaskByTaskNo } from '@/api/task'
|
||||
import { redirectTaskItemToDept } from '@/api/admin'
|
||||
import request from '@/utils/request'
|
||||
|
||||
type LimitPeriod = 'DAILY' | 'WEEKLY' | 'MONTHLY'
|
||||
@ -3332,6 +3405,12 @@ const taskDeleteForm = reactive({
|
||||
taskNo: ''
|
||||
})
|
||||
const deletingTask = ref(false)
|
||||
const taskItemDispatchForm = reactive({
|
||||
itemNo: '',
|
||||
targetDeptId: undefined as number | undefined,
|
||||
reason: ''
|
||||
})
|
||||
const redirectingTaskItem = ref(false)
|
||||
|
||||
// 盲盒模式相关
|
||||
const blindBoxMode = ref(false)
|
||||
@ -3392,6 +3471,36 @@ const handleDeleteTask = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleRedirectTaskItemToDept = async () => {
|
||||
const itemNo = taskItemDispatchForm.itemNo.trim()
|
||||
if (!itemNo) {
|
||||
message.warning('请输入子任务编号')
|
||||
return
|
||||
}
|
||||
if (!taskItemDispatchForm.targetDeptId) {
|
||||
message.warning('请选择目标接单部门')
|
||||
return
|
||||
}
|
||||
|
||||
redirectingTaskItem.value = true
|
||||
try {
|
||||
const response = await redirectTaskItemToDept(
|
||||
itemNo,
|
||||
taskItemDispatchForm.targetDeptId,
|
||||
taskItemDispatchForm.reason?.trim() || undefined
|
||||
)
|
||||
const data = response?.data || {}
|
||||
const deptName = data.newDeptName || '目标部门'
|
||||
message.success(`调拨成功:${itemNo} 已切换到 ${deptName} 可接单`)
|
||||
taskItemDispatchForm.itemNo = ''
|
||||
taskItemDispatchForm.reason = ''
|
||||
} catch (error: any) {
|
||||
message.error(error?.response?.data?.message || error?.message || '调拨失败')
|
||||
} finally {
|
||||
redirectingTaskItem.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 第三方数据库配置相关
|
||||
const thirdPartyDbConfigs = ref<ThirdPartyDbConfig[]>([])
|
||||
const loadingThirdPartyDb = ref(false)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user