|
@@ -54,9 +54,6 @@ public class TaskService implements TaskServiceInterface {
|
|
|
@Resource
|
|
@Resource
|
|
|
private EnergyService energyService;
|
|
private EnergyService energyService;
|
|
|
|
|
|
|
|
- @Resource
|
|
|
|
|
- private OnboardingService onboardingService;
|
|
|
|
|
-
|
|
|
|
|
@Resource
|
|
@Resource
|
|
|
private FamilyRelationshipScoreMapper familyRelationshipScoreMapper;
|
|
private FamilyRelationshipScoreMapper familyRelationshipScoreMapper;
|
|
|
|
|
|
|
@@ -75,6 +72,9 @@ public class TaskService implements TaskServiceInterface {
|
|
|
@Resource
|
|
@Resource
|
|
|
private SurveyTemplateMapper surveyTemplateMapper;
|
|
private SurveyTemplateMapper surveyTemplateMapper;
|
|
|
|
|
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private TaskTemplateMapper taskTemplateMapper;
|
|
|
|
|
+
|
|
|
private static final int EARLY_BONUS = 1; // 提前完成奖励
|
|
private static final int EARLY_BONUS = 1; // 提前完成奖励
|
|
|
private static final int PENALTY_MAX_DAILY = 5; // 每日最多扣分
|
|
private static final int PENALTY_MAX_DAILY = 5; // 每日最多扣分
|
|
|
private static final int LATE_GRACE_MINUTES = 10; // 10分钟内不算迟到
|
|
private static final int LATE_GRACE_MINUTES = 10; // 10分钟内不算迟到
|
|
@@ -772,7 +772,7 @@ public List<Task> getTodayTasks(Long memberId, String dimensionCode) {
|
|
|
Map<String, Object> result = new HashMap<>();
|
|
Map<String, Object> result = new HashMap<>();
|
|
|
result.put("pointsEarned", pointsEarned);
|
|
result.put("pointsEarned", pointsEarned);
|
|
|
|
|
|
|
|
- try { FamilyMember child = familyMemberMapper.selectById(task.getFamilyMemberId()); if (child != null) { onboardingService.completeTask(child.getUserId(), "FIRST_TASK"); } } catch (Exception e) { /* onboarding trigger failure should not block task completion */ }
|
|
|
|
|
|
|
+ try { FamilyMember child = familyMemberMapper.selectById(task.getFamilyMemberId()); if (child != null) { this.completeOnboardingTask(child.getUserId(), "FIRST_TASK"); } } catch (Exception e) { /* onboarding trigger failure should not block task completion */ }
|
|
|
result.put("newBalance", user.getTotalPoints());
|
|
result.put("newBalance", user.getTotalPoints());
|
|
|
result.put("needReview", task.getNeedReview() == 1);
|
|
result.put("needReview", task.getNeedReview() == 1);
|
|
|
|
|
|
|
@@ -1709,4 +1709,96 @@ return 0; // 达到每日扣分上限
|
|
|
}
|
|
}
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== 统一任务系统:新人引导迁移(原 OnboardingService) ====================
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 统一任务系统:新用户注册初始化新人引导任务
|
|
|
|
|
+ * 按 admin_task_templates 中 onboarding_sequence 有值的模板生成 tasks(source_type=onboarding)
|
|
|
|
|
+ * 顺序链:sequence 递增,前序未完成则后序 locked(复用 prerequisite_task_id + locked 状态)
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional
|
|
|
|
|
+ public void initOnboardingTasks(Long userId) {
|
|
|
|
|
+ User user = userService.getUserInfo(userId);
|
|
|
|
|
+ if (user == null || user.getFamilyId() == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 幂等:该用户已有 onboarding 任务则跳过
|
|
|
|
|
+ Long existing = taskMapper.selectCount(new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getCreatorId, userId)
|
|
|
|
|
+ .eq(Task::getSourceType, "onboarding"));
|
|
|
|
|
+ if (existing != null && existing > 0) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ List<TaskTemplate> templates = taskTemplateMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<TaskTemplate>()
|
|
|
|
|
+ .eq(TaskTemplate::getIsActive, 1)
|
|
|
|
|
+ .isNotNull(TaskTemplate::getOnboardingSequence)
|
|
|
|
|
+ .orderByAsc(TaskTemplate::getOnboardingSequence));
|
|
|
|
|
+ if (templates.isEmpty()) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ FamilyMember member = familyMemberMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<FamilyMember>()
|
|
|
|
|
+ .eq(FamilyMember::getUserId, userId)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ Long prevTaskId = null;
|
|
|
|
|
+ for (TaskTemplate tpl : templates) {
|
|
|
|
|
+ Task task = new Task();
|
|
|
|
|
+ task.setFamilyId(user.getFamilyId());
|
|
|
|
|
+ task.setCreatorId(userId);
|
|
|
|
|
+ task.setExecutorType("child");
|
|
|
|
|
+ if (member != null) {
|
|
|
|
|
+ task.setExecutorId(member.getId());
|
|
|
|
|
+ task.setFamilyMemberId(member.getId());
|
|
|
|
|
+ task.setChildId(member.getId());
|
|
|
|
|
+ }
|
|
|
|
|
+ task.setTitle(tpl.getTitle());
|
|
|
|
|
+ task.setDescription(tpl.getDescription());
|
|
|
|
|
+ task.setPoints(tpl.getPoints() != null ? tpl.getPoints() : 2);
|
|
|
|
|
+ task.setCategory(tpl.getCategory());
|
|
|
|
|
+ task.setStatus(prevTaskId == null ? "pending" : "locked"); // 首任务可直接做,后续需解锁
|
|
|
|
|
+ task.setNeedReview(tpl.getNeedReview() != null ? tpl.getNeedReview() : 0);
|
|
|
|
|
+ task.setIsTemplate(0);
|
|
|
|
|
+ task.setSourceType("onboarding");
|
|
|
|
|
+ task.setTemplateId(tpl.getId());
|
|
|
|
|
+ task.setPrerequisiteTaskId(prevTaskId); // 顺序链
|
|
|
|
|
+ task.setMemberOnly(0);
|
|
|
|
|
+ task.setDeadline(new Date(now.getTime() + 30L * 24 * 3600 * 1000)); // 30天有效期
|
|
|
|
|
+ task.setCreatedAt(now);
|
|
|
|
|
+ task.setUpdatedAt(now);
|
|
|
|
|
+ taskMapper.insert(task);
|
|
|
|
|
+ prevTaskId = task.getId();
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("新用户{}初始化{}条新人引导任务", userId, templates.size());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 统一任务系统:完成指定新人引导任务(原 OnboardingService.completeTask)
|
|
|
|
|
+ * 按 source_type=onboarding + title 精确匹配(title 约定 = 原 taskType,如 FIRST_TASK)
|
|
|
|
|
+ */
|
|
|
|
|
+ public void completeOnboardingTask(Long userId, String templateTitle) {
|
|
|
|
|
+ FamilyMember member = familyMemberMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<FamilyMember>()
|
|
|
|
|
+ .eq(FamilyMember::getUserId, userId)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ if (member == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ Task task = taskMapper.selectOne(new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getFamilyMemberId, member.getId())
|
|
|
|
|
+ .eq(Task::getSourceType, "onboarding")
|
|
|
|
|
+ .eq(Task::getTitle, templateTitle)
|
|
|
|
|
+ .eq(Task::getStatus, "pending")
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ if (task != null) {
|
|
|
|
|
+ awardTaskCompletionRewards(task, member.getId(), new Date());
|
|
|
|
|
+ unlockPrerequisiteTasks(task.getId());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|