366 lines
15 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.entity.Task;
import com.designhub.entity.TaskItem;
import com.designhub.entity.Notification;
import com.designhub.exception.BusinessException;
import com.designhub.mapper.TaskItemMapper;
import com.designhub.mapper.TaskMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 任务子项服务类(流水线任务)
*/
@Service
@RequiredArgsConstructor
public class TaskItemService {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TaskItemService.class);
private final TaskItemMapper taskItemMapper;
private final TaskMapper taskMapper;
private final UserService userService;
private final NotificationService notificationService;
/**
* 获取可接单的子任务列表
*/
public IPage<TaskItem> getAvailableItems(Integer page, Integer pageSize, Long taskTypeId, String keyword) {
Page<TaskItem> pageParam = new Page<>(page, pageSize);
return taskItemMapper.selectAvailableItems(pageParam, taskTypeId, keyword);
}
/**
* 获取进行中的子任务列表
*/
public IPage<TaskItem> getInProgressItems(Integer page, Integer pageSize, Long receiverId) {
Page<TaskItem> pageParam = new Page<>(page, pageSize);
return taskItemMapper.selectInProgressItems(pageParam, receiverId);
}
/**
* 获取已完成的子任务列表
*/
public IPage<TaskItem> getCompletedItems(Integer page, Integer pageSize, Long receiverId) {
Page<TaskItem> pageParam = new Page<>(page, pageSize);
return taskItemMapper.selectCompletedItems(pageParam, receiverId);
}
/**
* 根据任务ID获取所有子任务
*/
public List<TaskItem> getItemsByTaskId(Long taskId) {
return taskItemMapper.selectByTaskId(taskId);
}
/**
* 获取子任务详情
*/
public TaskItem getItemById(Long itemId) {
TaskItem item = taskItemMapper.selectById(itemId);
if (item == null) {
throw new BusinessException(ResultCode.NOT_FOUND, "子任务不存在");
}
// 加载主任务信息
Task mainTask = taskMapper.selectById(item.getTaskId());
if (mainTask != null) {
item.setMainTaskTitle(mainTask.getTitle());
item.setPipelineMode(mainTask.getPipelineMode());
item.setBrandName(mainTask.getBrandName());
item.setCategoryName(mainTask.getCategoryName());
item.setPlatformName(mainTask.getPlatformName());
item.setPublisherName(mainTask.getPublisherName());
item.setSpuInfo(mainTask.getSpuInfo());
item.setTaskSubType(mainTask.getTaskSubType());
item.setAttachmentUrl(mainTask.getAttachmentUrl());
}
return item;
}
/**
* 抢单子任务
*/
@Transactional(rollbackFor = Exception.class)
public void grabItem(Long itemId, Long userId) {
log.info("设计师 {} 抢单子任务 {}", userId, itemId);
// 获取用户信息
var user = userService.getUserById(userId);
if (user == null) {
throw new BusinessException(ResultCode.FORBIDDEN, "用户不存在");
}
// 获取子任务信息
TaskItem item = taskItemMapper.selectById(itemId);
if (item == null) {
throw new BusinessException(ResultCode.NOT_FOUND, "子任务不存在");
}
// 验证状态
if (!"pending".equals(item.getStatus())) {
throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "该子任务不可接单");
}
if (item.getIsLocked() != null && item.getIsLocked() == 1) {
throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "该子任务已被锁定,请等待前序任务完成");
}
if (item.getReceiverId() != null) {
throw new BusinessException(ResultCode.TASK_ALREADY_TAKEN, "该子任务已被接单");
}
// 乐观锁抢单
int result = taskItemMapper.grabItem(itemId, userId, user.getRealName());
if (result == 0) {
throw new BusinessException(ResultCode.TASK_ALREADY_TAKEN, "抢单失败,该子任务可能已被他人接单");
}
log.info("设计师 {} 成功抢单子任务 {}", userId, itemId);
// 发送通知给任务发布者
Task mainTask = taskMapper.selectById(item.getTaskId());
if (mainTask != null && mainTask.getPublisherId() != null) {
Notification notification = new Notification();
notification.setReceiverId(mainTask.getPublisherId());
notification.setNotificationType("task");
notification.setTitle("子任务已被接单");
notification.setContent(user.getRealName() + " 接单了子任务:" + item.getItemNo());
notification.setRelatedId(itemId);
notification.setRelatedType("task_item");
notification.setPriority(1); // 普通
notificationService.sendNotification(notification);
}
}
/**
* 更新子任务进度
*/
@Transactional(rollbackFor = Exception.class)
public void updateProgress(Long itemId, Long userId, Integer progress) {
log.info("设计师 {} 更新子任务 {} 进度为 {}%", userId, itemId, progress);
TaskItem item = taskItemMapper.selectById(itemId);
if (item == null) {
throw new BusinessException(ResultCode.NOT_FOUND, "子任务不存在");
}
// 验证权限
if (!userId.equals(item.getReceiverId())) {
throw new BusinessException(ResultCode.FORBIDDEN, "无权更新该子任务");
}
// 验证状态
if (!"in_progress".equals(item.getStatus())) {
throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "只能更新进行中的子任务");
}
// 验证进度值
if (progress < 0 || progress > 100) {
throw new BusinessException(ResultCode.PARAM_ERROR, "进度值必须在0-100之间");
}
// 更新进度
item.setProgress(progress);
item.setUpdateTime(LocalDateTime.now());
taskItemMapper.updateById(item);
log.info("子任务 {} 进度已更新为 {}%", itemId, progress);
}
/**
* 完结子任务(关键方法:包含自动解锁逻辑)
*/
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> completeItem(Long itemId, Long userId, String deliveryPath) {
log.info("设计师 {} 完结子任务 {}", userId, itemId);
// 1. 获取子任务信息
TaskItem item = taskItemMapper.selectById(itemId);
if (item == null) {
throw new BusinessException(ResultCode.NOT_FOUND, "子任务不存在");
}
// 2. 验证权限
if (!userId.equals(item.getReceiverId())) {
throw new BusinessException(ResultCode.FORBIDDEN, "无权完结该子任务");
}
// 3. 验证状态
if (!"in_progress".equals(item.getStatus())) {
throw new BusinessException(ResultCode.TASK_STATUS_ERROR, "只能完结进行中的子任务");
}
// 4. 更新子任务状态
item.setStatus("completed");
item.setProgress(100);
item.setDeliveryPath(deliveryPath);
item.setCompleteTime(LocalDateTime.now());
item.setUpdateTime(LocalDateTime.now());
taskItemMapper.updateById(item);
log.info("子任务 {} 已完成", itemId);
// 5. 设计师立即获得积分(如果需要,可以调用积分服务)
// scoreService.addScore(userId, item.getTotalScore());
Map<String, Object> result = new HashMap<>();
result.put("completedItem", item);
// 6. 🔥 关键步骤:检查是否是顺序流水线,基于group_no解锁下一组
Task mainTask = taskMapper.selectById(item.getTaskId());
if (mainTask != null && "sequential".equals(mainTask.getPipelineMode())) {
// 获取当前子任务的分组号
Integer currentGroupNo = item.getGroupNo();
if (currentGroupNo == null) {
currentGroupNo = 1; // 兼容旧数据
}
// 检查当前组的所有子任务是否都已完成
List<TaskItem> currentGroupItems = taskItemMapper.selectList(
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getTaskId, item.getTaskId())
.eq(TaskItem::getGroupNo, currentGroupNo)
);
boolean allGroupCompleted = currentGroupItems.stream()
.allMatch(i -> "completed".equals(i.getStatus()));
if (allGroupCompleted) {
log.info("✅ 第 {} 组所有子任务已完成,准备解锁下一组", currentGroupNo);
// 解锁下一组的所有子任务
Integer nextGroupNo = currentGroupNo + 1;
List<TaskItem> nextGroupItems = taskItemMapper.selectList(
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getTaskId, item.getTaskId())
.eq(TaskItem::getGroupNo, nextGroupNo)
.eq(TaskItem::getStatus, "locked")
);
if (!nextGroupItems.isEmpty()) {
// 批量解锁下一组的所有子任务
for (TaskItem nextItem : nextGroupItems) {
nextItem.setStatus("pending");
nextItem.setIsLocked(0);
nextItem.setUpdateTime(LocalDateTime.now());
taskItemMapper.updateById(nextItem);
log.info("✅ 解锁子任务: {} (组号: {}, ID: {})",
nextItem.getItemNo(), nextGroupNo, nextItem.getId());
}
result.put("unlockedGroupNo", nextGroupNo);
result.put("unlockedCount", nextGroupItems.size());
// 发送通知:新的子任务组已开放
Notification notification = new Notification();
notification.setNotificationType("task");
notification.setTitle("新子任务组已开放");
notification.setContent("第 " + nextGroupNo + " 组共 " + nextGroupItems.size() + " 个子任务已解锁,可以接单了!");
notification.setRelatedId(item.getTaskId());
notification.setRelatedType("task");
notification.setPriority(1); // 普通
// 可以通知所有接单部门的成员(这里暂时省略,可以扩展)
log.info("第 {} 组 {} 个子任务已进入可接任务池", nextGroupNo, nextGroupItems.size());
} else {
log.info("没有更多子任务组需要解锁");
}
} else {
log.info("当前组还有子任务未完成,暂不解锁下一组");
}
}
// 7. 检查主任务是否所有子任务都已完成
int completedCount = taskItemMapper.countCompletedItems(item.getTaskId());
int totalCount = taskItemMapper.countTotalItems(item.getTaskId());
if (completedCount == totalCount) {
// 所有子任务完成,更新主任务状态
mainTask.setStatus("completed");
mainTask.setCompleteTime(LocalDateTime.now());
mainTask.setUpdateTime(LocalDateTime.now());
taskMapper.updateById(mainTask);
log.info("🎉 主任务 {} 所有子任务已完成,主任务标记为完成", mainTask.getId());
// 发送通知给发布者
if (mainTask.getPublisherId() != null) {
Notification notification = new Notification();
notification.setReceiverId(mainTask.getPublisherId());
notification.setNotificationType("task");
notification.setTitle("流水线任务已完成");
notification.setContent("您的任务《" + mainTask.getTitle() + "》所有子任务已完成!");
notification.setRelatedId(mainTask.getId());
notification.setRelatedType("task");
notification.setPriority(1); // 普通
notificationService.sendNotification(notification);
}
result.put("mainTaskCompleted", true);
} else {
result.put("mainTaskCompleted", false);
result.put("progress", completedCount + "/" + totalCount);
}
return result;
}
/**
* 驳回子任务
*/
@Transactional(rollbackFor = Exception.class)
public void rejectItem(Long itemId, String reason) {
TaskItem item = taskItemMapper.selectById(itemId);
if (item == null) {
throw new BusinessException(ResultCode.NOT_FOUND, "子任务不存在");
}
item.setStatus("rejected");
item.setUpdateTime(LocalDateTime.now());
taskItemMapper.updateById(item);
// 发送通知给接单人
if (item.getReceiverId() != null) {
Notification notification = new Notification();
notification.setReceiverId(item.getReceiverId());
notification.setNotificationType("task");
notification.setTitle("子任务被驳回");
notification.setContent("您的子任务 " + item.getItemNo() + " 被驳回。原因:" + reason);
notification.setRelatedId(itemId);
notification.setRelatedType("task_item");
notification.setPriority(1); // 普通
notificationService.sendNotification(notification);
}
log.info("子任务 {} 已驳回", itemId);
}
/**
* 获取主任务的进度统计
*/
public Map<String, Object> getTaskProgress(Long taskId) {
int completedCount = taskItemMapper.countCompletedItems(taskId);
int totalCount = taskItemMapper.countTotalItems(taskId);
Map<String, Object> progress = new HashMap<>();
progress.put("completedCount", completedCount);
progress.put("totalCount", totalCount);
progress.put("percentage", totalCount > 0 ? (completedCount * 100 / totalCount) : 0);
return progress;
}
}