|
|
@@ -0,0 +1,255 @@
|
|
|
+package com.etotem.cfc.service;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.etotem.cfc.entity.Child;
|
|
|
+import com.etotem.cfc.entity.GrowthTask;
|
|
|
+import com.etotem.cfc.entity.GrowthTaskLog;
|
|
|
+import com.etotem.cfc.mapper.ChildMapper;
|
|
|
+import com.etotem.cfc.mapper.GrowthTaskLogMapper;
|
|
|
+import com.etotem.cfc.mapper.GrowthTaskMapper;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class GrowthTaskService {
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private GrowthTaskMapper growthTaskMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private GrowthTaskLogMapper growthTaskLogMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ChildMapper childMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private PointsService pointsService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private EnergyService energyService;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get task list with user's progress for today (daily) or all-time (newbie).
|
|
|
+ */
|
|
|
+ public List<Map<String, Object>> getTaskList(Long userId, String type) {
|
|
|
+ if (type == null || type.isEmpty()) {
|
|
|
+ type = "DAILY";
|
|
|
+ }
|
|
|
+
|
|
|
+ List<GrowthTask> tasks = growthTaskMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<GrowthTask>()
|
|
|
+ .eq(GrowthTask::getType, type)
|
|
|
+ .eq(GrowthTask::getEnabled, 1)
|
|
|
+ .orderByAsc(GrowthTask::getSortOrder)
|
|
|
+ );
|
|
|
+
|
|
|
+ String today = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
|
|
|
+
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
+ for (GrowthTask task : tasks) {
|
|
|
+ Map<String, Object> item = new LinkedHashMap<>();
|
|
|
+ item.put("id", task.getId());
|
|
|
+ item.put("type", task.getType());
|
|
|
+ item.put("title", task.getTitle());
|
|
|
+ item.put("description", task.getDescription());
|
|
|
+ item.put("rewardPoints", task.getRewardPoints());
|
|
|
+ item.put("rewardEnergy", task.getRewardEnergy());
|
|
|
+ item.put("targetValue", task.getTargetValue());
|
|
|
+ item.put("taskKey", task.getTaskKey());
|
|
|
+
|
|
|
+ // Find user's log for this task
|
|
|
+ LambdaQueryWrapper<GrowthTaskLog> logWrapper = new LambdaQueryWrapper<GrowthTaskLog>()
|
|
|
+ .eq(GrowthTaskLog::getUserId, userId)
|
|
|
+ .eq(GrowthTaskLog::getTaskId, task.getId());
|
|
|
+
|
|
|
+ if ("DAILY".equals(type)) {
|
|
|
+ logWrapper.eq(GrowthTaskLog::getDate, today);
|
|
|
+ }
|
|
|
+ // NEWBIE: no date filter, all-time
|
|
|
+
|
|
|
+ GrowthTaskLog taskLog = growthTaskLogMapper.selectOne(logWrapper.last("LIMIT 1"));
|
|
|
+
|
|
|
+ if (taskLog != null) {
|
|
|
+ item.put("taskLogId", taskLog.getId());
|
|
|
+ item.put("progress", taskLog.getProgress());
|
|
|
+ item.put("completed", taskLog.getCompleted());
|
|
|
+ item.put("claimed", taskLog.getClaimed());
|
|
|
+ } else {
|
|
|
+ item.put("taskLogId", null);
|
|
|
+ item.put("progress", 0);
|
|
|
+ item.put("completed", 0);
|
|
|
+ item.put("claimed", 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ result.add(item);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Update progress for a task. Auto-marks completed when progress >= targetValue.
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public void updateProgress(Long userId, String taskKey, int increment) {
|
|
|
+ GrowthTask task = growthTaskMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<GrowthTask>()
|
|
|
+ .eq(GrowthTask::getTaskKey, taskKey)
|
|
|
+ .eq(GrowthTask::getEnabled, 1)
|
|
|
+ .last("LIMIT 1")
|
|
|
+ );
|
|
|
+ if (task == null) {
|
|
|
+ log.warn("成长任务不存在或未启用: taskKey={}", taskKey);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ String today = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
|
|
|
+
|
|
|
+ // Find or create log
|
|
|
+ LambdaQueryWrapper<GrowthTaskLog> logWrapper = new LambdaQueryWrapper<GrowthTaskLog>()
|
|
|
+ .eq(GrowthTaskLog::getUserId, userId)
|
|
|
+ .eq(GrowthTaskLog::getTaskId, task.getId());
|
|
|
+
|
|
|
+ if ("DAILY".equals(task.getType())) {
|
|
|
+ logWrapper.eq(GrowthTaskLog::getDate, today);
|
|
|
+ }
|
|
|
+
|
|
|
+ GrowthTaskLog taskLog = growthTaskLogMapper.selectOne(logWrapper.last("LIMIT 1"));
|
|
|
+
|
|
|
+ if (taskLog == null) {
|
|
|
+ taskLog = new GrowthTaskLog();
|
|
|
+ taskLog.setUserId(userId);
|
|
|
+ taskLog.setTaskId(task.getId());
|
|
|
+ taskLog.setProgress(0);
|
|
|
+ taskLog.setCompleted(0);
|
|
|
+ taskLog.setClaimed(0);
|
|
|
+ if ("DAILY".equals(task.getType())) {
|
|
|
+ taskLog.setDate(today);
|
|
|
+ }
|
|
|
+ taskLog.setCreatedAt(new Date());
|
|
|
+ growthTaskLogMapper.insert(taskLog);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Already completed, skip
|
|
|
+ if (taskLog.getCompleted() != null && taskLog.getCompleted() == 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Update progress
|
|
|
+ int newProgress = (taskLog.getProgress() != null ? taskLog.getProgress() : 0) + increment;
|
|
|
+ taskLog.setProgress(newProgress);
|
|
|
+
|
|
|
+ // Auto-complete when target reached
|
|
|
+ int targetValue = task.getTargetValue() != null ? task.getTargetValue() : 1;
|
|
|
+ if (newProgress >= targetValue) {
|
|
|
+ taskLog.setCompleted(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ growthTaskLogMapper.updateById(taskLog);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Claim reward for a completed task. Awards points + energy to the child.
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public String claimReward(Long userId, Long taskLogId) {
|
|
|
+ GrowthTaskLog taskLog = growthTaskLogMapper.selectById(taskLogId);
|
|
|
+ if (taskLog == null) {
|
|
|
+ return "任务记录不存在";
|
|
|
+ }
|
|
|
+ if (!taskLog.getUserId().equals(userId)) {
|
|
|
+ return "无权操作此任务";
|
|
|
+ }
|
|
|
+ if (taskLog.getCompleted() == null || taskLog.getCompleted() != 1) {
|
|
|
+ return "任务未完成,无法领取";
|
|
|
+ }
|
|
|
+ if (taskLog.getClaimed() != null && taskLog.getClaimed() == 1) {
|
|
|
+ return "奖励已领取";
|
|
|
+ }
|
|
|
+
|
|
|
+ GrowthTask task = growthTaskMapper.selectById(taskLog.getTaskId());
|
|
|
+ if (task == null) {
|
|
|
+ return "任务不存在";
|
|
|
+ }
|
|
|
+
|
|
|
+ // Mark as claimed
|
|
|
+ taskLog.setClaimed(1);
|
|
|
+ growthTaskLogMapper.updateById(taskLog);
|
|
|
+
|
|
|
+ // Find child by userId
|
|
|
+ Child child = childMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<Child>()
|
|
|
+ .eq(Child::getUserId, userId)
|
|
|
+ .last("LIMIT 1")
|
|
|
+ );
|
|
|
+
|
|
|
+ if (child != null) {
|
|
|
+ // Award points
|
|
|
+ int rewardPoints = task.getRewardPoints() != null ? task.getRewardPoints() : 0;
|
|
|
+ if (rewardPoints > 0) {
|
|
|
+ try {
|
|
|
+ pointsService.awardSystemPoints(child.getId(), rewardPoints, "growth:" + task.getTaskKey());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("成长任务积分发放失败: childId={}, taskKey={}, error={}", child.getId(), task.getTaskKey(), e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Award energy
|
|
|
+ int rewardEnergy = task.getRewardEnergy() != null ? task.getRewardEnergy() : 0;
|
|
|
+ if (rewardEnergy > 0) {
|
|
|
+ try {
|
|
|
+ energyService.awardEnergy(child.getId(), "growth_day", 0L, rewardEnergy,
|
|
|
+ "每日任务奖励:" + task.getTaskKey(), 1);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("成长任务能量发放失败: childId={}, taskKey={}, error={}", child.getId(), task.getTaskKey(), e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.warn("成长任务奖励: 用户{}无孩子记录,跳过积分/能量发放", userId);
|
|
|
+ }
|
|
|
+
|
|
|
+ return "领取成功";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Initialize newbie task logs for a new user.
|
|
|
+ * Called from OnboardingService.initForUser to create growth_task_log entries
|
|
|
+ * for newbie-type tasks (NEWBIE_PROFILE, NEWBIE_CHILD, NEWBIE_REFERRAL).
|
|
|
+ */
|
|
|
+ public void initNewbieTasks(Long userId) {
|
|
|
+ List<GrowthTask> newbieTasks = growthTaskMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<GrowthTask>()
|
|
|
+ .eq(GrowthTask::getType, "NEWBIE")
|
|
|
+ .eq(GrowthTask::getEnabled, 1)
|
|
|
+ );
|
|
|
+
|
|
|
+ for (GrowthTask task : newbieTasks) {
|
|
|
+ // Check if log already exists (idempotent)
|
|
|
+ GrowthTaskLog existing = growthTaskLogMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<GrowthTaskLog>()
|
|
|
+ .eq(GrowthTaskLog::getUserId, userId)
|
|
|
+ .eq(GrowthTaskLog::getTaskId, task.getId())
|
|
|
+ .last("LIMIT 1")
|
|
|
+ );
|
|
|
+ if (existing != null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ GrowthTaskLog taskLog = new GrowthTaskLog();
|
|
|
+ taskLog.setUserId(userId);
|
|
|
+ taskLog.setTaskId(task.getId());
|
|
|
+ taskLog.setProgress(0);
|
|
|
+ taskLog.setCompleted(0);
|
|
|
+ taskLog.setClaimed(0);
|
|
|
+ taskLog.setDate(null); // NEWBIE tasks are all-time, no date
|
|
|
+ taskLog.setCreatedAt(new Date());
|
|
|
+ growthTaskLogMapper.insert(taskLog);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|