2029 lines
84 KiB
Java
2029 lines
84 KiB
Java
package com.designhub.service;
|
||
|
||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||
import com.designhub.common.ResultCode;
|
||
import com.designhub.dto.TaskAuditRequest;
|
||
import com.designhub.dto.TaskAssignRequest;
|
||
import com.designhub.dto.TaskUpdateDTO;
|
||
import com.designhub.entity.*;
|
||
import com.designhub.exception.BusinessException;
|
||
import com.designhub.mapper.TaskAuditLogMapper;
|
||
import com.designhub.mapper.TaskAssignLogMapper;
|
||
import com.designhub.mapper.TaskMapper;
|
||
import com.designhub.mapper.BrandMapper;
|
||
import com.designhub.mapper.CategoryMapper;
|
||
import com.designhub.mapper.PlatformMapper;
|
||
import com.designhub.mapper.DemandTypeMapper;
|
||
import com.designhub.vo.DashboardStatsVO;
|
||
import lombok.RequiredArgsConstructor;
|
||
import org.springframework.stereotype.Service;
|
||
import org.springframework.transaction.annotation.Transactional;
|
||
|
||
import java.math.BigDecimal;
|
||
import java.time.LocalDate;
|
||
import java.time.LocalDateTime;
|
||
import java.time.LocalTime;
|
||
import java.time.format.DateTimeFormatter;
|
||
import java.util.Arrays;
|
||
import java.util.List;
|
||
import java.util.Random;
|
||
import java.util.Set;
|
||
import java.util.stream.Collectors;
|
||
import org.springframework.util.StringUtils;
|
||
|
||
/**
|
||
* 任务服务类
|
||
*/
|
||
@Service
|
||
@RequiredArgsConstructor
|
||
public class TaskService {
|
||
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TaskService.class);
|
||
|
||
private final TaskMapper taskMapper;
|
||
private final TaskAuditLogMapper taskAuditLogMapper;
|
||
private final TaskAssignLogMapper taskAssignLogMapper;
|
||
private final BrandMapper brandMapper;
|
||
private final CategoryMapper categoryMapper;
|
||
private final PlatformMapper platformMapper;
|
||
private final DemandTypeMapper demandTypeMapper;
|
||
private final TeamScorePoolService teamScorePoolService;
|
||
private final ConfigService configService;
|
||
private final DelayPenaltyService delayPenaltyService;
|
||
private final UserService userService;
|
||
private final DepartmentService departmentService;
|
||
private final RoleService roleService;
|
||
private final NotificationService notificationService;
|
||
private final com.designhub.mapper.TaskItemMapper taskItemMapper;
|
||
|
||
/**
|
||
* 创建任务
|
||
*/
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public Long createTask(Task task) {
|
||
log.info("创建任务: {}", task.getTitle());
|
||
|
||
// 验证用户权限:只有需求部门可以发起任务(管理员除外)
|
||
if (task.getPublisherId() != null) {
|
||
var user = userService.getUserById(task.getPublisherId());
|
||
if (user == null) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "用户不存在");
|
||
}
|
||
|
||
// 检查是否是管理员(管理员可以同时拥有需求部门和接单部门权限)
|
||
boolean isAdmin = isAdminUser(user.getId());
|
||
|
||
if (!isAdmin) {
|
||
if (user.getDeptId() == null) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "用户部门信息不完整");
|
||
}
|
||
|
||
var department = departmentService.getDepartmentById(user.getDeptId());
|
||
if (department == null) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "用户部门不存在");
|
||
}
|
||
|
||
if (!"demand".equals(department.getDeptType())) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "只有需求部门可以发起任务,您所在的部门是接单部门");
|
||
}
|
||
}
|
||
|
||
// 设置发布人部门ID
|
||
if (task.getPublisherDeptId() == null && user.getDeptId() != null) {
|
||
task.setPublisherDeptId(user.getDeptId());
|
||
}
|
||
}
|
||
|
||
// 验证任务类型并计算积分(支持多类型)
|
||
if (task.getQuantity() != null) {
|
||
// 优先处理多类型
|
||
if (task.getTaskTypeIds() != null && !task.getTaskTypeIds().isEmpty()) {
|
||
String[] typeIds = task.getTaskTypeIds().split(",");
|
||
|
||
// 验证所有类型ID有效
|
||
for (String typeIdStr : typeIds) {
|
||
Long typeId = Long.parseLong(typeIdStr.trim());
|
||
var demandType = configService.getDemandTypeById(typeId);
|
||
if (demandType == null) {
|
||
throw new BusinessException(ResultCode.PARAM_ERROR, "任务类型ID无效: " + typeId);
|
||
}
|
||
}
|
||
|
||
// 计算总积分和工时:取第一个类型的单价
|
||
Long firstTypeId = Long.parseLong(typeIds[0].trim());
|
||
var firstType = configService.getDemandTypeById(firstTypeId);
|
||
int totalScore = firstType.getUnitScore().multiply(BigDecimal.valueOf(task.getQuantity())).intValue();
|
||
int totalHours = firstType.getEstimatedHours() != null ? firstType.getEstimatedHours() * task.getQuantity() : 0;
|
||
|
||
task.setScore(totalScore);
|
||
task.setEstimatedHours(BigDecimal.valueOf(totalHours));
|
||
|
||
// 兼容性:同时保存第一个类型到task_type_id
|
||
task.setTaskTypeId(firstTypeId);
|
||
|
||
log.info("创建多类型任务: typeIds={}, score={}", task.getTaskTypeIds(), totalScore);
|
||
}
|
||
// 兼容单类型模式
|
||
else if (task.getTaskTypeId() != null) {
|
||
var demandType = configService.getDemandTypeById(task.getTaskTypeId());
|
||
if (demandType == null) {
|
||
throw new BusinessException(ResultCode.PARAM_ERROR, "任务类型不存在");
|
||
}
|
||
|
||
// 计算总积分和工时
|
||
int totalScore = demandType.getUnitScore().multiply(BigDecimal.valueOf(task.getQuantity())).intValue();
|
||
int totalHours = demandType.getEstimatedHours() != null ? demandType.getEstimatedHours() * task.getQuantity() : 0;
|
||
|
||
task.setScore(totalScore);
|
||
task.setEstimatedHours(BigDecimal.valueOf(totalHours));
|
||
|
||
// 自动填充taskTypeIds字段
|
||
task.setTaskTypeIds(String.valueOf(task.getTaskTypeId()));
|
||
|
||
log.info("创建单类型任务: typeId={}, score={}", task.getTaskTypeId(), totalScore);
|
||
} else {
|
||
throw new BusinessException(ResultCode.PARAM_ERROR, "请选择任务类型");
|
||
}
|
||
}
|
||
|
||
// 检查部门积分是否充足(管理员跳过积分检查)
|
||
boolean isAdminPublisher = task.getPublisherId() != null && isAdminUser(task.getPublisherId());
|
||
if (!isAdminPublisher && task.getPublisherDeptId() != null && task.getScore() != null) {
|
||
if (!teamScorePoolService.checkBalance(task.getPublisherDeptId(), task.getScore())) {
|
||
throw new BusinessException(ResultCode.INSUFFICIENT_SCORE, "部门积分余额不足");
|
||
}
|
||
|
||
// 检查周期消耗限制
|
||
if (!teamScorePoolService.checkPeriodLimit(task.getPublisherDeptId(), task.getScore())) {
|
||
throw new BusinessException(ResultCode.SCORE_LIMIT_EXCEEDED, "超过本周期积分消耗限制");
|
||
}
|
||
}
|
||
|
||
if (isAdminPublisher) {
|
||
log.info("管理员发布任务,跳过积分检查: publisherId={}", task.getPublisherId());
|
||
}
|
||
|
||
// 生成任务编号
|
||
task.setTaskNo(generateTaskNo());
|
||
|
||
// 生成第三方平台任务编号
|
||
task.setThirdPartyTaskNo(generateThirdPartyTaskNo());
|
||
|
||
// 设置任务状态:默认为草稿,需要审核
|
||
task.setStatus("draft");
|
||
|
||
// 设置审核状态:初始为草稿状态
|
||
task.setAuditStatus("draft");
|
||
|
||
// 设置进度为0
|
||
task.setProgress(0);
|
||
|
||
// 设置紧急标识默认值
|
||
if (task.getIsUrgent() == null) {
|
||
task.setIsUrgent(0);
|
||
}
|
||
|
||
// 设置延误标识默认值
|
||
if (task.getIsDelayed() == null) {
|
||
task.setIsDelayed(0);
|
||
}
|
||
|
||
// 【两级审核流程】初始化审核状态
|
||
initApprovalStatus(task);
|
||
|
||
// 保存任务(草稿状态不扣除积分)
|
||
taskMapper.insert(task);
|
||
|
||
// 注意:草稿状态不扣除积分,只有审核通过后才扣除
|
||
// 积分扣除将在审核通过时进行
|
||
|
||
// 【两级审核流程】发送审核通知
|
||
sendApprovalNotification(task);
|
||
|
||
return task.getId();
|
||
}
|
||
|
||
/**
|
||
* 创建流水线任务
|
||
*/
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public Long createPipelineTask(com.designhub.dto.PipelineTaskDTO dto, Long publisherId) {
|
||
log.info("创建流水线任务: {}, 模式: {}", dto.getTitle(), dto.getPipelineMode());
|
||
|
||
// 验证用户权限
|
||
var user = userService.getUserById(publisherId);
|
||
if (user == null) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "用户不存在");
|
||
}
|
||
|
||
// 检查是否是管理员
|
||
boolean isAdmin = isAdminUser(user.getId());
|
||
|
||
if (!isAdmin) {
|
||
if (user.getDeptId() == null) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "用户部门信息不完整");
|
||
}
|
||
|
||
var department = departmentService.getDepartmentById(user.getDeptId());
|
||
if (department == null) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "用户部门不存在");
|
||
}
|
||
|
||
if (!"demand".equals(department.getDeptType())) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "只有需求部门可以发起任务");
|
||
}
|
||
}
|
||
|
||
// 验证流水线模式
|
||
if (!"sequential".equals(dto.getPipelineMode()) && !"parallel".equals(dto.getPipelineMode())) {
|
||
throw new BusinessException(ResultCode.PARAM_ERROR, "流水线模式必须是 sequential 或 parallel");
|
||
}
|
||
|
||
// 验证子任务列表
|
||
if (dto.getItems() == null || dto.getItems().isEmpty()) {
|
||
throw new BusinessException(ResultCode.PARAM_ERROR, "流水线任务至少需要一个子任务");
|
||
}
|
||
|
||
// 计算总积分和工时
|
||
int totalScore = 0;
|
||
double totalHours = 0;
|
||
for (com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto : dto.getItems()) {
|
||
var demandType = configService.getDemandTypeById(itemDto.getTaskTypeId());
|
||
if (demandType == null) {
|
||
throw new BusinessException(ResultCode.PARAM_ERROR, "任务类型ID无效: " + itemDto.getTaskTypeId());
|
||
}
|
||
|
||
int itemScore = demandType.getUnitScore().multiply(BigDecimal.valueOf(itemDto.getQuantity())).intValue();
|
||
double itemHours = demandType.getEstimatedHours() != null ? demandType.getEstimatedHours() * itemDto.getQuantity() : 0;
|
||
|
||
totalScore += itemScore;
|
||
totalHours += itemHours;
|
||
}
|
||
|
||
// 检查部门积分是否充足(管理员跳过积分检查)
|
||
if (!isAdmin && user.getDeptId() != null) {
|
||
if (!teamScorePoolService.checkBalance(user.getDeptId(), totalScore)) {
|
||
throw new BusinessException(ResultCode.INSUFFICIENT_SCORE, "部门积分余额不足");
|
||
}
|
||
|
||
if (!teamScorePoolService.checkPeriodLimit(user.getDeptId(), totalScore)) {
|
||
throw new BusinessException(ResultCode.SCORE_LIMIT_EXCEEDED, "超过本周期积分消耗限制");
|
||
}
|
||
}
|
||
|
||
if (isAdmin) {
|
||
log.info("管理员创建流水线任务,跳过积分检查: publisherId={}", publisherId);
|
||
}
|
||
|
||
// 创建主任务
|
||
Task task = new Task();
|
||
task.setTaskNo(generateTaskNo());
|
||
task.setThirdPartyTaskNo(generateThirdPartyTaskNo());
|
||
task.setTitle(dto.getTitle());
|
||
task.setPublisherId(publisherId);
|
||
task.setPublisherDeptId(user.getDeptId());
|
||
task.setIsPipeline(1);
|
||
task.setPipelineMode(dto.getPipelineMode());
|
||
task.setScore(totalScore);
|
||
task.setEstimatedHours(BigDecimal.valueOf(totalHours));
|
||
task.setBrandId(dto.getBrandId());
|
||
task.setCategoryId(dto.getCategoryId());
|
||
task.setPlatformId(dto.getPlatformId());
|
||
task.setSpuInfo(dto.getSpuInfo());
|
||
task.setTaskSubType(dto.getTaskSubType());
|
||
task.setAttachmentUrl(dto.getAttachmentUrl());
|
||
task.setRemark(dto.getRemark());
|
||
task.setPriority(dto.getPriority() != null ? dto.getPriority() : 1); // 默认优先级:1-普通
|
||
|
||
// 设置时间字段
|
||
task.setCreateTime(LocalDateTime.now());
|
||
// 如果前端没有传递截止时间,默认为创建时间+7天
|
||
if (dto.getDeadline() != null) {
|
||
task.setDeadline(dto.getDeadline());
|
||
} else {
|
||
task.setDeadline(LocalDateTime.now().plusDays(7));
|
||
}
|
||
|
||
// 设置任务类型ID(使用第一个任务类型作为主任务类型)
|
||
if (dto.getItems() != null && !dto.getItems().isEmpty()) {
|
||
task.setTaskTypeId(dto.getItems().get(0).getTaskTypeId());
|
||
// 设置所有任务类型ID(逗号分隔)
|
||
String taskTypeIds = dto.getItems().stream()
|
||
.map(item -> String.valueOf(item.getTaskTypeId()))
|
||
.collect(Collectors.joining(","));
|
||
task.setTaskTypeIds(taskTypeIds);
|
||
}
|
||
|
||
task.setStatus("draft");
|
||
task.setAuditStatus("draft");
|
||
task.setProgress(0);
|
||
task.setIsUrgent(0);
|
||
task.setIsDelayed(0);
|
||
// 设置发布类型(从前端传递,默认为recruitment)
|
||
task.setReleaseType(dto.getReleaseType() != null ? dto.getReleaseType() : "recruitment");
|
||
// 如果是assigned模式,设置接收人
|
||
if ("assigned".equals(dto.getReleaseType()) && dto.getReceiverId() != null) {
|
||
task.setReceiverId(dto.getReceiverId());
|
||
// 获取接收人的部门ID
|
||
User receiver = userService.getUserById(dto.getReceiverId());
|
||
if (receiver != null && receiver.getDeptId() != null) {
|
||
task.setReceiverDeptId(receiver.getDeptId());
|
||
}
|
||
}
|
||
|
||
// 【两级审核流程】初始化审核状态
|
||
initApprovalStatus(task);
|
||
|
||
taskMapper.insert(task);
|
||
log.info("主任务创建成功,ID: {}, 任务编号: {}", task.getId(), task.getTaskNo());
|
||
|
||
// **步骤1:按接单部门自动分组**
|
||
// 遍历任务类型列表,根据接单部门分组
|
||
int currentGroupNo = 1;
|
||
String lastAllowedDeptIds = null;
|
||
|
||
for (int i = 0; i < dto.getItems().size(); i++) {
|
||
com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto = dto.getItems().get(i);
|
||
var demandType = configService.getDemandTypeById(itemDto.getTaskTypeId());
|
||
|
||
// 获取当前任务类型的接单部门配置
|
||
String currentAllowedDeptIds = demandType.getAllowedDeptIds();
|
||
|
||
// 判断是否需要创建新分组
|
||
boolean needNewGroup = false;
|
||
if (i > 0) {
|
||
// 如果当前或上一个的 allowed_dept_ids 为 null,则必须分组
|
||
// 因为 null 表示所有部门可接单,无法确定具体接单部门
|
||
if (currentAllowedDeptIds == null || lastAllowedDeptIds == null) {
|
||
needNewGroup = true;
|
||
}
|
||
// 如果两者都不为 null,则比较是否相同
|
||
else if (!currentAllowedDeptIds.equals(lastAllowedDeptIds)) {
|
||
needNewGroup = true;
|
||
}
|
||
}
|
||
|
||
if (needNewGroup) {
|
||
currentGroupNo++;
|
||
log.info("检测到接单部门变化,创建新分组: groupNo={}, 前一部门={}, 当前部门={}",
|
||
currentGroupNo, lastAllowedDeptIds, currentAllowedDeptIds);
|
||
}
|
||
|
||
// 记录当前分组号(临时存储在sortOrder字段,稍后会恢复)
|
||
itemDto.setSortOrder(currentGroupNo * 1000 + i); // 高位存group_no,低位存原始顺序
|
||
lastAllowedDeptIds = currentAllowedDeptIds;
|
||
}
|
||
|
||
log.info("分组完成,共 {} 个子任务组", currentGroupNo);
|
||
|
||
// **步骤2:创建子任务,按分组设置状态**
|
||
for (int i = 0; i < dto.getItems().size(); i++) {
|
||
com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto = dto.getItems().get(i);
|
||
|
||
var demandType = configService.getDemandTypeById(itemDto.getTaskTypeId());
|
||
|
||
// 解析分组号
|
||
int itemGroupNo = itemDto.getSortOrder() / 1000;
|
||
int itemIndex = itemDto.getSortOrder() % 1000;
|
||
|
||
com.designhub.entity.TaskItem item = new com.designhub.entity.TaskItem();
|
||
item.setTaskId(task.getId());
|
||
item.setItemNo(generateTaskItemNo(task.getTaskNo(), i + 1));
|
||
item.setGroupNo(itemGroupNo);
|
||
item.setTaskTypeId(itemDto.getTaskTypeId());
|
||
item.setQuantity(itemDto.getQuantity());
|
||
item.setUnitScore(demandType.getUnitScore().intValue());
|
||
item.setTotalScore(demandType.getUnitScore().multiply(BigDecimal.valueOf(itemDto.getQuantity())).intValue());
|
||
item.setEstimatedHours(BigDecimal.valueOf(demandType.getEstimatedHours() != null ? demandType.getEstimatedHours() * itemDto.getQuantity() : 0));
|
||
item.setSortOrder(i + 1); // 恢复真实的排序号
|
||
item.setProgress(0);
|
||
|
||
// 设置子任务指定的接收人
|
||
if (itemDto.getReceiverId() != null) {
|
||
item.setReceiverId(itemDto.getReceiverId());
|
||
// 获取接收人名字
|
||
User receiver = userService.getUserById(itemDto.getReceiverId());
|
||
if (receiver != null) {
|
||
item.setReceiverName(receiver.getRealName());
|
||
}
|
||
}
|
||
|
||
// 关键:根据流水线模式和分组设置初始状态
|
||
if ("sequential".equals(dto.getPipelineMode())) {
|
||
if (itemGroupNo == 1) {
|
||
// 顺序流水线:第一组
|
||
if (item.getReceiverId() != null) {
|
||
item.setStatus("in_progress"); // 指定了人,直接进行中
|
||
} else {
|
||
item.setStatus("pending"); // 没指定人,待接单
|
||
}
|
||
item.setIsLocked(0);
|
||
item.setPrevItemId(null);
|
||
} else {
|
||
// 其他组的子任务都是 locked(锁定)
|
||
item.setStatus("locked");
|
||
item.setIsLocked(1);
|
||
}
|
||
} else {
|
||
// 并行流水线
|
||
if (item.getReceiverId() != null) {
|
||
item.setStatus("in_progress");
|
||
} else {
|
||
item.setStatus("pending");
|
||
}
|
||
item.setIsLocked(0);
|
||
}
|
||
|
||
taskItemMapper.insert(item);
|
||
log.info("子任务创建成功,ID: {}, 编号: {}, 组号: {}, 状态: {}",
|
||
item.getId(), item.getItemNo(), item.getGroupNo(), item.getStatus());
|
||
}
|
||
|
||
log.info("流水线任务创建完成,主任务ID: {}, 子任务数: {}, 分组数: {}",
|
||
task.getId(), dto.getItems().size(), currentGroupNo);
|
||
|
||
return task.getId();
|
||
}
|
||
|
||
/**
|
||
* 生成子任务编号
|
||
*/
|
||
private String generateTaskItemNo(String taskNo, int itemIndex) {
|
||
return taskNo + "-" + String.format("%02d", itemIndex);
|
||
}
|
||
|
||
/**
|
||
* 生成任务编号
|
||
*/
|
||
private String generateTaskNo() {
|
||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||
String date = LocalDateTime.now().format(formatter);
|
||
String random = String.format("%04d", new Random().nextInt(10000));
|
||
return "TASK" + date + random;
|
||
}
|
||
|
||
/**
|
||
* 生成第三方平台任务编号
|
||
* 格式:THIRD-yyyyMMddHHmmss-随机数
|
||
*/
|
||
private String generateThirdPartyTaskNo() {
|
||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
||
String datetime = LocalDateTime.now().format(formatter);
|
||
String random = String.format("%06d", new Random().nextInt(1000000));
|
||
return "THIRD-" + datetime + "-" + random;
|
||
}
|
||
|
||
/**
|
||
* 获取任务列表
|
||
*/
|
||
public IPage<Task> getTaskList(Integer page, Integer pageSize, String status, Long taskTypeId) {
|
||
Page<Task> pageParam = new Page<>(page, pageSize);
|
||
LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
|
||
|
||
// 根据状态筛选
|
||
if (StringUtils.hasText(status)) {
|
||
wrapper.eq(Task::getStatus, status);
|
||
}
|
||
|
||
// 根据任务类型筛选
|
||
if (taskTypeId != null) {
|
||
wrapper.eq(Task::getTaskTypeId, taskTypeId);
|
||
}
|
||
|
||
// 按更新时间倒序
|
||
wrapper.orderByDesc(Task::getUpdateTime);
|
||
|
||
return taskMapper.selectPage(pageParam, wrapper);
|
||
}
|
||
|
||
/**
|
||
* 获取所有待审核任务(处于审批流程中的任务)
|
||
* 包括:pending_demand_approval 和 pending_receiving_approval 状态的任务
|
||
*/
|
||
public IPage<Task> getAllPendingApprovalTasks(Integer page, Integer pageSize, Long taskTypeId) {
|
||
Page<Task> pageParam = new Page<>(page, pageSize);
|
||
LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
|
||
|
||
// 查询处于审批流程中的任务
|
||
wrapper.in(Task::getApprovalStatus, "pending_demand_approval", "pending_receiving_approval");
|
||
|
||
// 根据任务类型筛选
|
||
if (taskTypeId != null) {
|
||
wrapper.eq(Task::getTaskTypeId, taskTypeId);
|
||
}
|
||
|
||
// 未删除的任务
|
||
wrapper.eq(Task::getDeleted, 0);
|
||
|
||
// 按创建时间倒序
|
||
wrapper.orderByDesc(Task::getCreateTime);
|
||
|
||
IPage<Task> result = taskMapper.selectPage(pageParam, wrapper);
|
||
|
||
// 填充关联信息
|
||
for (Task task : result.getRecords()) {
|
||
// 填充发布人名字
|
||
if (task.getPublisherId() != null) {
|
||
User publisher = userService.getUserById(task.getPublisherId());
|
||
if (publisher != null) {
|
||
task.setPublisherName(publisher.getRealName() != null ? publisher.getRealName() : publisher.getUsername());
|
||
}
|
||
}
|
||
// 填充接收人名字
|
||
if (task.getReceiverId() != null) {
|
||
User receiver = userService.getUserById(task.getReceiverId());
|
||
if (receiver != null) {
|
||
task.setReceiverName(receiver.getRealName() != null ? receiver.getRealName() : receiver.getUsername());
|
||
}
|
||
}
|
||
// 填充当前审核人姓名
|
||
if (task.getCurrentApproverId() != null) {
|
||
User approver = userService.getUserById(task.getCurrentApproverId());
|
||
if (approver != null) {
|
||
task.setCurrentApproverName(approver.getRealName() != null ? approver.getRealName() : approver.getUsername());
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 获取已完成任务列表(根据角色权限)
|
||
* - 个人用户:只看自己接的已完成任务
|
||
* - 部门负责人:看本部门所有已完成任务
|
||
* - 管理员:看所有已完成任务
|
||
*/
|
||
public IPage<Task> getCompletedTasks(Integer page, Integer pageSize,
|
||
Long userId, String keyword, String sortBy) {
|
||
// 1. 获取用户信息和角色
|
||
User user = userService.getUserById(userId);
|
||
if (user == null) {
|
||
throw new BusinessException("用户不存在");
|
||
}
|
||
|
||
boolean isAdmin = isAdminUser(userId);
|
||
boolean isDeptHead = isDeptHeadUser(userId);
|
||
|
||
// 2. 构建查询条件
|
||
Page<Task> pageParam = new Page<>(page, pageSize);
|
||
LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
|
||
|
||
// 状态必须是已完成
|
||
wrapper.eq(Task::getStatus, "completed");
|
||
|
||
// 3. 根据角色筛选数据范围
|
||
if (isAdmin) {
|
||
// 管理员:查看所有
|
||
log.info("管理员查看所有已完成任务");
|
||
} else if (isDeptHead) {
|
||
// 部门负责人:查看本部门的
|
||
wrapper.and(w -> w
|
||
.eq(Task::getPublisherDeptId, user.getDeptId()) // 本部门发起的
|
||
.or()
|
||
.eq(Task::getReceiverDeptId, user.getDeptId()) // 或本部门接的
|
||
);
|
||
log.info("部门负责人查看部门已完成任务: deptId={}", user.getDeptId());
|
||
} else {
|
||
// 普通用户:只看自己接的
|
||
wrapper.eq(Task::getReceiverId, userId);
|
||
log.info("个人用户查看个人已完成任务: userId={}", userId);
|
||
}
|
||
|
||
// 4. 关键词搜索
|
||
if (org.springframework.util.StringUtils.hasText(keyword)) {
|
||
wrapper.and(w -> w
|
||
.like(Task::getTitle, keyword)
|
||
.or()
|
||
.like(Task::getTaskNo, keyword)
|
||
);
|
||
}
|
||
|
||
// 5. 排序
|
||
if ("score".equals(sortBy)) {
|
||
wrapper.orderByDesc(Task::getScore);
|
||
} else {
|
||
wrapper.orderByDesc(Task::getCompleteTime); // 默认按完成时间降序
|
||
}
|
||
|
||
// 6. 查询并填充用户名
|
||
IPage<Task> result = taskMapper.selectPage(pageParam, wrapper);
|
||
for (Task task : result.getRecords()) {
|
||
// 填充发布人名字
|
||
if (task.getPublisherId() != null) {
|
||
User publisher = userService.getUserById(task.getPublisherId());
|
||
if (publisher != null) {
|
||
task.setPublisherName(publisher.getRealName());
|
||
}
|
||
}
|
||
// 填充接收人名字
|
||
if (task.getReceiverId() != null) {
|
||
User receiver = userService.getUserById(task.getReceiverId());
|
||
if (receiver != null) {
|
||
task.setReceiverName(receiver.getRealName());
|
||
}
|
||
}
|
||
// 填充当前审核人姓名
|
||
if (task.getCurrentApproverId() != null) {
|
||
User approver = userService.getUserById(task.getCurrentApproverId());
|
||
if (approver != null) {
|
||
task.setCurrentApproverName(approver.getRealName() != null ? approver.getRealName() : approver.getUsername());
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 获取任务大厅(根据用户部门筛选可接单的任务)
|
||
*/
|
||
public IPage<Task> getTaskHall(Integer page, Integer pageSize, Long taskTypeId, Long demandTypeId,
|
||
Long brandId, Long categoryId, Long platformId, String taskSubType,
|
||
Integer minScore, Integer maxScore, String keyword, String publisherName,
|
||
String timeRange, String sortBy, Long userDeptId) {
|
||
Page<Task> pageParam = new Page<>(page, pageSize);
|
||
|
||
// 获取当前用户部门可以接单的任务类型ID列表
|
||
List<Long> allowedTaskTypeIds = null;
|
||
if (userDeptId != null) {
|
||
allowedTaskTypeIds = demandTypeMapper.selectList(null).stream()
|
||
.filter(dt -> {
|
||
String allowedDepts = dt.getAllowedDeptIds();
|
||
// 如果allowedDeptIds为空,表示所有部门都可接单
|
||
if (allowedDepts == null || allowedDepts.isEmpty()) {
|
||
return true;
|
||
}
|
||
// 检查用户部门是否在允许列表中
|
||
String[] deptIds = allowedDepts.split(",");
|
||
for (String deptId : deptIds) {
|
||
if (deptId.trim().equals(String.valueOf(userDeptId))) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
})
|
||
.map(DemandType::getId)
|
||
.collect(Collectors.toList());
|
||
}
|
||
|
||
return taskMapper.selectTaskHallByDept(pageParam, taskTypeId, demandTypeId, brandId, categoryId,
|
||
platformId, taskSubType, minScore, maxScore, keyword,
|
||
publisherName, timeRange, sortBy, allowedTaskTypeIds);
|
||
}
|
||
|
||
/**
|
||
* 获取用户所属部门ID
|
||
*/
|
||
public Long getUserDeptId(Long userId) {
|
||
User user = userService.getUserById(userId);
|
||
return user != null ? user.getDeptId() : null;
|
||
}
|
||
|
||
/**
|
||
* 获取我发布的任务
|
||
*/
|
||
public IPage<Task> getMyPublishedTasks(Integer page, Integer pageSize, Long userId, String status) {
|
||
Page<Task> pageParam = new Page<>(page, pageSize);
|
||
IPage<Task> taskPage = taskMapper.selectMyPublishedTasks(pageParam, userId, status);
|
||
|
||
// 填充子任务统计信息
|
||
for (Task task : taskPage.getRecords()) {
|
||
fillSubTaskStats(task);
|
||
}
|
||
|
||
return taskPage;
|
||
}
|
||
|
||
/**
|
||
* 获取我接收的任务
|
||
*/
|
||
public IPage<Task> getMyReceivedTasks(Integer page, Integer pageSize, Long userId, String status) {
|
||
Page<Task> pageParam = new Page<>(page, pageSize);
|
||
IPage<Task> taskPage = taskMapper.selectMyReceivedTasks(pageParam, userId, status);
|
||
|
||
// 填充子任务统计信息
|
||
for (Task task : taskPage.getRecords()) {
|
||
fillSubTaskStats(task);
|
||
}
|
||
|
||
return taskPage;
|
||
}
|
||
|
||
/**
|
||
* 填充流水线任务的子任务统计信息
|
||
*/
|
||
private void fillSubTaskStats(Task task) {
|
||
// 只有流水线任务才需要填充子任务信息
|
||
if (task.getIsPipeline() == null || task.getIsPipeline() != 1) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 查询所有子任务
|
||
List<TaskItem> subTasks = taskItemMapper.selectList(
|
||
new LambdaQueryWrapper<TaskItem>()
|
||
.eq(TaskItem::getTaskId, task.getId())
|
||
.orderByAsc(TaskItem::getSortOrder)
|
||
);
|
||
|
||
// 汇总所有子任务的交付路径
|
||
if (subTasks != null && !subTasks.isEmpty()) {
|
||
List<String> allDeliveryPaths = subTasks.stream()
|
||
.filter(item -> item.getDeliveryPath() != null && !item.getDeliveryPath().isEmpty())
|
||
.map(item -> "【" + item.getItemNo() + "】" + item.getDeliveryPath())
|
||
.collect(Collectors.toList());
|
||
task.setAllDeliveryPaths(allDeliveryPaths);
|
||
}
|
||
|
||
if (subTasks != null && !subTasks.isEmpty()) {
|
||
task.setSubTaskTotal(subTasks.size());
|
||
|
||
// 统计各状态子任务数量
|
||
long completedCount = subTasks.stream().filter(item -> "completed".equals(item.getStatus())).count();
|
||
long inProgressCount = subTasks.stream().filter(item -> "in_progress".equals(item.getStatus())).count();
|
||
long pendingCount = subTasks.stream().filter(item -> "pending".equals(item.getStatus())).count();
|
||
|
||
task.setSubTaskCompleted((int) completedCount);
|
||
task.setSubTaskInProgress((int) inProgressCount);
|
||
task.setSubTaskPending((int) pendingCount);
|
||
|
||
// 设置子任务列表(包含处理人信息)
|
||
task.setSubTasks(subTasks);
|
||
|
||
// 如果主任务没有receiverName,从子任务中获取第一个有接单人的
|
||
if (task.getReceiverName() == null || task.getReceiverName().isEmpty()) {
|
||
subTasks.stream()
|
||
.filter(item -> item.getReceiverName() != null && !item.getReceiverName().isEmpty())
|
||
.findFirst()
|
||
.ifPresent(item -> task.setReceiverName(item.getReceiverName() + "等"));
|
||
}
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("填充子任务统计信息失败:taskId={}", task.getId(), e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取任务详情
|
||
*/
|
||
public Task getTaskById(Long id) {
|
||
Task task = taskMapper.selectById(id);
|
||
if (task == null) {
|
||
throw new BusinessException(ResultCode.TASK_NOT_EXIST);
|
||
}
|
||
|
||
// 填充关联信息
|
||
fillTaskRelatedInfo(task);
|
||
|
||
return task;
|
||
}
|
||
|
||
/**
|
||
* 填充任务关联信息(品牌、类目、平台、发布人等)
|
||
*/
|
||
private void fillTaskRelatedInfo(Task task) {
|
||
// 填充品牌名称
|
||
if (task.getBrandId() != null) {
|
||
Brand brand = brandMapper.selectById(task.getBrandId());
|
||
if (brand != null) {
|
||
task.setBrandName(brand.getName());
|
||
}
|
||
}
|
||
|
||
// 填充类目名称
|
||
if (task.getCategoryId() != null) {
|
||
Category category = categoryMapper.selectById(task.getCategoryId());
|
||
if (category != null) {
|
||
task.setCategoryName(category.getName());
|
||
}
|
||
}
|
||
|
||
// 填充平台名称
|
||
if (task.getPlatformId() != null) {
|
||
Platform platform = platformMapper.selectById(task.getPlatformId());
|
||
if (platform != null) {
|
||
task.setPlatformName(platform.getName());
|
||
}
|
||
}
|
||
|
||
// 填充发布人名称
|
||
if (task.getPublisherId() != null) {
|
||
User publisher = userService.getUserById(task.getPublisherId());
|
||
if (publisher != null) {
|
||
task.setPublisherName(publisher.getRealName() != null ? publisher.getRealName() : publisher.getUsername());
|
||
}
|
||
}
|
||
|
||
// 填充发布人部门名称
|
||
if (task.getPublisherDeptId() != null) {
|
||
Department dept = departmentService.getDepartmentById(task.getPublisherDeptId());
|
||
if (dept != null) {
|
||
task.setPublisherDeptName(dept.getDeptName());
|
||
}
|
||
}
|
||
|
||
// 填充接收人名称
|
||
if (task.getReceiverId() != null) {
|
||
User receiver = userService.getUserById(task.getReceiverId());
|
||
if (receiver != null) {
|
||
task.setReceiverName(receiver.getRealName() != null ? receiver.getRealName() : receiver.getUsername());
|
||
}
|
||
}
|
||
|
||
// 填充当前审核人名称
|
||
if (task.getCurrentApproverId() != null) {
|
||
User approver = userService.getUserById(task.getCurrentApproverId());
|
||
if (approver != null) {
|
||
task.setCurrentApproverName(approver.getRealName() != null ? approver.getRealName() : approver.getUsername());
|
||
}
|
||
}
|
||
|
||
// 填充子任务统计信息
|
||
fillSubTaskStats(task);
|
||
}
|
||
|
||
/**
|
||
* 更新任务
|
||
*/
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public void updateTask(Long id, Task task) {
|
||
Task existTask = getTaskById(id);
|
||
// TODO: 验证权限
|
||
task.setId(id);
|
||
taskMapper.updateById(task);
|
||
}
|
||
|
||
/**
|
||
* 抢单
|
||
*/
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public void grabTask(Long taskId, Long userId) {
|
||
log.info("用户 {} 抢单任务 {}", userId, taskId);
|
||
|
||
Task task = getTaskById(taskId);
|
||
|
||
if (!"pending".equals(task.getStatus())) {
|
||
throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "任务不是待接单状态");
|
||
}
|
||
if (task.getReceiverId() != null) {
|
||
throw new BusinessException(ResultCode.TASK_ALREADY_TAKEN);
|
||
}
|
||
if (!"recruitment".equalsIgnoreCase(task.getReleaseType())) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "该任务不支持抢单");
|
||
}
|
||
|
||
// 获取用户信息
|
||
var user = userService.getUserById(userId);
|
||
if (user == null) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "用户不存在");
|
||
}
|
||
|
||
// 检查是否是管理员(管理员可以抢单,绕过所有限制)
|
||
boolean isAdmin = isAdminUser(userId);
|
||
|
||
Long deptId = user.getDeptId();
|
||
|
||
// 非管理员需要进行权限验证
|
||
if (!isAdmin) {
|
||
if (deptId == null) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "您未分配部门,无法接单");
|
||
}
|
||
|
||
// 获取用户所属部门
|
||
Department department = departmentService.getDepartmentById(deptId);
|
||
if (department == null) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "未找到用户所属部门,请联系管理员");
|
||
}
|
||
|
||
// 检查1:需求部门不能抢单
|
||
if ("demand".equals(department.getDeptType())) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "需求部门成员不能接单,只能发起需求");
|
||
}
|
||
|
||
// 检查2:接单部门类型验证
|
||
if (!"receiving".equals(department.getDeptType())) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "只有接单部门成员可以接收任务");
|
||
}
|
||
|
||
// 检查3:部门负责人不能抢单
|
||
if (department.getLeaderId() != null && department.getLeaderId().equals(userId)) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "部门负责人不能接单,请分配给设计师");
|
||
}
|
||
|
||
// 检查4:验证部门是否允许接该需求类型
|
||
if (task.getDemandTypeId() != null) {
|
||
validateAllowedDemandTypes(department, task.getDemandTypeId());
|
||
}
|
||
} else {
|
||
log.info("管理员 {} 抢单任务 {}", user.getRealName(), taskId);
|
||
}
|
||
|
||
LocalDateTime now = LocalDateTime.now();
|
||
// 重新计算deadline:从接单时间开始计算,无论之前是否有deadline
|
||
LocalDateTime deadline = null;
|
||
if (task.getEstimatedHours() != null) {
|
||
int hours = task.getEstimatedHours().intValue();
|
||
if (hours > 0) {
|
||
deadline = now.plusHours(hours);
|
||
log.info("任务deadline已重新计算:从接单时间 {} 开始,预估 {} 小时,截止时间 {}",
|
||
now, hours, deadline);
|
||
}
|
||
}
|
||
|
||
int updated = taskMapper.grabTask(taskId, userId, deptId, now, deadline);
|
||
if (updated == 0) {
|
||
throw new BusinessException(ResultCode.TASK_ALREADY_TAKEN, "任务已被其他设计师抢走");
|
||
}
|
||
|
||
log.info("用户 {} 成功抢单任务 {}", userId, taskId);
|
||
|
||
// 发送通知给任务发起人
|
||
if (task.getPublisherId() != null) {
|
||
try {
|
||
notificationService.sendNotification(
|
||
task.getPublisherId(),
|
||
"任务已被接单",
|
||
String.format("您的任务【%s】已被 %s 接单", task.getTitle(), user.getRealName() != null ? user.getRealName() : user.getUsername()),
|
||
"task_grabbed",
|
||
taskId
|
||
);
|
||
} catch (Exception e) {
|
||
log.error("发送抢单通知失败", e);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 提交任务
|
||
*/
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public void submitTask(Long taskId, Long userId, String deliveryPath) {
|
||
log.info("用户 {} 提交任务 {}", userId, taskId);
|
||
|
||
Task task = getTaskById(taskId);
|
||
|
||
// 验证权限(必须是任务接收人)
|
||
if (!userId.equals(task.getReceiverId())) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "只有任务接收人可以提交");
|
||
}
|
||
|
||
// 验证任务状态:允许 in_progress 或 rejected(驳回后重新提交)
|
||
if (!"in_progress".equals(task.getStatus()) && !"rejected".equals(task.getStatus())) {
|
||
throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "任务状态不允许提交,当前状态:" + task.getStatus());
|
||
}
|
||
|
||
// 更新任务
|
||
task.setDeliveryPath(deliveryPath);
|
||
task.setStatus("review");
|
||
task.setCompleteTime(LocalDateTime.now());
|
||
task.setProgress(100);
|
||
|
||
taskMapper.updateById(task);
|
||
|
||
// 发送通知给发布人
|
||
if (task.getPublisherId() != null) {
|
||
try {
|
||
User receiver = userService.getUserById(userId);
|
||
String receiverName = receiver != null ?
|
||
(receiver.getRealName() != null ? receiver.getRealName() : receiver.getUsername()) : "设计师";
|
||
notificationService.sendTaskNotification(
|
||
task.getPublisherId(),
|
||
taskId,
|
||
"任务已提交待审核",
|
||
String.format("您发布的任务【%s】已由 %s 提交,请及时审核", task.getTitle(), receiverName)
|
||
);
|
||
} catch (Exception e) {
|
||
log.error("发送任务提交通知失败", e);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 审核任务
|
||
*/
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public void reviewTask(Long taskId, Long userId, Boolean approved, String comment, Integer rating) {
|
||
log.info("用户 {} 审核任务 {}, 结果: {}", userId, taskId, approved);
|
||
|
||
Task task = getTaskById(taskId);
|
||
|
||
// 验证权限(必须是任务发布人)
|
||
if (!userId.equals(task.getPublisherId())) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "只有任务发布人可以审核");
|
||
}
|
||
|
||
// 验证任务状态
|
||
if (!"review".equals(task.getStatus())) {
|
||
throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "任务不是待审核状态");
|
||
}
|
||
|
||
if (approved) {
|
||
// 审核通过
|
||
task.setStatus("completed");
|
||
task.setReviewComment(comment);
|
||
task.setRating(rating);
|
||
|
||
// 设置完成时间(如果还没有设置)
|
||
if (task.getCompleteTime() == null) {
|
||
task.setCompleteTime(LocalDateTime.now());
|
||
}
|
||
|
||
// 检查是否延误并计算扣分
|
||
if (task.getDeadline() != null && task.getCompleteTime() != null
|
||
&& task.getCompleteTime().isAfter(task.getDeadline())) {
|
||
task.setIsDelayed(1);
|
||
|
||
// 计算延误天数
|
||
long delayDays = java.time.temporal.ChronoUnit.DAYS.between(
|
||
task.getDeadline(), task.getCompleteTime());
|
||
task.setDelayDays((int) delayDays);
|
||
}
|
||
|
||
// 计算延误扣分(移到外层作用域)
|
||
DelayPenaltyRecord delayRecord = null;
|
||
if (task.getCompleteTime() != null && task.getDeadline() != null
|
||
&& task.getCompleteTime().isAfter(task.getDeadline())
|
||
&& task.getReceiverId() != null) {
|
||
delayRecord = delayPenaltyService.calculateDelayPenalty(
|
||
taskId,
|
||
task.getReceiverId(),
|
||
task.getDeadline(),
|
||
task.getCompleteTime()
|
||
);
|
||
|
||
if (delayRecord != null) {
|
||
// 保存延误扣分记录
|
||
delayPenaltyService.saveDelayPenaltyRecord(delayRecord);
|
||
log.info("任务延误扣分: taskId={}, designerId={}, penaltyScore={}",
|
||
taskId, task.getReceiverId(), delayRecord.getPenaltyScore());
|
||
}
|
||
}
|
||
|
||
// 奖励设计师所在部门积分(任务奖励)
|
||
if (task.getReceiverDeptId() != null && task.getScore() != null) {
|
||
teamScorePoolService.addScore(task.getReceiverDeptId(), task.getScore());
|
||
|
||
// 如果有延误扣分,从积分池中扣除
|
||
if (delayRecord != null && delayRecord.getPenaltyScore() > 0
|
||
&& "confirmed".equals(delayRecord.getStatus())) {
|
||
try {
|
||
teamScorePoolService.deductScore(task.getReceiverDeptId(), delayRecord.getPenaltyScore());
|
||
log.info("已扣除延误扣分: deptId={}, penaltyScore={}",
|
||
task.getReceiverDeptId(), delayRecord.getPenaltyScore());
|
||
} catch (Exception e) {
|
||
log.error("扣除延误扣分失败: {}", e.getMessage());
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
// 审核驳回
|
||
task.setStatus("rejected");
|
||
task.setRejectReason(comment);
|
||
// 驳回后设计师可以重新提交,无需更改积分
|
||
}
|
||
|
||
taskMapper.updateById(task);
|
||
|
||
// 发送通知给设计师
|
||
if (task.getReceiverId() != null) {
|
||
try {
|
||
if (approved) {
|
||
// 审核通过通知
|
||
notificationService.sendTaskNotification(
|
||
task.getReceiverId(),
|
||
taskId,
|
||
"任务审核通过",
|
||
String.format("恭喜!您完成的任务【%s】已审核通过,积分已发放", task.getTitle())
|
||
);
|
||
} else {
|
||
// 驳回通知
|
||
notificationService.sendTaskNotification(
|
||
task.getReceiverId(),
|
||
taskId,
|
||
"任务被驳回",
|
||
String.format("您提交的任务【%s】被驳回,原因:%s,请修改后重新提交", task.getTitle(), comment)
|
||
);
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("发送任务审核通知失败", e);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 取消任务
|
||
*/
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public void cancelTask(Long taskId, Long userId, String reason) {
|
||
log.info("用户 {} 取消任务 {}", userId, taskId);
|
||
|
||
Task task = getTaskById(taskId);
|
||
|
||
// 验证权限(必须是任务发布人)
|
||
if (!userId.equals(task.getPublisherId())) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "只有任务发布人可以取消");
|
||
}
|
||
|
||
// 验证任务状态(不能是已完成)
|
||
if ("completed".equals(task.getStatus())) {
|
||
throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "已完成的任务不能取消");
|
||
}
|
||
|
||
// 更新任务状态
|
||
task.setStatus("cancelled");
|
||
task.setRejectReason(reason);
|
||
|
||
taskMapper.updateById(task);
|
||
|
||
// 返还积分(任务取消时返还给发布部门)
|
||
if (task.getPublisherDeptId() != null && task.getScore() != null) {
|
||
teamScorePoolService.addScore(task.getPublisherDeptId(), task.getScore());
|
||
}
|
||
|
||
// 发送通知给接单设计师
|
||
if (task.getReceiverId() != null) {
|
||
try {
|
||
notificationService.sendTaskNotification(
|
||
task.getReceiverId(),
|
||
taskId,
|
||
"任务已取消",
|
||
String.format("任务【%s】已被发起人取消,原因:%s", task.getTitle(), reason != null ? reason : "无")
|
||
);
|
||
} catch (Exception e) {
|
||
log.error("发送任务取消通知失败", e);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查用户是否是管理员(ADMIN或SUPER_ADMIN)
|
||
*/
|
||
private boolean isAdminUser(Long userId) {
|
||
try {
|
||
// 首先检查用户的 isAdmin 字段
|
||
User user = userService.getUserById(userId);
|
||
if (user != null && Integer.valueOf(1).equals(user.getIsAdmin())) {
|
||
return true;
|
||
}
|
||
|
||
// 其次检查角色
|
||
var roles = roleService.getRolesByUserId(userId);
|
||
return roles.stream().anyMatch(role ->
|
||
"ADMIN".equals(role.getRoleCode()) ||
|
||
"SUPER_ADMIN".equals(role.getRoleCode())
|
||
);
|
||
} catch (Exception e) {
|
||
log.warn("检查用户角色失败: userId={}, error={}", userId, e.getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查用户是否是部门负责人
|
||
*/
|
||
private boolean isDeptHeadUser(Long userId) {
|
||
try {
|
||
User user = userService.getUserById(userId);
|
||
if (user == null || user.getDeptId() == null) {
|
||
return false;
|
||
}
|
||
|
||
Department dept = departmentService.getDepartmentById(user.getDeptId());
|
||
if (dept == null) {
|
||
return false;
|
||
}
|
||
|
||
// 检查是否是部门负责人
|
||
return userId.equals(dept.getLeaderId());
|
||
} catch (Exception e) {
|
||
log.warn("检查部门负责人失败: userId={}, error={}", userId, e.getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private void validateAllowedDemandTypes(Department department, Long demandTypeId) {
|
||
if (department == null || !StringUtils.hasText(department.getAllowedDemandTypes()) || demandTypeId == null) {
|
||
return;
|
||
}
|
||
Set<Long> allowed = Arrays.stream(department.getAllowedDemandTypes().split(","))
|
||
.map(String::trim)
|
||
.filter(str -> !str.isEmpty())
|
||
.map(Long::valueOf)
|
||
.collect(Collectors.toSet());
|
||
if (!allowed.isEmpty() && !allowed.contains(demandTypeId)) {
|
||
throw new BusinessException(ResultCode.FORBIDDEN, "您所在部门不支持该需求类型的任务");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 提交任务审核
|
||
*/
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public boolean submitTaskForAudit(Long taskId, Long userId) {
|
||
log.info("提交任务审核: taskId={}, userId={}", taskId, userId);
|
||
|
||
Task task = taskMapper.selectById(taskId);
|
||
if (task == null) {
|
||
throw new BusinessException("任务不存在");
|
||
}
|
||
|
||
// 检查是否是任务发布人
|
||
if (!task.getPublisherId().equals(userId)) {
|
||
throw new BusinessException("只有任务发布人才能提交审核");
|
||
}
|
||
|
||
// 检查任务状态
|
||
if (!"draft".equals(task.getAuditStatus())) {
|
||
throw new BusinessException("只有草稿状态的任务才能提交审核");
|
||
}
|
||
|
||
// 更新审核状态
|
||
task.setAuditStatus("pending_audit");
|
||
taskMapper.updateById(task);
|
||
|
||
// 记录审核日志
|
||
TaskAuditLog auditLog = new TaskAuditLog();
|
||
auditLog.setTaskId(taskId);
|
||
auditLog.setAuditorId(userId);
|
||
auditLog.setAction("submit");
|
||
auditLog.setComment("提交审核");
|
||
taskAuditLogMapper.insert(auditLog);
|
||
|
||
// 发送通知给部门负责人
|
||
Department department = departmentService.getDepartmentById(task.getPublisherDeptId());
|
||
if (department != null && department.getLeaderId() != null) {
|
||
User publisher = userService.getUserById(userId);
|
||
String publisherName = publisher != null ?
|
||
(publisher.getRealName() != null ? publisher.getRealName() : publisher.getUsername())
|
||
: "未知用户";
|
||
|
||
notificationService.sendTaskNotification(
|
||
department.getLeaderId(),
|
||
taskId,
|
||
"【待审核】新任务提交审核",
|
||
String.format("%s 提交了任务《%s》,请及时审核", publisherName, task.getTitle())
|
||
);
|
||
log.info("已发送审核通知给负责人: leaderId={}, taskId={}", department.getLeaderId(), taskId);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 审核任务
|
||
*/
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public boolean auditTask(TaskAuditRequest request, Long auditorId) {
|
||
log.info("审核任务: taskId={}, result={}, auditorId={}",
|
||
request.getTaskId(), request.getResult(), auditorId);
|
||
|
||
Task task = taskMapper.selectById(request.getTaskId());
|
||
if (task == null) {
|
||
throw new BusinessException("任务不存在");
|
||
}
|
||
|
||
// 检查是否是部门负责人
|
||
User auditor = userService.getUserById(auditorId);
|
||
if (auditor == null) {
|
||
throw new BusinessException("用户不存在");
|
||
}
|
||
|
||
Department publisherDept = departmentService.getDepartmentById(task.getPublisherDeptId());
|
||
if (publisherDept == null || !auditorId.equals(publisherDept.getLeaderId())) {
|
||
throw new BusinessException("只有发起部门的负责人才能审核任务");
|
||
}
|
||
|
||
// 检查任务状态
|
||
if (!"pending_audit".equals(task.getAuditStatus())) {
|
||
throw new BusinessException("只有待审核状态的任务才能审核");
|
||
}
|
||
|
||
// 更新审核状态
|
||
if ("pass".equals(request.getResult())) {
|
||
task.setAuditStatus("audit_pass");
|
||
task.setStatus("pending"); // 审核通过后变为待接单状态
|
||
|
||
// 审核通过后扣除部门积分
|
||
if (task.getPublisherDeptId() != null && task.getScore() != null) {
|
||
// 检查积分是否充足
|
||
if (!teamScorePoolService.checkBalance(task.getPublisherDeptId(), task.getScore())) {
|
||
throw new BusinessException("部门积分余额不足,无法通过审核");
|
||
}
|
||
|
||
// 扣除积分
|
||
teamScorePoolService.consumeScore(task.getPublisherDeptId(), task.getScore());
|
||
log.info("审核通过,扣除部门积分: deptId={}, score={}", task.getPublisherDeptId(), task.getScore());
|
||
}
|
||
} else if ("reject".equals(request.getResult())) {
|
||
task.setAuditStatus("audit_reject");
|
||
task.setStatus("draft"); // 审核驳回后回到草稿状态
|
||
} else {
|
||
throw new BusinessException("审核结果参数错误");
|
||
}
|
||
|
||
task.setAuditorId(auditorId);
|
||
task.setAuditTime(LocalDateTime.now());
|
||
task.setAuditComment(request.getComment());
|
||
taskMapper.updateById(task);
|
||
|
||
// 记录审核日志
|
||
TaskAuditLog auditLog = new TaskAuditLog();
|
||
auditLog.setTaskId(task.getId());
|
||
auditLog.setAuditorId(auditorId);
|
||
auditLog.setAuditorName(auditor.getRealName() != null ? auditor.getRealName() : auditor.getUsername());
|
||
auditLog.setAction(request.getResult());
|
||
auditLog.setComment(request.getComment());
|
||
taskAuditLogMapper.insert(auditLog);
|
||
|
||
// 发送审核结果通知给任务发起人
|
||
if (task.getPublisherId() != null) {
|
||
String auditorName = auditor.getRealName() != null ? auditor.getRealName() : auditor.getUsername();
|
||
String resultText = "pass".equals(request.getResult()) ? "通过" : "驳回";
|
||
String title = String.format("【审核%s】任务《%s》", resultText, task.getTitle());
|
||
String content;
|
||
|
||
if ("pass".equals(request.getResult())) {
|
||
content = String.format("%s 审核通过了您的任务《%s》,任务已发布到任务大厅",
|
||
auditorName, task.getTitle());
|
||
if (request.getComment() != null && !request.getComment().isEmpty()) {
|
||
content += "。审核意见:" + request.getComment();
|
||
}
|
||
} else {
|
||
content = String.format("%s 驳回了您的任务《%s》", auditorName, task.getTitle());
|
||
if (request.getComment() != null && !request.getComment().isEmpty()) {
|
||
content += "。驳回原因:" + request.getComment();
|
||
}
|
||
}
|
||
|
||
notificationService.sendTaskNotification(
|
||
task.getPublisherId(),
|
||
task.getId(),
|
||
title,
|
||
content
|
||
);
|
||
log.info("已发送审核结果通知: publisherId={}, result={}, taskId={}",
|
||
task.getPublisherId(), request.getResult(), task.getId());
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 强制指派任务给部门成员
|
||
*/
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public boolean assignTask(TaskAssignRequest request, Long assignerId) {
|
||
log.info("强制指派任务: taskId={}, assigneeId={}, assignerId={}",
|
||
request.getTaskId(), request.getAssigneeId(), assignerId);
|
||
|
||
Task task = taskMapper.selectById(request.getTaskId());
|
||
if (task == null) {
|
||
throw new BusinessException("任务不存在");
|
||
}
|
||
|
||
// 检查任务状态(只有待接单或招募中的任务可以指派)
|
||
if (!"pending".equals(task.getStatus())) {
|
||
throw new BusinessException("只有待接单状态的任务才能指派");
|
||
}
|
||
|
||
// 检查是否已有人接单
|
||
if (task.getReceiverId() != null) {
|
||
throw new BusinessException("该任务已被接单");
|
||
}
|
||
|
||
// 检查指派人是否是部门负责人
|
||
User assigner = userService.getUserById(assignerId);
|
||
if (assigner == null || assigner.getDeptId() == null) {
|
||
throw new BusinessException("用户不存在或未分配部门");
|
||
}
|
||
|
||
Department assignerDept = departmentService.getDepartmentById(assigner.getDeptId());
|
||
if (assignerDept == null || !assignerId.equals(assignerDept.getLeaderId())) {
|
||
throw new BusinessException("只有部门负责人才能指派任务");
|
||
}
|
||
|
||
// 检查被指派人是否在该部门
|
||
User assignee = userService.getUserById(request.getAssigneeId());
|
||
if (assignee == null || !assigner.getDeptId().equals(assignee.getDeptId())) {
|
||
throw new BusinessException("只能指派给本部门成员");
|
||
}
|
||
|
||
// 更新任务
|
||
task.setReceiverId(request.getAssigneeId());
|
||
task.setReceiverDeptId(assignee.getDeptId());
|
||
task.setStatus("in_progress");
|
||
task.setStartTime(LocalDateTime.now());
|
||
taskMapper.updateById(task);
|
||
|
||
// 记录指派日志
|
||
TaskAssignLog log = new TaskAssignLog();
|
||
log.setTaskId(task.getId());
|
||
log.setAssignerId(assignerId);
|
||
log.setAssigneeId(request.getAssigneeId());
|
||
log.setAssignType(request.getAssignType());
|
||
log.setComment(request.getComment());
|
||
taskAssignLogMapper.insert(log);
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 获取部门负责人的任务列表(需求部门负责人视图)
|
||
*/
|
||
public IPage<Task> getLeaderTaskList(Long userId, Integer page, Integer pageSize, String auditStatus) {
|
||
log.info("查询负责人任务列表: userId={}, auditStatus={}", userId, auditStatus);
|
||
|
||
User user = userService.getUserById(userId);
|
||
if (user == null || user.getDeptId() == null) {
|
||
throw new BusinessException("用户不存在或未分配部门");
|
||
}
|
||
|
||
Department dept = departmentService.getDepartmentById(user.getDeptId());
|
||
if (dept == null) {
|
||
throw new BusinessException("部门不存在");
|
||
}
|
||
|
||
// 检查是否是部门负责人
|
||
if (!userId.equals(dept.getLeaderId())) {
|
||
throw new BusinessException("只有部门负责人才能查看该视图");
|
||
}
|
||
|
||
// 查询部门内所有任务
|
||
Page<Task> pageParam = new Page<>(page, pageSize);
|
||
return taskMapper.selectLeaderTaskPage(pageParam, user.getDeptId(), auditStatus);
|
||
}
|
||
|
||
/**
|
||
* 获取接单部门负责人的接单明细(接单部门负责人视图)
|
||
*/
|
||
public IPage<Task> getLeaderReceivingTaskList(Long userId, Integer page, Integer pageSize, String status) {
|
||
log.info("查询接单部门负责人视图: userId={}, status={}", userId, status);
|
||
|
||
User user = userService.getUserById(userId);
|
||
if (user == null || user.getDeptId() == null) {
|
||
throw new BusinessException("用户不存在或未分配部门");
|
||
}
|
||
|
||
Department dept = departmentService.getDepartmentById(user.getDeptId());
|
||
if (dept == null) {
|
||
throw new BusinessException("部门不存在");
|
||
}
|
||
|
||
// 检查是否是部门负责人
|
||
if (!userId.equals(dept.getLeaderId())) {
|
||
throw new BusinessException("只有部门负责人才能查看该视图");
|
||
}
|
||
|
||
// 查询部门成员接单的所有任务
|
||
Page<Task> pageParam = new Page<>(page, pageSize);
|
||
return taskMapper.selectDeptReceivingTaskPage(pageParam, user.getDeptId(), status);
|
||
}
|
||
|
||
/**
|
||
* 获取工作台统计数据
|
||
*/
|
||
public DashboardStatsVO getDashboardStats(Long userId, String month) {
|
||
try {
|
||
// 获取用户角色,判断是否为管理员
|
||
List<Role> roles = roleService.getRolesByUserId(userId);
|
||
boolean isAdmin = roles.stream()
|
||
.anyMatch(role -> "ADMIN".equals(role.getRoleCode()) || "SUPER_ADMIN".equals(role.getRoleCode()));
|
||
|
||
log.info("获取工作台统计数据: userId={}, isAdmin={}, month={}", userId, isAdmin, month);
|
||
|
||
// 解析月份,如果不传则使用当前月份
|
||
LocalDate targetMonth;
|
||
if (month != null && !month.isEmpty()) {
|
||
try {
|
||
targetMonth = LocalDate.parse(month + "-01", java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||
} catch (Exception e) {
|
||
log.warn("月份格式错误: {}, 使用当前月份", month);
|
||
targetMonth = LocalDate.now().withDayOfMonth(1);
|
||
}
|
||
} else {
|
||
targetMonth = LocalDate.now().withDayOfMonth(1);
|
||
}
|
||
|
||
// 计算月份的开始和结束时间
|
||
LocalDateTime monthStart = LocalDateTime.of(targetMonth, LocalTime.MIN);
|
||
LocalDateTime monthEnd = monthStart.plusMonths(1).minusSeconds(1);
|
||
|
||
// 当前日期范围
|
||
LocalDateTime todayStart = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
|
||
LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
|
||
|
||
// 本周范围
|
||
LocalDate now = LocalDate.now();
|
||
LocalDate weekStart = now.minusDays(now.getDayOfWeek().getValue() - 1);
|
||
LocalDate weekEnd = weekStart.plusDays(6);
|
||
LocalDateTime weekStartTime = LocalDateTime.of(weekStart, LocalTime.MIN);
|
||
LocalDateTime weekEndTime = LocalDateTime.of(weekEnd, LocalTime.MAX);
|
||
|
||
// 上周范围
|
||
LocalDate lastWeekStart = weekStart.minusDays(7);
|
||
LocalDate lastWeekEnd = lastWeekStart.plusDays(6);
|
||
LocalDateTime lastWeekStartTime = LocalDateTime.of(lastWeekStart, LocalTime.MIN);
|
||
LocalDateTime lastWeekEndTime = LocalDateTime.of(lastWeekEnd, LocalTime.MAX);
|
||
|
||
// 进行中的任务数
|
||
LambdaQueryWrapper<Task> inProgressQuery = new LambdaQueryWrapper<Task>()
|
||
.eq(Task::getStatus, "in_progress");
|
||
if (!isAdmin) {
|
||
inProgressQuery.eq(Task::getReceiverId, userId);
|
||
}
|
||
Long inProgressCount = taskMapper.selectCount(inProgressQuery);
|
||
|
||
// 已完成的任务数
|
||
LambdaQueryWrapper<Task> completedQuery = new LambdaQueryWrapper<Task>()
|
||
.eq(Task::getStatus, "completed");
|
||
if (!isAdmin) {
|
||
completedQuery.eq(Task::getReceiverId, userId);
|
||
}
|
||
Long completedCount = taskMapper.selectCount(completedQuery);
|
||
|
||
// 待审核的任务数
|
||
LambdaQueryWrapper<Task> reviewQuery = new LambdaQueryWrapper<Task>()
|
||
.eq(Task::getStatus, "review");
|
||
if (!isAdmin) {
|
||
reviewQuery.eq(Task::getReceiverId, userId);
|
||
}
|
||
Long reviewCount = taskMapper.selectCount(reviewQuery);
|
||
|
||
// 今日开始的任务数
|
||
LambdaQueryWrapper<Task> todayStartedQuery = new LambdaQueryWrapper<Task>()
|
||
.eq(Task::getStatus, "in_progress")
|
||
.ge(Task::getStartTime, todayStart)
|
||
.le(Task::getStartTime, todayEnd);
|
||
if (!isAdmin) {
|
||
todayStartedQuery.eq(Task::getReceiverId, userId);
|
||
}
|
||
Long todayStartedCount = taskMapper.selectCount(todayStartedQuery);
|
||
|
||
// 今日完成的任务数
|
||
LambdaQueryWrapper<Task> todayCompletedQuery = new LambdaQueryWrapper<Task>()
|
||
.eq(Task::getStatus, "completed")
|
||
.ge(Task::getCompleteTime, todayStart)
|
||
.le(Task::getCompleteTime, todayEnd);
|
||
if (!isAdmin) {
|
||
todayCompletedQuery.eq(Task::getReceiverId, userId);
|
||
}
|
||
Long todayCompletedCount = taskMapper.selectCount(todayCompletedQuery);
|
||
|
||
// 本周完成的任务数
|
||
LambdaQueryWrapper<Task> weekCompletedQuery = new LambdaQueryWrapper<Task>()
|
||
.eq(Task::getStatus, "completed")
|
||
.ge(Task::getCompleteTime, weekStartTime)
|
||
.le(Task::getCompleteTime, weekEndTime);
|
||
if (!isAdmin) {
|
||
weekCompletedQuery.eq(Task::getReceiverId, userId);
|
||
}
|
||
Long weekCompletedCount = taskMapper.selectCount(weekCompletedQuery);
|
||
|
||
// 上周完成的任务数
|
||
LambdaQueryWrapper<Task> lastWeekCompletedQuery = new LambdaQueryWrapper<Task>()
|
||
.eq(Task::getStatus, "completed")
|
||
.ge(Task::getCompleteTime, lastWeekStartTime)
|
||
.le(Task::getCompleteTime, lastWeekEndTime);
|
||
if (!isAdmin) {
|
||
lastWeekCompletedQuery.eq(Task::getReceiverId, userId);
|
||
}
|
||
Long lastWeekCompletedCount = taskMapper.selectCount(lastWeekCompletedQuery);
|
||
|
||
// 可接任务数(状态为pending的任务)
|
||
LambdaQueryWrapper<Task> availableQuery = new LambdaQueryWrapper<Task>()
|
||
.eq(Task::getStatus, "pending");
|
||
// 可接任务对所有人都是全局的,不需要过滤用户
|
||
Long availableCount = taskMapper.selectCount(availableQuery);
|
||
|
||
// 当日新增任务数
|
||
LambdaQueryWrapper<Task> todayNewQuery = new LambdaQueryWrapper<Task>()
|
||
.ge(Task::getCreateTime, todayStart)
|
||
.le(Task::getCreateTime, todayEnd);
|
||
if (!isAdmin) {
|
||
// 普通用户看自己相关的任务(作为发起人或接单人)
|
||
todayNewQuery.and(wrapper -> wrapper
|
||
.eq(Task::getPublisherId, userId)
|
||
.or()
|
||
.eq(Task::getReceiverId, userId));
|
||
}
|
||
Long todayNewCount = taskMapper.selectCount(todayNewQuery);
|
||
|
||
// 获取总积分
|
||
LambdaQueryWrapper<Task> scoreQuery = new LambdaQueryWrapper<Task>()
|
||
.eq(Task::getStatus, "completed")
|
||
.select(Task::getScoreReward);
|
||
if (!isAdmin) {
|
||
scoreQuery.eq(Task::getReceiverId, userId);
|
||
}
|
||
List<Task> completedTasks = taskMapper.selectList(scoreQuery);
|
||
|
||
int totalScore = completedTasks.stream()
|
||
.filter(t -> t.getScoreReward() != null)
|
||
.mapToInt(t -> t.getScoreReward().intValue())
|
||
.sum();
|
||
|
||
// 获取当前用户当月发布的任务数
|
||
LambdaQueryWrapper<Task> publishedQuery = new LambdaQueryWrapper<Task>()
|
||
.eq(Task::getPublisherId, userId)
|
||
.ge(Task::getCreateTime, monthStart)
|
||
.le(Task::getCreateTime, monthEnd)
|
||
.eq(Task::getDeleted, 0);
|
||
Long publishedCount = taskMapper.selectCount(publishedQuery);
|
||
|
||
DashboardStatsVO result = DashboardStatsVO.builder()
|
||
.availableCount(availableCount.intValue())
|
||
.publishedCount(publishedCount.intValue())
|
||
.inProgressCount(inProgressCount.intValue())
|
||
.completedCount(completedCount.intValue())
|
||
.reviewCount(reviewCount.intValue())
|
||
.todayNewCount(todayNewCount.intValue())
|
||
.totalScore(totalScore)
|
||
.todayStartedCount(todayStartedCount.intValue())
|
||
.todayCompletedCount(todayCompletedCount.intValue())
|
||
.weekCompletedCount(weekCompletedCount.intValue())
|
||
.lastWeekCompletedCount(lastWeekCompletedCount.intValue())
|
||
.build();
|
||
|
||
log.info("工作台统计数据查询完成: userId={}, isAdmin={}, result={}", userId, isAdmin, result);
|
||
return result;
|
||
|
||
} catch (Exception e) {
|
||
log.error("获取工作台统计数据失败: userId={}", userId, e);
|
||
// 返回空数据而不是抛异常
|
||
return DashboardStatsVO.builder()
|
||
.availableCount(0)
|
||
.publishedCount(0)
|
||
.inProgressCount(0)
|
||
.completedCount(0)
|
||
.reviewCount(0)
|
||
.todayNewCount(0)
|
||
.totalScore(0)
|
||
.todayStartedCount(0)
|
||
.todayCompletedCount(0)
|
||
.weekCompletedCount(0)
|
||
.lastWeekCompletedCount(0)
|
||
.build();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取我的进行中任务列表
|
||
*/
|
||
public List<Task> getMyInProgressTasks(Long userId) {
|
||
return taskMapper.selectList(
|
||
new LambdaQueryWrapper<Task>()
|
||
.eq(Task::getReceiverId, userId)
|
||
.eq(Task::getStatus, "in_progress")
|
||
.orderByDesc(Task::getStartTime)
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 批量更新任务进度
|
||
*/
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public void batchUpdateProgress(Long userId, List<java.util.Map<String, Object>> updates) {
|
||
for (java.util.Map<String, Object> update : updates) {
|
||
Long taskId = ((Number) update.get("taskId")).longValue();
|
||
Integer progress = ((Number) update.get("progress")).intValue();
|
||
String remark = (String) update.get("remark");
|
||
|
||
// 验证任务归属
|
||
Task task = taskMapper.selectById(taskId);
|
||
if (task == null) {
|
||
throw new BusinessException("任务不存在: taskId=" + taskId);
|
||
}
|
||
|
||
if (!task.getReceiverId().equals(userId)) {
|
||
throw new BusinessException("无权更新此任务进度: taskId=" + taskId);
|
||
}
|
||
|
||
if (!"in_progress".equals(task.getStatus())) {
|
||
throw new BusinessException("只能更新进行中的任务进度: taskId=" + taskId);
|
||
}
|
||
|
||
// 验证进度值
|
||
if (progress < 0 || progress > 100) {
|
||
throw new BusinessException("进度值必须在0-100之间: taskId=" + taskId);
|
||
}
|
||
|
||
Integer oldProgress = task.getProgress() != null ? task.getProgress() : 0;
|
||
if (progress < oldProgress) {
|
||
throw new BusinessException("新进度不能小于当前进度: taskId=" + taskId);
|
||
}
|
||
|
||
// 更新进度
|
||
Task updateTask = new Task();
|
||
updateTask.setId(taskId);
|
||
updateTask.setProgress(progress);
|
||
updateTask.setUpdateTime(LocalDateTime.now());
|
||
|
||
// 如果进度达到100%,自动标记为待审核
|
||
if (progress == 100) {
|
||
updateTask.setStatus("pending_review");
|
||
}
|
||
|
||
taskMapper.updateById(updateTask);
|
||
|
||
log.info("任务进度更新成功: taskId={}, userId={}, oldProgress={}, newProgress={}, remark={}",
|
||
taskId, userId, oldProgress, progress, remark);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新任务进度
|
||
*/
|
||
@Transactional
|
||
public void updateTaskProgress(Long taskId, Long userId, Integer progress) {
|
||
log.info("更新任务进度: taskId={}, userId={}, progress={}", taskId, userId, progress);
|
||
|
||
// 验证进度值
|
||
if (progress < 0 || progress > 100) {
|
||
throw new BusinessException("进度值必须在0-100之间");
|
||
}
|
||
|
||
Task task = taskMapper.selectById(taskId);
|
||
if (task == null) {
|
||
throw new BusinessException("任务不存在");
|
||
}
|
||
|
||
// 验证是接单人
|
||
if (!userId.equals(task.getReceiverId())) {
|
||
throw new BusinessException("只有接单人才能更新任务进度");
|
||
}
|
||
|
||
// 验证任务状态(只有进行中的任务才能更新进度)
|
||
if (!"in_progress".equals(task.getStatus())) {
|
||
throw new BusinessException("只有进行中的任务才能更新进度");
|
||
}
|
||
|
||
// 更新进度
|
||
task.setProgress(progress);
|
||
task.setUpdateTime(LocalDateTime.now());
|
||
taskMapper.updateById(task);
|
||
|
||
log.info("任务进度更新成功: taskId={}, progress={}", taskId, progress);
|
||
}
|
||
|
||
/**
|
||
* 完结任务(提交交付路径)
|
||
*/
|
||
@Transactional
|
||
public void completeTask(Long taskId, Long userId, String deliveryPath) {
|
||
log.info("完结任务: taskId={}, userId={}, deliveryPath={}", taskId, userId, deliveryPath);
|
||
|
||
Task task = taskMapper.selectById(taskId);
|
||
if (task == null) {
|
||
throw new BusinessException("任务不存在");
|
||
}
|
||
|
||
// 验证是接单人
|
||
if (!userId.equals(task.getReceiverId())) {
|
||
throw new BusinessException("只有接单人才能完结任务");
|
||
}
|
||
|
||
// 验证任务状态(只有进行中的任务才能完结)
|
||
if (!"in_progress".equals(task.getStatus())) {
|
||
throw new BusinessException("只有进行中的任务才能完结");
|
||
}
|
||
|
||
// 更新任务状态为待审核
|
||
task.setStatus("review");
|
||
task.setDeliveryPath(deliveryPath);
|
||
task.setProgress(100); // 完结时进度设为100%
|
||
task.setSubmitTime(LocalDateTime.now());
|
||
task.setUpdateTime(LocalDateTime.now());
|
||
taskMapper.updateById(task);
|
||
|
||
// 发送通知给发起人
|
||
try {
|
||
Notification notification = new Notification();
|
||
notification.setReceiverId(task.getPublisherId());
|
||
notification.setTitle("任务提交审核");
|
||
notification.setContent("任务【" + task.getTitle() + "】已提交审核,请查看");
|
||
notification.setNotificationType("TASK_REVIEW");
|
||
notification.setRelatedType("task");
|
||
notification.setRelatedId(taskId);
|
||
notification.setPushDingtalk(1); // 推送到钉钉
|
||
notificationService.sendNotification(notification);
|
||
} catch (Exception e) {
|
||
log.error("发送审核通知失败", e);
|
||
}
|
||
|
||
log.info("任务已提交审核: taskId={}", taskId);
|
||
}
|
||
|
||
/**
|
||
* 审核通过任务
|
||
*/
|
||
@Transactional
|
||
public void approveTask(Long taskId, Long userId) {
|
||
log.info("审核通过任务: taskId={}, userId={}", taskId, userId);
|
||
|
||
Task task = taskMapper.selectById(taskId);
|
||
if (task == null) {
|
||
throw new BusinessException("任务不存在");
|
||
}
|
||
|
||
// 验证权限(发起人或部门负责人)
|
||
User user = userService.getUserById(userId);
|
||
if (user == null) {
|
||
throw new BusinessException("用户不存在");
|
||
}
|
||
|
||
boolean isPublisher = userId.equals(task.getPublisherId());
|
||
boolean isDeptLeader = false;
|
||
|
||
if (user.getDeptId() != null) {
|
||
Department dept = departmentService.getDepartmentById(user.getDeptId());
|
||
if (dept != null && userId.equals(dept.getLeaderId())) {
|
||
isDeptLeader = true;
|
||
}
|
||
}
|
||
|
||
if (!isPublisher && !isDeptLeader) {
|
||
throw new BusinessException("只有任务发起人或部门负责人才能审核任务");
|
||
}
|
||
|
||
// 验证任务状态
|
||
if (!"review".equals(task.getStatus())) {
|
||
throw new BusinessException("只有待审核的任务才能审核");
|
||
}
|
||
|
||
// 更新任务状态为已完成
|
||
task.setStatus("completed");
|
||
task.setCompleteTime(LocalDateTime.now());
|
||
task.setUpdateTime(LocalDateTime.now());
|
||
task.setRating(5); // 默认5分
|
||
|
||
// 计算积分奖励
|
||
if (task.getScore() != null) {
|
||
task.setScoreReward(BigDecimal.valueOf(task.getScore()));
|
||
}
|
||
|
||
taskMapper.updateById(task);
|
||
|
||
// 发送通知给接单人
|
||
try {
|
||
Notification notification = new Notification();
|
||
notification.setReceiverId(task.getReceiverId());
|
||
notification.setTitle("任务审核通过");
|
||
notification.setContent("任务【" + task.getTitle() + "】审核通过,积分已到账");
|
||
notification.setNotificationType("TASK_COMPLETED");
|
||
notification.setRelatedType("task");
|
||
notification.setRelatedId(taskId);
|
||
notification.setPushDingtalk(1); // 推送到钉钉
|
||
notificationService.sendNotification(notification);
|
||
} catch (Exception e) {
|
||
log.error("发送完成通知失败", e);
|
||
}
|
||
|
||
log.info("任务审核通过: taskId={}", taskId);
|
||
}
|
||
|
||
/**
|
||
* 驳回任务
|
||
*/
|
||
@Transactional
|
||
public void rejectTask(Long taskId, Long userId, String reason) {
|
||
log.info("驳回任务: taskId={}, userId={}, reason={}", taskId, userId, reason);
|
||
|
||
Task task = taskMapper.selectById(taskId);
|
||
if (task == null) {
|
||
throw new BusinessException("任务不存在");
|
||
}
|
||
|
||
// 验证权限(发起人或部门负责人)
|
||
User user = userService.getUserById(userId);
|
||
if (user == null) {
|
||
throw new BusinessException("用户不存在");
|
||
}
|
||
|
||
boolean isPublisher = userId.equals(task.getPublisherId());
|
||
boolean isDeptLeader = false;
|
||
|
||
if (user.getDeptId() != null) {
|
||
Department dept = departmentService.getDepartmentById(user.getDeptId());
|
||
if (dept != null && userId.equals(dept.getLeaderId())) {
|
||
isDeptLeader = true;
|
||
}
|
||
}
|
||
|
||
if (!isPublisher && !isDeptLeader) {
|
||
throw new BusinessException("只有任务发起人或部门负责人才能驳回任务");
|
||
}
|
||
|
||
// 验证任务状态
|
||
if (!"review".equals(task.getStatus())) {
|
||
throw new BusinessException("只有待审核的任务才能驳回");
|
||
}
|
||
|
||
// 更新任务状态为进行中(驳回后重新修改)
|
||
task.setStatus("in_progress");
|
||
task.setRejectReason(reason);
|
||
task.setUpdateTime(LocalDateTime.now());
|
||
taskMapper.updateById(task);
|
||
|
||
// 发送通知给接单人
|
||
try {
|
||
Notification notification = new Notification();
|
||
notification.setReceiverId(task.getReceiverId());
|
||
notification.setTitle("任务被驳回");
|
||
notification.setContent("任务【" + task.getTitle() + "】被驳回,原因:" + reason);
|
||
notification.setNotificationType("TASK_REJECTED");
|
||
notification.setRelatedType("task");
|
||
notification.setRelatedId(taskId);
|
||
notification.setPriority(2); // 高优先级
|
||
notification.setPushDingtalk(1); // 推送到钉钉
|
||
notificationService.sendNotification(notification);
|
||
} catch (Exception e) {
|
||
log.error("发送驳回通知失败", e);
|
||
}
|
||
|
||
log.info("任务已驳回: taskId={}, reason={}", taskId, reason);
|
||
}
|
||
|
||
/**
|
||
* 初始化审核状态(两级审核流程)
|
||
*/
|
||
private void initApprovalStatus(Task task) {
|
||
try {
|
||
// 设置审核状态为待需求部门审核
|
||
task.setApprovalStatus("pending_demand_approval");
|
||
|
||
// 确定需求部门负责人
|
||
if (task.getPublisherDeptId() != null) {
|
||
Department demandDept = departmentService.getDepartmentById(task.getPublisherDeptId());
|
||
if (demandDept != null && demandDept.getLeaderId() != null) {
|
||
task.setCurrentApproverId(demandDept.getLeaderId());
|
||
task.setCurrentApproverRole("demand_leader");
|
||
log.info("任务已设置待审核状态,等待需求部门负责人审核:leaderId={}", demandDept.getLeaderId());
|
||
} else {
|
||
log.warn("需求部门未配置负责人,任务将直接发布:deptId={}", task.getPublisherDeptId());
|
||
// 如果没有配置负责人,跳过审核直接发布
|
||
task.setApprovalStatus("approved");
|
||
task.setStatus("pending");
|
||
}
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("初始化审核状态失败", e);
|
||
// 失败时跳过审核直接发布
|
||
task.setApprovalStatus("approved");
|
||
task.setStatus("pending");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发送审核通知(两级审核流程)
|
||
*/
|
||
private void sendApprovalNotification(Task task) {
|
||
try {
|
||
if (task.getCurrentApproverId() != null) {
|
||
User approver = userService.getUserById(task.getCurrentApproverId());
|
||
if (approver != null) {
|
||
Notification notification = new Notification();
|
||
notification.setReceiverId(task.getCurrentApproverId());
|
||
notification.setTitle("新任务待审核");
|
||
notification.setContent(String.format("任务【%s】需要您审核", task.getTitle()));
|
||
notification.setNotificationType("TASK_APPROVAL");
|
||
notification.setRelatedType("task");
|
||
notification.setRelatedId(task.getId());
|
||
notification.setPushDingtalk(1); // 推送到钉钉
|
||
notificationService.sendNotification(notification);
|
||
log.info("已发送审核通知给用户:userId={}, taskId={}", task.getCurrentApproverId(), task.getId());
|
||
}
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("发送审核通知失败", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 计算任务总积分(用于审核时修改)
|
||
*/
|
||
public int calculateTotalScore(List<TaskUpdateDTO.TaskTypeUpdateDTO> taskTypes) {
|
||
int totalScore = 0;
|
||
for (TaskUpdateDTO.TaskTypeUpdateDTO taskType : taskTypes) {
|
||
DemandType demandType = configService.getDemandTypeById(taskType.getTaskTypeId());
|
||
if (demandType != null && demandType.getUnitScore() != null) {
|
||
totalScore += demandType.getUnitScore().multiply(BigDecimal.valueOf(taskType.getQuantity())).intValue();
|
||
}
|
||
}
|
||
return totalScore;
|
||
}
|
||
|
||
/**
|
||
* 创建流水线任务的子任务项(用于审核通过后拆分)
|
||
*/
|
||
public void createPipelineTaskItems(Task mainTask, com.designhub.dto.PipelineTaskDTO dto) {
|
||
// 委托给已有的createPipelineTask逻辑中的子任务创建部分
|
||
// 这里简化处理,实际应该提取createPipelineTask中的子任务创建逻辑
|
||
log.info("开始拆分流水线子任务:taskId={}, mode={}", mainTask.getId(), dto.getPipelineMode());
|
||
|
||
// TODO: 实现流水线子任务拆分逻辑
|
||
// 可以参考createPipelineTask方法中的实现
|
||
}
|
||
}
|
||
|