소스 검색

feat(task-plan): 新增统一任务生成器generateTasksFromTemplate

iwt 2 주 전
부모
커밋
7348b6fe07

+ 99 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/TaskPlanService.java

@@ -19,6 +19,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -179,6 +180,104 @@ public class TaskPlanService implements TaskPlanServiceInterface {
         return count;
     }
 
+    /**
+     * 统一方案任务生成器(设计: 2026-09-04-plan-to-task-unification-design.md)
+     * 引导任务 + 从 task_template_items(按 sort_order)批量生成日常任务。
+     * 幂等:sourceType=plan & sourceId=planId 已存在任务则跳过。
+     * deadline:当天 23:59:59;familyMemberId:统一设置;memberOnly=1。
+     */
+    @Transactional
+    public int generateTasksFromTemplate(TaskPlanInstance plan, Long operatorId) {
+        if (plan == null) {
+            throw new IllegalStateException("方案不存在");
+        }
+        Long planId = plan.getId();
+        // 幂等:该方案已生成过任务则跳过
+        Long existing = taskMapper.selectCount(
+                new LambdaQueryWrapper<Task>()
+                        .eq(Task::getSourceType, "plan")
+                        .eq(Task::getSourceId, planId));
+        if (existing != null && existing > 0) {
+            return 0;
+        }
+
+        Date now = new Date();
+        Date deadline = buildTodayDeadline();
+        int count = 0;
+
+        // 1. 引导任务(方案启动)
+        Task guideTask = new Task();
+        guideTask.setFamilyId(plan.getFamilyId());
+        guideTask.setChildId(plan.getChildId());
+        guideTask.setFamilyMemberId(plan.getChildId());
+        guideTask.setCreatorId(operatorId);
+        guideTask.setExecutorType("child");
+        guideTask.setExecutorId(plan.getChildId());
+        guideTask.setTitle("方案启动: " + plan.getName());
+        guideTask.setDescription("请阅读并开始执行测评方案");
+        guideTask.setPoints(2);
+        guideTask.setCategory("成长");
+        guideTask.setStatus("pending");
+        guideTask.setSourceType("plan");
+        guideTask.setSourceId(planId);
+        guideTask.setNeedReview(0);
+        guideTask.setIsTemplate(0);
+        guideTask.setMemberOnly(1);
+        guideTask.setDeadline(deadline);
+        guideTask.setCreatedAt(now);
+        guideTask.setUpdatedAt(now);
+        taskMapper.insert(guideTask);
+        count++;
+
+        // 2. 模板任务(task_template_items 为唯一来源)
+        if (plan.getPackageId() != null) {
+            List<TaskTemplateItem> items = templateItemMapper.selectList(
+                    new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<TaskTemplateItem>()
+                            .eq("package_id", plan.getPackageId())
+                            .orderByAsc("sort_order"));
+            for (TaskTemplateItem item : items) {
+                if (item.getTaskName() == null || item.getTaskName().isEmpty()) continue;
+                Task task = new Task();
+                task.setFamilyId(plan.getFamilyId());
+                task.setChildId(plan.getChildId());
+                task.setFamilyMemberId(plan.getChildId());
+                task.setCreatorId(operatorId);
+                task.setExecutorType("child");
+                task.setExecutorId(plan.getChildId());
+                task.setTitle(item.getTaskName());
+                task.setDescription(item.getDescription());
+                task.setPoints(item.getPoints() != null ? item.getPoints() : 2);
+                task.setCategory("成长");
+                task.setStatus("pending");
+                task.setSourceType("plan");
+                task.setSourceId(planId);
+                task.setNeedReview(0);
+                task.setIsTemplate(0);
+                task.setMemberOnly(1);
+                task.setFrequency(item.getFrequency() != null ? item.getFrequency() : "daily");
+                task.setDuration(item.getDuration());
+                task.setDeadline(deadline);
+                task.setCreatedAt(now);
+                task.setUpdatedAt(now);
+                taskMapper.insert(task);
+                count++;
+            }
+        }
+
+        log.info("方案统一生成任务: planId={}, familyId={}, taskCount={}", planId, plan.getFamilyId(), count);
+        return count;
+    }
+
+    /** 当天 23:59:59 截止(与 TaskService.getTodayTasks 区间对齐) */
+    private Date buildTodayDeadline() {
+        Calendar cal = Calendar.getInstance();
+        cal.set(Calendar.HOUR_OF_DAY, 23);
+        cal.set(Calendar.MINUTE, 59);
+        cal.set(Calendar.SECOND, 59);
+        cal.set(Calendar.MILLISECOND, 0);
+        return cal.getTime();
+    }
+
     /**
      * 获取计划详情
      */

+ 1 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/api/TaskPlanServiceInterface.java

@@ -13,5 +13,6 @@ public interface TaskPlanServiceInterface {
     List<TaskPlanItem> getPlanItems(Long instanceId);
     boolean cancelPlan(Long id);
     boolean updateCompletedDays(Long id, int days);
+    int generateTasksFromTemplate(TaskPlanInstance plan, Long operatorId);
     List<TaskPlanInstance> getChildPlans(Long memberId);
 }