diff --git a/backend/src/main/java/com/designhub/controller/TaskProductController.java b/backend/src/main/java/com/designhub/controller/TaskProductController.java index 988ebc6..ac40f75 100644 --- a/backend/src/main/java/com/designhub/controller/TaskProductController.java +++ b/backend/src/main/java/com/designhub/controller/TaskProductController.java @@ -98,5 +98,16 @@ public class TaskProductController { long count = taskProductService.countByTaskId(taskId); return Result.success(count); } + + @Operation(summary = "确认商品ID填写完成") + @PostMapping("/confirm") + public Result confirmProductIdFilled( + @PathVariable Long taskId, + @RequestHeader(value = "Authorization", required = false) String authHeader) { + String token = jwtUtil.extractTokenFromHeader(authHeader); + Long userId = jwtUtil.getUserIdFromToken(token); + taskProductService.confirmProductIdFilled(taskId, userId); + return Result.success(); + } } diff --git a/backend/src/main/java/com/designhub/entity/Task.java b/backend/src/main/java/com/designhub/entity/Task.java index d94c927..089d7c8 100644 --- a/backend/src/main/java/com/designhub/entity/Task.java +++ b/backend/src/main/java/com/designhub/entity/Task.java @@ -230,6 +230,12 @@ public class Task implements Serializable { */ private LocalDateTime rejectDeadline; + /** + * 商品ID是否已填写确认:0-未填写 1-已填写 + * 只有当此字段为1时,任务才真正"完结" + */ + private Integer productIdFilled; + /** * 是否紧急:0-否 1-是 */ @@ -358,6 +364,12 @@ public class Task implements Serializable { @TableField(exist = false) private String currentApproverName; + /** + * 所有子任务的交付路径汇总(非数据库字段,流水线任务专用) + */ + @TableField(exist = false) + private java.util.List allDeliveryPaths; + // ==================== Getters and Setters (手动添加以解决Lombok编译问题) ==================== public Long getId() { return id; } @@ -489,6 +501,9 @@ public class Task implements Serializable { public LocalDateTime getRejectDeadline() { return rejectDeadline; } public void setRejectDeadline(LocalDateTime rejectDeadline) { this.rejectDeadline = rejectDeadline; } + public Integer getProductIdFilled() { return productIdFilled; } + public void setProductIdFilled(Integer productIdFilled) { this.productIdFilled = productIdFilled; } + public Integer getIsUrgent() { return isUrgent; } public void setIsUrgent(Integer isUrgent) { this.isUrgent = isUrgent; } @@ -566,5 +581,8 @@ public class Task implements Serializable { public java.util.List getSubTasks() { return subTasks; } public void setSubTasks(java.util.List subTasks) { this.subTasks = subTasks; } + + public java.util.List getAllDeliveryPaths() { return allDeliveryPaths; } + public void setAllDeliveryPaths(java.util.List allDeliveryPaths) { this.allDeliveryPaths = allDeliveryPaths; } } diff --git a/backend/src/main/java/com/designhub/service/TaskProductService.java b/backend/src/main/java/com/designhub/service/TaskProductService.java index 132213d..e70d613 100644 --- a/backend/src/main/java/com/designhub/service/TaskProductService.java +++ b/backend/src/main/java/com/designhub/service/TaskProductService.java @@ -77,6 +77,9 @@ public class TaskProductService { taskProductMapper.insert(taskProduct); log.info("添加任务商品ID成功: taskId={}, productId={}, userId={}", taskId, productId, userId); + // 更新任务的商品ID填写状态 + updateTaskProductIdFilledStatus(taskId); + return taskProduct; } @@ -121,6 +124,9 @@ public class TaskProductService { taskProductMapper.deleteById(id); log.info("删除任务商品ID成功: id={}, taskId={}, productId={}", id, product.getTaskId(), product.getProductId()); + + // 更新任务的商品ID填写状态 + updateTaskProductIdFilledStatus(product.getTaskId()); } /** @@ -166,5 +172,41 @@ public class TaskProductService { wrapper.eq(TaskProduct::getTaskId, taskId); return taskProductMapper.selectCount(wrapper); } + + /** + * 更新任务的商品ID填写状态 + */ + private void updateTaskProductIdFilledStatus(Long taskId) { + long count = countByTaskId(taskId); + Task task = taskMapper.selectById(taskId); + if (task != null) { + // 如果有商品ID记录,标记为已填写 + task.setProductIdFilled(count > 0 ? 1 : 0); + taskMapper.updateById(task); + log.info("更新任务商品ID填写状态: taskId={}, productIdFilled={}", taskId, count > 0 ? 1 : 0); + } + } + + /** + * 确认商品ID填写完成(手动确认) + */ + @Transactional(rollbackFor = Exception.class) + public void confirmProductIdFilled(Long taskId, Long userId) { + Task task = taskMapper.selectById(taskId); + if (task == null) { + throw new BusinessException(ResultCode.NOT_FOUND, "任务不存在"); + } + + // 检查是否有商品ID + long count = countByTaskId(taskId); + if (count == 0) { + throw new BusinessException(ResultCode.PARAM_ERROR, "请先填写商品ID后再确认"); + } + + // 更新状态 + task.setProductIdFilled(1); + taskMapper.updateById(task); + log.info("确认商品ID填写完成: taskId={}, userId={}", taskId, userId); + } } diff --git a/backend/src/main/java/com/designhub/service/TaskService.java b/backend/src/main/java/com/designhub/service/TaskService.java index 441dfd3..85936e3 100644 --- a/backend/src/main/java/com/designhub/service/TaskService.java +++ b/backend/src/main/java/com/designhub/service/TaskService.java @@ -727,6 +727,15 @@ public class TaskService { .orderByAsc(TaskItem::getSortOrder) ); + // 汇总所有子任务的交付路径 + if (subTasks != null && !subTasks.isEmpty()) { + List 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()); diff --git a/database/add_product_id_filled.sql b/database/add_product_id_filled.sql new file mode 100644 index 0000000..983cd61 --- /dev/null +++ b/database/add_product_id_filled.sql @@ -0,0 +1,9 @@ +-- 添加商品ID填写确认字段 +ALTER TABLE task ADD COLUMN product_id_filled TINYINT(1) DEFAULT 0 COMMENT '商品ID是否已填写确认:0-未填写 1-已填写' AFTER reject_deadline; + +-- 更新现有已完成任务:如果已有商品ID记录则标记为已填写 +UPDATE task t +SET t.product_id_filled = 1 +WHERE t.status = 'completed' +AND EXISTS (SELECT 1 FROM task_product tp WHERE tp.task_id = t.id); + diff --git a/frontend/src/api/task.ts b/frontend/src/api/task.ts index 17e361a..d04d53f 100644 --- a/frontend/src/api/task.ts +++ b/frontend/src/api/task.ts @@ -64,6 +64,12 @@ export interface Task { subTasks?: TaskItem[] // 标记字段(前端使用) isSubTask?: boolean + // 商品ID填写状态 + productIdFilled?: number // 0-未填写 1-已填写 + // 所有子任务交付路径汇总 + allDeliveryPaths?: string[] + // 预览图 + previewImage?: string } // 任务子项(流水线任务) @@ -637,3 +643,10 @@ export const getTaskProductCount = (taskId: number) => { return request.get(`/tasks/${taskId}/products/count`) } +/** + * 确认商品ID填写完成 + */ +export const confirmTaskProductFilled = (taskId: number) => { + return request.post(`/tasks/${taskId}/products/confirm`) +} + diff --git a/frontend/src/views/TaskHall.vue b/frontend/src/views/TaskHall.vue index 21a1ba2..129a474 100644 --- a/frontend/src/views/TaskHall.vue +++ b/frontend/src/views/TaskHall.vue @@ -2133,6 +2133,42 @@ + + +
+ + + + 确认完结任务 + +
+ + + +
+

+ + 所有子任务交付路径 +

+
+
+ + + {{ path }} + + {{ path }} +
+
@@ -2270,6 +2306,7 @@ const taskProducts = ref([]) const newProductId = ref('') const batchProductIds = ref('') const addingProduct = ref(false) +const confirmingProductId = ref(false) // 已完成任务状态 const completedLoading = ref(false) @@ -3922,6 +3959,39 @@ const handleDeleteProduct = async (productId: number) => { }) } +// 确认商品ID填写完成 +const handleConfirmProductIdFilled = async () => { + if (!selectedTask.value || !selectedTask.value.id) return + + confirmingProductId.value = true + try { + await taskApi.confirmTaskProductFilled(selectedTask.value.id) + message.success('任务已确认完结') + // 更新当前任务状态 + selectedTask.value.productIdFilled = 1 + // 刷新任务列表 + await loadCompletedTasks() + await loadMyTasks() + } catch (error: any) { + message.error(error.message || '确认失败') + } finally { + confirmingProductId.value = false + } +} + +// 判断是否是URL +const isUrl = (str: string) => { + // 从字符串中提取可能的URL部分 + const urlMatch = str.match(/https?:\/\/[^\s]+/) + return urlMatch !== null +} + +// 从字符串中提取URL +const extractUrl = (str: string) => { + const urlMatch = str.match(/https?:\/\/[^\s]+/) + return urlMatch ? urlMatch[0] : str +} + // 监听配置更新事件 const handleConfigUpdate = async (event: CustomEvent) => { console.log('配置已更新,刷新配置数据:', event.detail) @@ -3967,6 +4037,15 @@ const getTaskStatusText = (status: string, task?: any) => { return '待接单' } } + + // 5. 已完成任务:检查是否已填写商品ID + if (status === 'completed') { + // productIdFilled为0或undefined表示未填写 + if (!task.productIdFilled || task.productIdFilled === 0) { + return '待填写ID' + } + return '已完结' + } } const statusMap: Record = { @@ -4568,6 +4647,58 @@ onUnmounted(() => { color: #1890ff; } +// 待填写ID状态样式 +.status-pending_id { + background: rgba(250, 173, 20, 0.1); + border: 1px solid rgba(250, 173, 20, 0.3); + color: #faad14; +} + +// 已完结状态样式 +.status-finished { + background: rgba(49, 240, 139, 0.1); + border: 1px solid rgba(49, 240, 139, 0.3); + color: #31f08b; +} + +// 确认完结区域样式 +.confirm-complete-section { + margin-top: 16px; + padding-top: 16px; + border-top: 1px solid $border-primary; +} + +// 所有子任务交付路径样式 +.all-delivery-paths { + .delivery-paths-list { + display: flex; + flex-direction: column; + gap: 8px; + + .delivery-path-item { + padding: 8px 12px; + background: $bg-tertiary; + border-radius: $radius-sm; + font-size: 13px; + + .delivery-link { + color: #31f08b; + text-decoration: none; + word-break: break-all; + + &:hover { + text-decoration: underline; + } + } + + .delivery-text { + color: $text-secondary; + word-break: break-all; + } + } + } +} + // 驳回信息显示 .reject-info { margin: 12px 0;