feat: 接单部门审核时添加指定设计师接单功能,显示设计师工作量
This commit is contained in:
parent
b44a7e744f
commit
e86b5a45f3
@ -62,7 +62,8 @@ public class TaskApprovalController {
|
||||
) {
|
||||
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||
Long approverId = jwtUtil.getUserIdFromToken(token);
|
||||
taskApprovalService.approveReceivingLevel(id, approverId, dto);
|
||||
// 从dto中获取指定的接收人ID
|
||||
taskApprovalService.approveReceivingLevel(id, approverId, dto, dto.getAssignedReceiverId());
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
|
||||
@ -125,5 +125,14 @@ public class UserController {
|
||||
log.info("调整用户部门结果: userId={}, deptId={}, result={}", id, deptId, result);
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取设计师列表及工作量", description = "获取部门内的设计师列表,包含每个设计师当前进行中的任务数量")
|
||||
@GetMapping("/designers-with-workload")
|
||||
public Result<List<Map<String, Object>>> getDesignersWithWorkload(
|
||||
@RequestParam(required = false) Long deptId
|
||||
) {
|
||||
log.info("获取设计师列表及工作量: deptId={}", deptId);
|
||||
return Result.success(userService.getDesignersWithWorkload(deptId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,6 +17,11 @@ public class TaskApprovalDTO {
|
||||
*/
|
||||
private TaskUpdateDTO taskUpdates;
|
||||
|
||||
/**
|
||||
* 指定的接收人ID(可选,用于接单部门审核时指定设计师)
|
||||
*/
|
||||
private Long assignedReceiverId;
|
||||
|
||||
// Getters and Setters
|
||||
public String getComment() {
|
||||
return comment;
|
||||
@ -33,4 +38,12 @@ public class TaskApprovalDTO {
|
||||
public void setTaskUpdates(TaskUpdateDTO taskUpdates) {
|
||||
this.taskUpdates = taskUpdates;
|
||||
}
|
||||
|
||||
public Long getAssignedReceiverId() {
|
||||
return assignedReceiverId;
|
||||
}
|
||||
|
||||
public void setAssignedReceiverId(Long assignedReceiverId) {
|
||||
this.assignedReceiverId = assignedReceiverId;
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,5 +202,12 @@ public interface TaskMapper extends BaseMapper<Task> {
|
||||
IPage<Task> selectDeptReceivingTaskPage(Page<Task> page,
|
||||
@Param("deptId") Long deptId,
|
||||
@Param("status") String status);
|
||||
|
||||
/**
|
||||
* 查询设计师进行中的子任务数量
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM task_item WHERE receiver_id = #{receiverId} " +
|
||||
"AND status = 'in_progress' AND is_locked = 0")
|
||||
Long countSubTasksByReceiverId(@Param("receiverId") Long receiverId);
|
||||
}
|
||||
|
||||
|
||||
@ -189,9 +189,13 @@ public class TaskApprovalService {
|
||||
|
||||
/**
|
||||
* 接单部门负责人审核通过
|
||||
* @param taskId 任务ID
|
||||
* @param approverId 审核人ID
|
||||
* @param dto 审核数据(包含备注和任务修改)
|
||||
* @param assignedReceiverId 指定的接收人ID(可选)
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void approveReceivingLevel(Long taskId, Long approverId, TaskApprovalDTO dto) {
|
||||
public void approveReceivingLevel(Long taskId, Long approverId, TaskApprovalDTO dto, Long assignedReceiverId) {
|
||||
Task task = getTaskForApproval(taskId, "pending_receiving_approval");
|
||||
validateApprover(task, approverId, "receiving_leader");
|
||||
|
||||
@ -201,13 +205,31 @@ public class TaskApprovalService {
|
||||
logApproval(taskId, approverId, 2, "modify", dto.getComment());
|
||||
}
|
||||
|
||||
// 如果指定了接收人,验证接收人是否存在并设置
|
||||
if (assignedReceiverId != null) {
|
||||
com.designhub.entity.User receiver = userMapper.selectById(assignedReceiverId);
|
||||
if (receiver == null) {
|
||||
throw new BusinessException(ResultCode.PARAM_ERROR, "指定的接收人不存在");
|
||||
}
|
||||
task.setReceiverId(assignedReceiverId);
|
||||
task.setReceiverDeptId(receiver.getDeptId());
|
||||
task.setReleaseType("assigned"); // 设置为指定模式
|
||||
log.info("任务【{}】已指定给设计师【{}】", task.getTitle(), receiver.getRealName());
|
||||
}
|
||||
|
||||
// 判断是否为流水线任务
|
||||
if (task.getIsPipeline() != null && task.getIsPipeline() == 1) {
|
||||
// 流水线任务:拆分子任务
|
||||
splitIntoPipelineSubTasks(task);
|
||||
} else {
|
||||
// 普通任务:直接发布到任务大厅
|
||||
task.setStatus("pending");
|
||||
// 普通任务:根据是否指定接收人设置状态
|
||||
if (assignedReceiverId != null) {
|
||||
task.setStatus("in_progress"); // 指定接单直接进入进行中状态
|
||||
task.setStartTime(LocalDateTime.now());
|
||||
task.setActualStartTime(LocalDateTime.now());
|
||||
} else {
|
||||
task.setStatus("pending"); // 未指定则发布到大厅
|
||||
}
|
||||
}
|
||||
|
||||
// 更新审核状态
|
||||
@ -226,13 +248,33 @@ public class TaskApprovalService {
|
||||
}
|
||||
|
||||
// 记录审核日志
|
||||
logApproval(taskId, approverId, 2, "approve", dto.getComment());
|
||||
String logComment = dto.getComment();
|
||||
if (assignedReceiverId != null) {
|
||||
com.designhub.entity.User receiver = userMapper.selectById(assignedReceiverId);
|
||||
logComment = (logComment != null ? logComment + ";" : "") +
|
||||
"已指定给设计师:" + (receiver != null ? receiver.getRealName() : assignedReceiverId);
|
||||
}
|
||||
logApproval(taskId, approverId, 2, "approve", logComment);
|
||||
|
||||
// TODO: 通知任务大厅(所有接单部门成员)
|
||||
// 暂时不实现群发通知功能,后续可以通过WebSocket或轮询方式实现
|
||||
log.info("任务【{}】已发布到任务大厅", task.getTitle());
|
||||
// 如果指定了接收人,发送通知
|
||||
if (assignedReceiverId != null) {
|
||||
// TODO: 通知指定的设计师
|
||||
log.info("任务【{}】已指定给设计师", task.getTitle());
|
||||
} else {
|
||||
// TODO: 通知任务大厅(所有接单部门成员)
|
||||
log.info("任务【{}】已发布到任务大厅", task.getTitle());
|
||||
}
|
||||
|
||||
log.info("接单部门负责人审核通过,任务ID:{},审核人:{}", taskId, approverId);
|
||||
log.info("接单部门负责人审核通过,任务ID:{},审核人:{},指定接收人:{}",
|
||||
taskId, approverId, assignedReceiverId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 接单部门负责人审核通过(重载方法,保持向后兼容)
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void approveReceivingLevel(Long taskId, Long approverId, TaskApprovalDTO dto) {
|
||||
approveReceivingLevel(taskId, approverId, dto, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -19,6 +19,7 @@ import com.designhub.vo.UserInfoVO;
|
||||
import com.designhub.vo.UserDepartmentVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -48,6 +49,9 @@ public class UserService {
|
||||
private final DepartmentService departmentService;
|
||||
private final JwtUtil jwtUtil;
|
||||
private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
|
||||
@Autowired(required = false)
|
||||
private com.designhub.mapper.TaskMapper taskMapper;
|
||||
|
||||
private Department getDepartmentOrThrow(Long deptId) {
|
||||
if (deptId == null) {
|
||||
@ -1195,4 +1199,67 @@ public class UserService {
|
||||
|
||||
return matchedDeptIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设计师列表及工作量
|
||||
* @param deptId 部门ID(可选,如果不传则返回所有设计师)
|
||||
* @return 设计师列表,包含每个设计师的进行中任务数量
|
||||
*/
|
||||
public List<Map<String, Object>> getDesignersWithWorkload(Long deptId) {
|
||||
log.info("获取设计师列表及工作量: deptId={}", deptId);
|
||||
|
||||
// 查询用户列表
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(User::getStatus, 1); // 只查询启用的用户
|
||||
queryWrapper.eq(User::getDeleted, 0); // 未删除
|
||||
if (deptId != null) {
|
||||
queryWrapper.eq(User::getDeptId, deptId);
|
||||
}
|
||||
queryWrapper.orderByAsc(User::getRealName);
|
||||
|
||||
List<User> users = userMapper.selectList(queryWrapper);
|
||||
|
||||
// 构建返回结果
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (User user : users) {
|
||||
Map<String, Object> designerInfo = new HashMap<>();
|
||||
designerInfo.put("id", user.getId());
|
||||
designerInfo.put("username", user.getUsername());
|
||||
designerInfo.put("realName", user.getRealName());
|
||||
designerInfo.put("deptId", user.getDeptId());
|
||||
|
||||
// 查询进行中的任务数量(包括普通任务和子任务)
|
||||
int inProgressCount = 0;
|
||||
if (taskMapper != null) {
|
||||
// 查询普通任务
|
||||
Long normalTaskCount = taskMapper.selectCount(
|
||||
new LambdaQueryWrapper<com.designhub.entity.Task>()
|
||||
.eq(com.designhub.entity.Task::getReceiverId, user.getId())
|
||||
.eq(com.designhub.entity.Task::getStatus, "in_progress")
|
||||
.eq(com.designhub.entity.Task::getDeleted, 0)
|
||||
);
|
||||
|
||||
// 查询子任务(TaskItem)
|
||||
Long subTaskCount = taskMapper.countSubTasksByReceiverId(user.getId());
|
||||
|
||||
inProgressCount = (normalTaskCount != null ? normalTaskCount.intValue() : 0) +
|
||||
(subTaskCount != null ? subTaskCount.intValue() : 0);
|
||||
}
|
||||
|
||||
designerInfo.put("inProgressCount", inProgressCount);
|
||||
|
||||
// 获取部门名称
|
||||
if (user.getDeptId() != null) {
|
||||
Department dept = departmentMapper.selectById(user.getDeptId());
|
||||
if (dept != null) {
|
||||
designerInfo.put("deptName", dept.getDeptName());
|
||||
}
|
||||
}
|
||||
|
||||
result.add(designerInfo);
|
||||
}
|
||||
|
||||
log.info("查询到{}个设计师", result.size());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -452,6 +452,7 @@ export const getTaskItemsProgress = (taskId: number) => {
|
||||
export interface TaskApprovalDTO {
|
||||
comment?: string // 审核意见
|
||||
taskUpdates?: TaskUpdateDTO // 任务修改(可选)
|
||||
assignedReceiverId?: number // 指定的接收人ID(用于接单部门审核时指定设计师)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -139,3 +139,25 @@ export const updateUserDepartment = async (id: number, deptId: number | null): P
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 设计师及工作量信息
|
||||
*/
|
||||
export interface DesignerWithWorkload {
|
||||
id: number;
|
||||
username: string;
|
||||
realName: string;
|
||||
deptId?: number;
|
||||
deptName?: string;
|
||||
inProgressCount: number; // 进行中的任务数量
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设计师列表及工作量
|
||||
*/
|
||||
export const getDesignersWithWorkload = async (deptId?: number): Promise<DesignerWithWorkload[]> => {
|
||||
const params: any = {};
|
||||
if (deptId) params.deptId = deptId;
|
||||
const response = await request.get('/users/designers-with-workload', { params });
|
||||
return response.data || [];
|
||||
};
|
||||
|
||||
|
||||
@ -1875,11 +1875,13 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted, onUnmounted, h } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { message, Modal, InputNumber, Textarea } from 'ant-design-vue'
|
||||
import { message, Modal, InputNumber, Textarea, Radio, RadioGroup } from 'ant-design-vue'
|
||||
import DefaultLayout from '@/layouts/DefaultLayout.vue'
|
||||
import Icon from '@/components/Icon.vue'
|
||||
import * as taskApi from '@/api/task'
|
||||
import * as userApi from '@/api/user'
|
||||
import type { Task, TaskApprovalDTO } from '@/api/task'
|
||||
import type { DesignerWithWorkload } from '@/api/user'
|
||||
import { useConfigStore } from '@/stores/config'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
@ -2358,40 +2360,172 @@ const getApprovalStatusText = (status: string) => {
|
||||
|
||||
// 两级审核:审核通过
|
||||
const handleApproveTwoLevelTask = async (task: Task) => {
|
||||
Modal.confirm({
|
||||
title: '确认审核通过',
|
||||
content: `确定通过任务"${task.title}"的审核吗?`,
|
||||
okText: '确认通过',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
try {
|
||||
approvalLoading.value = true
|
||||
|
||||
const approvalData: TaskApprovalDTO = {
|
||||
comment: '审核通过'
|
||||
}
|
||||
|
||||
// 根据当前审核角色调用不同的API
|
||||
if (task.currentApproverRole === 'demand_leader') {
|
||||
// 如果是接单部门负责人审核,显示指定设计师选项
|
||||
if (task.currentApproverRole === 'receiving_leader') {
|
||||
showAssignDesignerModal(task)
|
||||
} else {
|
||||
// 需求部门负责人审核,直接通过
|
||||
Modal.confirm({
|
||||
title: '确认审核通过',
|
||||
content: `确定通过任务"${task.title}"的审核吗?`,
|
||||
okText: '确认通过',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
try {
|
||||
approvalLoading.value = true
|
||||
|
||||
const approvalData: TaskApprovalDTO = {
|
||||
comment: '审核通过'
|
||||
}
|
||||
|
||||
await taskApi.approveDemandLevel(task.id!, approvalData)
|
||||
message.success('需求部门审核通过')
|
||||
} else if (task.currentApproverRole === 'receiving_leader') {
|
||||
await taskApi.approveReceivingLevel(task.id!, approvalData)
|
||||
message.success('接单部门审核通过,任务已发布到大厅')
|
||||
|
||||
// 刷新列表和数量
|
||||
loadPendingApprovalTasks()
|
||||
loadAllTabCounts()
|
||||
loadDashboardStats()
|
||||
} catch (error: any) {
|
||||
console.error('审核失败:', error)
|
||||
message.error(error.msg || error.message || '审核失败')
|
||||
} finally {
|
||||
approvalLoading.value = false
|
||||
}
|
||||
|
||||
// 刷新列表和数量
|
||||
loadPendingApprovalTasks()
|
||||
loadAllTabCounts()
|
||||
loadDashboardStats()
|
||||
} catch (error: any) {
|
||||
console.error('审核失败:', error)
|
||||
message.error(error.msg || error.message || '审核失败')
|
||||
} finally {
|
||||
approvalLoading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 显示指定设计师弹窗(接单部门审核时)
|
||||
const showAssignDesignerModal = async (task: Task) => {
|
||||
try {
|
||||
// 获取当前部门的设计师列表
|
||||
const userInfo = localStorage.getItem('userInfo')
|
||||
let deptId: number | undefined
|
||||
if (userInfo) {
|
||||
const user = JSON.parse(userInfo)
|
||||
deptId = user.deptId
|
||||
}
|
||||
})
|
||||
|
||||
// 加载设计师列表及工作量
|
||||
const designers = await userApi.getDesignersWithWorkload(deptId)
|
||||
|
||||
if (designers.length === 0) {
|
||||
message.warning('当前部门没有可用的设计师')
|
||||
return
|
||||
}
|
||||
|
||||
let assignType = 'auto' // 'auto' 或 'manual'
|
||||
let selectedDesignerId: number | null = null
|
||||
|
||||
const modalContent = h('div', { style: 'padding: 10px 0;' }, [
|
||||
h('p', { style: 'margin-bottom: 16px; color: #666;' },
|
||||
'选择任务接单方式:'
|
||||
),
|
||||
h(RadioGroup, {
|
||||
value: assignType,
|
||||
onChange: (e: any) => {
|
||||
assignType = e.target.value
|
||||
},
|
||||
style: { width: '100%', marginBottom: '16px' }
|
||||
}, [
|
||||
h(Radio, { value: 'auto', style: { display: 'block', marginBottom: '12px' } },
|
||||
'发布到任务大厅(设计师自由抢单)'
|
||||
),
|
||||
h(Radio, { value: 'manual', style: { display: 'block' } },
|
||||
'指定设计师接单'
|
||||
)
|
||||
]),
|
||||
// 设计师列表(仅在指定接单时显示)
|
||||
h('div', {
|
||||
style: {
|
||||
marginTop: '16px',
|
||||
display: assignType === 'manual' ? 'block' : 'none'
|
||||
}
|
||||
}, [
|
||||
h('p', { style: { marginBottom: '12px', color: '#666', fontSize: '13px' } },
|
||||
'选择设计师(显示当前进行中的任务数量):'
|
||||
),
|
||||
h(RadioGroup, {
|
||||
value: selectedDesignerId,
|
||||
onChange: (e: any) => {
|
||||
selectedDesignerId = e.target.value
|
||||
},
|
||||
style: { width: '100%' }
|
||||
}, designers.map(designer =>
|
||||
h(Radio, {
|
||||
value: designer.id,
|
||||
style: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginBottom: '8px',
|
||||
padding: '8px',
|
||||
border: '1px solid #f0f0f0',
|
||||
borderRadius: '4px'
|
||||
}
|
||||
}, [
|
||||
h('span', { style: { flex: 1 } }, designer.realName),
|
||||
h('span', {
|
||||
style: {
|
||||
color: designer.inProgressCount > 5 ? '#ff4d4f' :
|
||||
designer.inProgressCount > 3 ? '#faad14' : '#52c41a',
|
||||
fontSize: '12px',
|
||||
marginLeft: '12px'
|
||||
}
|
||||
}, `进行中: ${designer.inProgressCount} 个任务`)
|
||||
])
|
||||
))
|
||||
])
|
||||
])
|
||||
|
||||
Modal.confirm({
|
||||
title: '接单部门审核通过',
|
||||
content: modalContent,
|
||||
width: 600,
|
||||
okText: '确认通过',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
try {
|
||||
approvalLoading.value = true
|
||||
|
||||
// 检查是否需要指定设计师
|
||||
if (assignType === 'manual' && !selectedDesignerId) {
|
||||
message.error('请选择要指定的设计师')
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
const approvalData: TaskApprovalDTO = {
|
||||
comment: assignType === 'manual' ?
|
||||
`指定设计师接单` : '审核通过,发布到任务大厅',
|
||||
assignedReceiverId: assignType === 'manual' ? selectedDesignerId : undefined
|
||||
}
|
||||
|
||||
await taskApi.approveReceivingLevel(task.id!, approvalData)
|
||||
|
||||
if (assignType === 'manual') {
|
||||
const designer = designers.find(d => d.id === selectedDesignerId)
|
||||
message.success(`已指定设计师 ${designer?.realName} 接单`)
|
||||
} else {
|
||||
message.success('审核通过,任务已发布到大厅')
|
||||
}
|
||||
|
||||
// 刷新列表和数量
|
||||
loadPendingApprovalTasks()
|
||||
loadAllTabCounts()
|
||||
loadDashboardStats()
|
||||
} catch (error: any) {
|
||||
console.error('审核失败:', error)
|
||||
message.error(error.msg || error.message || '审核失败')
|
||||
return Promise.reject(error)
|
||||
} finally {
|
||||
approvalLoading.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (error: any) {
|
||||
console.error('获取设计师列表失败:', error)
|
||||
message.error('获取设计师列表失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 两级审核:驳回任务
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user