|
@@ -0,0 +1,278 @@
|
|
|
|
|
+package com.zxyj.service;
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.zxyj.entity.Task;
|
|
|
|
|
+import com.zxyj.mapper.TaskMapper;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+
|
|
|
|
|
+import javax.annotation.Resource;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * TASK-013~015: 任务提醒和统计服务
|
|
|
|
|
+ */
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+@Service
|
|
|
|
|
+public class TaskReminderService {
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private TaskMapper taskMapper;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * TASK-013: 获取即将到期的任务(用于提醒)
|
|
|
|
|
+ * 返回指定时间范围内即将到期的任务
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<Task> getUpcomingTasks(Long childId, int minutesBefore) {
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
|
|
+ cal.add(Calendar.MINUTE, minutesBefore);
|
|
|
|
|
+ Date endTime = cal.getTime();
|
|
|
|
|
+
|
|
|
|
|
+ return taskMapper.selectList(new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getChildId, childId)
|
|
|
|
|
+ .eq(Task::getStatus, "pending")
|
|
|
|
|
+ .ge(Task::getDeadline, now)
|
|
|
|
|
+ .le(Task::getDeadline, endTime)
|
|
|
|
|
+ .orderByAsc(Task::getDeadline));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * TASK-013: 获取需要提醒的任务(半小时内到期)
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<Task> getTasksNeedReminder(Long childId) {
|
|
|
|
|
+ return getUpcomingTasks(childId, 30);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * TASK-013: 获取已超时但未完成的任务
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<Task> getOverdueTasks(Long childId) {
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+
|
|
|
|
|
+ return taskMapper.selectList(new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getChildId, childId)
|
|
|
|
|
+ .eq(Task::getStatus, "pending")
|
|
|
|
|
+ .lt(Task::getDeadline, now)
|
|
|
|
|
+ .orderByAsc(Task::getDeadline));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * TASK-014: 获取任务历史(带筛选条件)
|
|
|
|
|
+ */
|
|
|
|
|
+ public Map<String, Object> getTaskHistoryWithFilter(Long childId, String status,
|
|
|
|
|
+ Date startDate, Date endDate,
|
|
|
|
|
+ Integer page, Integer size) {
|
|
|
|
|
+ LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ wrapper.eq(Task::getChildId, childId);
|
|
|
|
|
+
|
|
|
|
|
+ // 状态筛选
|
|
|
|
|
+ if (status != null && !status.isEmpty()) {
|
|
|
|
|
+ wrapper.eq(Task::getStatus, status);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 时间范围
|
|
|
|
|
+ if (startDate != null) {
|
|
|
|
|
+ wrapper.ge(Task::getCreatedAt, startDate);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (endDate != null) {
|
|
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
|
|
+ cal.setTime(endDate);
|
|
|
|
|
+ cal.add(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
|
+ wrapper.le(Task::getCreatedAt, cal.getTime());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 计算总数
|
|
|
|
|
+ Long total = taskMapper.selectCount(wrapper);
|
|
|
|
|
+
|
|
|
|
|
+ // 分页查询
|
|
|
|
|
+ wrapper.orderByDesc(Task::getCreatedAt);
|
|
|
|
|
+ int offset = (page - 1) * size;
|
|
|
|
|
+ List<Task> tasks = taskMapper.selectList(wrapper);
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
|
|
+ result.put("list", tasks);
|
|
|
|
|
+ result.put("total", total);
|
|
|
|
|
+ result.put("page", page);
|
|
|
|
|
+ result.put("size", size);
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * TASK-015: 获取任务统计概览
|
|
|
|
|
+ */
|
|
|
|
|
+ public Map<String, Object> getTaskStatistics(Long childId, Date startDate, Date endDate) {
|
|
|
|
|
+ Map<String, Object> stats = new HashMap<>();
|
|
|
|
|
+
|
|
|
|
|
+ // 基础统计
|
|
|
|
|
+ LambdaQueryWrapper<Task> baseWrapper = new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getChildId, childId);
|
|
|
|
|
+
|
|
|
|
|
+ if (startDate != null && endDate != null) {
|
|
|
|
|
+ baseWrapper.ge(Task::getCreatedAt, startDate)
|
|
|
|
|
+ .le(Task::getCreatedAt, endDate);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 总任务数
|
|
|
|
|
+ Long totalTasks = taskMapper.selectCount(baseWrapper);
|
|
|
|
|
+ stats.put("totalTasks", totalTasks);
|
|
|
|
|
+
|
|
|
|
|
+ // 已完成任务数
|
|
|
|
|
+ Long completedTasks = taskMapper.selectCount(new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getChildId, childId)
|
|
|
|
|
+ .eq(Task::getStatus, "completed")
|
|
|
|
|
+ .ge(startDate != null, Task::getCompletedAt, startDate)
|
|
|
|
|
+ .le(endDate != null, Task::getCompletedAt, endDate));
|
|
|
|
|
+ stats.put("completedTasks", completedTasks);
|
|
|
|
|
+
|
|
|
|
|
+ // 待完成任务数
|
|
|
|
|
+ Long pendingTasks = taskMapper.selectCount(new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getChildId, childId)
|
|
|
|
|
+ .eq(Task::getStatus, "pending"));
|
|
|
|
|
+ stats.put("pendingTasks", pendingTasks);
|
|
|
|
|
+
|
|
|
|
|
+ // 超时任务数
|
|
|
|
|
+ Long overdueTasks = taskMapper.selectCount(new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getChildId, childId)
|
|
|
|
|
+ .eq(Task::getStatus, "pending")
|
|
|
|
|
+ .lt(Task::getDeadline, new Date()));
|
|
|
|
|
+ stats.put("overdueTasks", overdueTasks);
|
|
|
|
|
+
|
|
|
|
|
+ // 已取消任务数
|
|
|
|
|
+ Long cancelledTasks = taskMapper.selectCount(new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getChildId, childId)
|
|
|
|
|
+ .eq(Task::getStatus, "cancelled"));
|
|
|
|
|
+ stats.put("cancelledTasks", cancelledTasks);
|
|
|
|
|
+
|
|
|
|
|
+ // 完成率
|
|
|
|
|
+ double completionRate = totalTasks > 0 ? (double) completedTasks / totalTasks * 100 : 0;
|
|
|
|
|
+ stats.put("completionRate", Math.round(completionRate * 100.0) / 100.0);
|
|
|
|
|
+
|
|
|
|
|
+ return stats;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * TASK-015: 获取按分类统计
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<Map<String, Object>> getTaskStatsByCategory(Long childId) {
|
|
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ // 获取所有任务按分类统计
|
|
|
|
|
+ List<Task> allTasks = taskMapper.selectList(new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getChildId, childId)
|
|
|
|
|
+ .isNotNull(Task::getCategory));
|
|
|
|
|
+
|
|
|
|
|
+ // 按分类分组统计
|
|
|
|
|
+ Map<String, Integer> categoryCount = new HashMap<>();
|
|
|
|
|
+ Map<String, Integer> categoryCompleted = new HashMap<>();
|
|
|
|
|
+
|
|
|
|
|
+ for (Task task : allTasks) {
|
|
|
|
|
+ String category = task.getCategory();
|
|
|
|
|
+ if (category != null) {
|
|
|
|
|
+ categoryCount.merge(category, 1, Integer::sum);
|
|
|
|
|
+ if ("completed".equals(task.getStatus())) {
|
|
|
|
|
+ categoryCompleted.merge(category, 1, Integer::sum);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 构建结果
|
|
|
|
|
+ for (Map.Entry<String, Integer> entry : categoryCount.entrySet()) {
|
|
|
|
|
+ Map<String, Object> item = new HashMap<>();
|
|
|
|
|
+ item.put("category", entry.getKey());
|
|
|
|
|
+ item.put("total", entry.getValue());
|
|
|
|
|
+ item.put("completed", categoryCompleted.getOrDefault(entry.getKey(), 0));
|
|
|
|
|
+ double rate = entry.getValue() > 0 ?
|
|
|
|
|
+ (double) categoryCompleted.getOrDefault(entry.getKey(), 0) / entry.getValue() * 100 : 0;
|
|
|
|
|
+ item.put("completionRate", Math.round(rate * 100.0) / 100.0);
|
|
|
|
|
+ result.add(item);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * TASK-015: 获取按日期统计(最近7天/30天)
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<Map<String, Object>> getTaskStatsByDate(Long childId, int days) {
|
|
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = days - 1; i >= 0; i--) {
|
|
|
|
|
+ cal.setTime(new Date());
|
|
|
|
|
+ cal.add(Calendar.DAY_OF_MONTH, -i);
|
|
|
|
|
+ cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
|
|
+ cal.set(Calendar.MINUTE, 0);
|
|
|
|
|
+ cal.set(Calendar.SECOND, 0);
|
|
|
|
|
+ cal.set(Calendar.MILLISECOND, 0);
|
|
|
|
|
+ Date startOfDay = cal.getTime();
|
|
|
|
|
+
|
|
|
|
|
+ cal.add(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
|
+ Date endOfDay = cal.getTime();
|
|
|
|
|
+
|
|
|
|
|
+ // 当天创建的任务数
|
|
|
|
|
+ Long created = taskMapper.selectCount(new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getChildId, childId)
|
|
|
|
|
+ .ge(Task::getCreatedAt, startOfDay)
|
|
|
|
|
+ .lt(Task::getCreatedAt, endOfDay));
|
|
|
|
|
+
|
|
|
|
|
+ // 当天完成的任务数
|
|
|
|
|
+ Long completed = taskMapper.selectCount(new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getChildId, childId)
|
|
|
|
|
+ .eq(Task::getStatus, "completed")
|
|
|
|
|
+ .ge(Task::getCompletedAt, startOfDay)
|
|
|
|
|
+ .lt(Task::getCompletedAt, endOfDay));
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> dayStats = new HashMap<>();
|
|
|
|
|
+ dayStats.put("date", startOfDay);
|
|
|
|
|
+ dayStats.put("created", created);
|
|
|
|
|
+ dayStats.put("completed", completed);
|
|
|
|
|
+ result.add(dayStats);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * TASK-015: 获取家庭任务统计
|
|
|
|
|
+ */
|
|
|
|
|
+ public Map<String, Object> getFamilyTaskStatistics(Long familyId) {
|
|
|
|
|
+ Map<String, Object> stats = new HashMap<>();
|
|
|
|
|
+
|
|
|
|
|
+ // 家庭总任务数
|
|
|
|
|
+ Long totalTasks = taskMapper.selectCount(new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getFamilyId, familyId));
|
|
|
|
|
+ stats.put("totalTasks", totalTasks);
|
|
|
|
|
+
|
|
|
|
|
+ // 今日任务数
|
|
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
|
|
+ cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
|
|
+ cal.set(Calendar.MINUTE, 0);
|
|
|
|
|
+ cal.set(Calendar.SECOND, 0);
|
|
|
|
|
+ cal.set(Calendar.MILLISECOND, 0);
|
|
|
|
|
+ Date startOfDay = cal.getTime();
|
|
|
|
|
+ cal.add(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
|
+ Date endOfDay = cal.getTime();
|
|
|
|
|
+
|
|
|
|
|
+ Long todayTasks = taskMapper.selectCount(new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getFamilyId, familyId)
|
|
|
|
|
+ .ge(Task::getDeadline, startOfDay)
|
|
|
|
|
+ .lt(Task::getDeadline, endOfDay));
|
|
|
|
|
+ stats.put("todayTasks", todayTasks);
|
|
|
|
|
+
|
|
|
|
|
+ // 今日已完成
|
|
|
|
|
+ Long todayCompleted = taskMapper.selectCount(new LambdaQueryWrapper<Task>()
|
|
|
|
|
+ .eq(Task::getFamilyId, familyId)
|
|
|
|
|
+ .eq(Task::getStatus, "completed")
|
|
|
|
|
+ .ge(Task::getCompletedAt, startOfDay)
|
|
|
|
|
+ .lt(Task::getCompletedAt, endOfDay));
|
|
|
|
|
+ stats.put("todayCompleted", todayCompleted);
|
|
|
|
|
+
|
|
|
|
|
+ // 今日完成率
|
|
|
|
|
+ double todayRate = todayTasks > 0 ? (double) todayCompleted / todayTasks * 100 : 0;
|
|
|
|
|
+ stats.put("todayCompletionRate", Math.round(todayRate * 100.0) / 100.0);
|
|
|
|
|
+
|
|
|
|
|
+ return stats;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|