|
@@ -1,24 +1,30 @@
|
|
|
package com.etotem.cfc.controller.stats;
|
|
package com.etotem.cfc.controller.stats;
|
|
|
|
|
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
import com.etotem.cfc.common.Result;
|
|
import com.etotem.cfc.common.Result;
|
|
|
import com.etotem.cfc.entity.AfterSalesRequest;
|
|
import com.etotem.cfc.entity.AfterSalesRequest;
|
|
|
-import com.etotem.cfc.entity.FamilyMember;
|
|
|
|
|
import com.etotem.cfc.entity.CommissionRecord;
|
|
import com.etotem.cfc.entity.CommissionRecord;
|
|
|
import com.etotem.cfc.entity.Family;
|
|
import com.etotem.cfc.entity.Family;
|
|
|
|
|
+import com.etotem.cfc.entity.FamilyMember;
|
|
|
|
|
+import com.etotem.cfc.entity.GameRecord;
|
|
|
import com.etotem.cfc.entity.MemberUpgradeRecord;
|
|
import com.etotem.cfc.entity.MemberUpgradeRecord;
|
|
|
|
|
+import com.etotem.cfc.entity.PointsLog;
|
|
|
import com.etotem.cfc.entity.Task;
|
|
import com.etotem.cfc.entity.Task;
|
|
|
import com.etotem.cfc.entity.TrialMembership;
|
|
import com.etotem.cfc.entity.TrialMembership;
|
|
|
import com.etotem.cfc.entity.User;
|
|
import com.etotem.cfc.entity.User;
|
|
|
import com.etotem.cfc.mapper.AfterSalesRequestMapper;
|
|
import com.etotem.cfc.mapper.AfterSalesRequestMapper;
|
|
|
-import com.etotem.cfc.mapper.FamilyMemberMapper;
|
|
|
|
|
import com.etotem.cfc.mapper.CommissionRecordMapper;
|
|
import com.etotem.cfc.mapper.CommissionRecordMapper;
|
|
|
import com.etotem.cfc.mapper.FamilyMapper;
|
|
import com.etotem.cfc.mapper.FamilyMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.FamilyMemberMapper;
|
|
|
import com.etotem.cfc.mapper.GuidePackageMapper;
|
|
import com.etotem.cfc.mapper.GuidePackageMapper;
|
|
|
import com.etotem.cfc.mapper.MemberUpgradeRecordMapper;
|
|
import com.etotem.cfc.mapper.MemberUpgradeRecordMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.PointsLogMapper;
|
|
|
import com.etotem.cfc.mapper.TaskMapper;
|
|
import com.etotem.cfc.mapper.TaskMapper;
|
|
|
import com.etotem.cfc.mapper.TrialMembershipMapper;
|
|
import com.etotem.cfc.mapper.TrialMembershipMapper;
|
|
|
import com.etotem.cfc.mapper.UserMapper;
|
|
import com.etotem.cfc.mapper.UserMapper;
|
|
|
|
|
+import com.etotem.cfc.service.GameRecordService;
|
|
|
|
|
+import com.etotem.cfc.service.TaskService;
|
|
|
import com.etotem.cfc.service.TaskStatsService;
|
|
import com.etotem.cfc.service.TaskStatsService;
|
|
|
import io.swagger.v3.oas.annotations.Operation;
|
|
import io.swagger.v3.oas.annotations.Operation;
|
|
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
@@ -27,83 +33,51 @@ import org.springframework.web.bind.annotation.*;
|
|
|
import javax.annotation.Resource;
|
|
import javax.annotation.Resource;
|
|
|
import java.text.ParseException;
|
|
import java.text.ParseException;
|
|
|
import java.text.SimpleDateFormat;
|
|
import java.text.SimpleDateFormat;
|
|
|
-import java.util.Calendar;
|
|
|
|
|
-import java.util.Date;
|
|
|
|
|
-import java.util.HashMap;
|
|
|
|
|
-import java.util.LinkedHashMap;
|
|
|
|
|
-import java.util.List;
|
|
|
|
|
-import java.util.Map;
|
|
|
|
|
|
|
+import java.util.*;
|
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|
|
import com.etotem.cfc.util.SortUtil;
|
|
import com.etotem.cfc.util.SortUtil;
|
|
|
import com.etotem.cfc.util.ParamUtils;
|
|
import com.etotem.cfc.util.ParamUtils;
|
|
|
|
|
|
|
|
-@Tag(name = "任务统计", description = "任务完成率分析")
|
|
|
|
|
|
|
+@Tag(name = "统计", description = "管理后台数据看板、任务完成率、家庭数据概览")
|
|
|
@RestController
|
|
@RestController
|
|
|
@RequestMapping("/api/stats")
|
|
@RequestMapping("/api/stats")
|
|
|
public class StatsController {
|
|
public class StatsController {
|
|
|
|
|
|
|
|
- @Resource
|
|
|
|
|
- private TaskStatsService taskStatsService;
|
|
|
|
|
-
|
|
|
|
|
- @Resource
|
|
|
|
|
- private UserMapper userMapper;
|
|
|
|
|
-
|
|
|
|
|
- @Resource
|
|
|
|
|
- private FamilyMemberMapper familyMemberMapper;
|
|
|
|
|
-
|
|
|
|
|
- @Resource
|
|
|
|
|
- private FamilyMapper familyMapper;
|
|
|
|
|
-
|
|
|
|
|
- @Resource
|
|
|
|
|
- private TaskMapper taskMapper;
|
|
|
|
|
-
|
|
|
|
|
- @Resource
|
|
|
|
|
- private GuidePackageMapper guidePackageMapper;
|
|
|
|
|
-
|
|
|
|
|
- @Resource
|
|
|
|
|
- private ThreadPoolTaskExecutor taskExecutor;
|
|
|
|
|
-
|
|
|
|
|
- @Resource
|
|
|
|
|
- private CommissionRecordMapper commissionRecordMapper;
|
|
|
|
|
-
|
|
|
|
|
- @Resource
|
|
|
|
|
- private TrialMembershipMapper trialMembershipMapper;
|
|
|
|
|
-
|
|
|
|
|
- @Resource
|
|
|
|
|
- private MemberUpgradeRecordMapper memberUpgradeRecordMapper;
|
|
|
|
|
-
|
|
|
|
|
- @Resource
|
|
|
|
|
- private AfterSalesRequestMapper afterSalesRequestMapper;
|
|
|
|
|
|
|
+ @Resource private TaskStatsService taskStatsService;
|
|
|
|
|
+ @Resource private UserMapper userMapper;
|
|
|
|
|
+ @Resource private FamilyMemberMapper familyMemberMapper;
|
|
|
|
|
+ @Resource private FamilyMapper familyMapper;
|
|
|
|
|
+ @Resource private TaskService taskService;
|
|
|
|
|
+ @Resource private TaskMapper taskMapper;
|
|
|
|
|
+ @Resource private GuidePackageMapper guidePackageMapper;
|
|
|
|
|
+ @Resource private ThreadPoolTaskExecutor taskExecutor;
|
|
|
|
|
+ @Resource private CommissionRecordMapper commissionRecordMapper;
|
|
|
|
|
+ @Resource private TrialMembershipMapper trialMembershipMapper;
|
|
|
|
|
+ @Resource private MemberUpgradeRecordMapper memberUpgradeRecordMapper;
|
|
|
|
|
+ @Resource private AfterSalesRequestMapper afterSalesRequestMapper;
|
|
|
|
|
+ @Resource private PointsLogMapper pointsLogMapper;
|
|
|
|
|
+ @Resource private GameRecordService gameRecordService;
|
|
|
|
|
+
|
|
|
|
|
+ // ── 管理后台仪表盘 ──
|
|
|
|
|
|
|
|
@Operation(summary = "管理后台仪表盘汇总")
|
|
@Operation(summary = "管理后台仪表盘汇总")
|
|
|
@PostMapping("/dashboard")
|
|
@PostMapping("/dashboard")
|
|
|
public Result<Map<String, Object>> getDashboardSummary() {
|
|
public Result<Map<String, Object>> getDashboardSummary() {
|
|
|
Map<String, Object> data = new HashMap<>();
|
|
Map<String, Object> data = new HashMap<>();
|
|
|
-
|
|
|
|
|
- CompletableFuture<Long> familyFuture = CompletableFuture.supplyAsync(() ->
|
|
|
|
|
- familyMapper.selectCount(null), taskExecutor);
|
|
|
|
|
- CompletableFuture<Long> parentFuture = CompletableFuture.supplyAsync(() ->
|
|
|
|
|
- userMapper.selectCount(new QueryWrapper<User>().eq("role", "parent")), taskExecutor);
|
|
|
|
|
- CompletableFuture<Long> childFuture = CompletableFuture.supplyAsync(() ->
|
|
|
|
|
- familyMemberMapper.selectCount(null), taskExecutor);
|
|
|
|
|
- CompletableFuture<Long> teacherFuture = CompletableFuture.supplyAsync(() ->
|
|
|
|
|
- userMapper.selectCount(new QueryWrapper<User>().eq("role", "teacher")), taskExecutor);
|
|
|
|
|
- CompletableFuture<Long> pendingGuideFuture = CompletableFuture.supplyAsync(() ->
|
|
|
|
|
- userMapper.selectCount(new QueryWrapper<User>().eq("role", "teacher").eq("teacher_status", "pending")), taskExecutor);
|
|
|
|
|
- CompletableFuture<Long> pendingPackageFuture = CompletableFuture.supplyAsync(() ->
|
|
|
|
|
- guidePackageMapper.selectCount(new QueryWrapper<com.etotem.cfc.entity.GuidePackage>()
|
|
|
|
|
- .eq("status", "pending")), taskExecutor);
|
|
|
|
|
|
|
+ CompletableFuture<Long> familyFuture = CompletableFuture.supplyAsync(() -> familyMapper.selectCount(null), taskExecutor);
|
|
|
|
|
+ CompletableFuture<Long> parentFuture = CompletableFuture.supplyAsync(() -> userMapper.selectCount(new QueryWrapper<User>().eq("role", "parent")), taskExecutor);
|
|
|
|
|
+ CompletableFuture<Long> childFuture = CompletableFuture.supplyAsync(() -> familyMemberMapper.selectCount(null), taskExecutor);
|
|
|
|
|
+ CompletableFuture<Long> teacherFuture = CompletableFuture.supplyAsync(() -> userMapper.selectCount(new QueryWrapper<User>().eq("role", "teacher")), taskExecutor);
|
|
|
|
|
+ CompletableFuture<Long> pendingGuideFuture = CompletableFuture.supplyAsync(() -> userMapper.selectCount(new QueryWrapper<User>().eq("role", "teacher").eq("teacher_status", "pending")), taskExecutor);
|
|
|
|
|
+ CompletableFuture<Long> pendingPackageFuture = CompletableFuture.supplyAsync(() -> guidePackageMapper.selectCount(new QueryWrapper<com.etotem.cfc.entity.GuidePackage>().eq("status", "pending")), taskExecutor);
|
|
|
CompletableFuture<List<Task>> recentTasksFuture = CompletableFuture.supplyAsync(() -> {
|
|
CompletableFuture<List<Task>> recentTasksFuture = CompletableFuture.supplyAsync(() -> {
|
|
|
QueryWrapper<Task> q = new QueryWrapper<Task>().orderByDesc("created_at").last("LIMIT 5");
|
|
QueryWrapper<Task> q = new QueryWrapper<Task>().orderByDesc("created_at").last("LIMIT 5");
|
|
|
SortUtil.applySort(q);
|
|
SortUtil.applySort(q);
|
|
|
return taskMapper.selectList(q);
|
|
return taskMapper.selectList(q);
|
|
|
}, taskExecutor);
|
|
}, taskExecutor);
|
|
|
-
|
|
|
|
|
CompletableFuture<Long> pendingRefundFuture = CompletableFuture.supplyAsync(() ->
|
|
CompletableFuture<Long> pendingRefundFuture = CompletableFuture.supplyAsync(() ->
|
|
|
- afterSalesRequestMapper.selectCount(new QueryWrapper<AfterSalesRequest>()
|
|
|
|
|
- .eq("status", "pending")), taskExecutor);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ afterSalesRequestMapper.selectCount(new QueryWrapper<AfterSalesRequest>().eq("status", "pending")), taskExecutor);
|
|
|
try {
|
|
try {
|
|
|
data.put("totalFamilies", familyFuture.get());
|
|
data.put("totalFamilies", familyFuture.get());
|
|
|
data.put("totalParents", parentFuture.get());
|
|
data.put("totalParents", parentFuture.get());
|
|
@@ -116,91 +90,33 @@ public class StatsController {
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
return Result.error("获取仪表盘数据失败");
|
|
return Result.error("获取仪表盘数据失败");
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
return Result.success(data);
|
|
return Result.success(data);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- @Operation(summary = "孩子任务完成率")
|
|
|
|
|
- @PostMapping("/child-completion")
|
|
|
|
|
- public Result<Map<String, Object>> childCompletion(
|
|
|
|
|
- @RequestAttribute("userId") Long userId,
|
|
|
|
|
- @RequestBody Map<String, Object> params) {
|
|
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
|
|
- if (user == null) return Result.error("用户不存在");
|
|
|
|
|
-
|
|
|
|
|
- Long memberId = ParamUtils.getLong(params.get("memberId"));
|
|
|
|
|
- Date startDate = parseDate(params.get("startDate"));
|
|
|
|
|
- Date endDate = parseDate(params.get("endDate"));
|
|
|
|
|
-
|
|
|
|
|
- Map<String, Object> stats = taskStatsService.getChildStats(memberId, startDate, endDate);
|
|
|
|
|
- return Result.success(stats);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- @Operation(summary = "家庭任务完成率")
|
|
|
|
|
- @PostMapping("/family-completion")
|
|
|
|
|
- public Result<Map<String, Object>> familyCompletion(
|
|
|
|
|
- @RequestAttribute("userId") Long userId,
|
|
|
|
|
- @RequestBody Map<String, Object> params) {
|
|
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
|
|
- if (user == null) return Result.error("用户不存在");
|
|
|
|
|
- if (user.getFamilyId() == null) return Result.noFamily("请先创建或加入家庭");
|
|
|
|
|
-
|
|
|
|
|
- Date startDate = parseDate(params.get("startDate"));
|
|
|
|
|
- Date endDate = parseDate(params.get("endDate"));
|
|
|
|
|
-
|
|
|
|
|
- Map<String, Object> stats = taskStatsService.getFamilyStats(user.getFamilyId(), startDate, endDate);
|
|
|
|
|
- return Result.success(stats);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private Date parseDate(Object value) {
|
|
|
|
|
- if (value == null) return null;
|
|
|
|
|
- try {
|
|
|
|
|
- return new SimpleDateFormat("yyyy-MM-dd").parse(value.toString());
|
|
|
|
|
- } catch (ParseException e) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
@Operation(summary = "管理后台数据总览(新)")
|
|
@Operation(summary = "管理后台数据总览(新)")
|
|
|
@PostMapping("/overview")
|
|
@PostMapping("/overview")
|
|
|
public Result<Map<String, Object>> getOverview() {
|
|
public Result<Map<String, Object>> getOverview() {
|
|
|
Map<String, Object> data = new HashMap<>();
|
|
Map<String, Object> data = new HashMap<>();
|
|
|
try {
|
|
try {
|
|
|
- CompletableFuture<Long> familyFuture = CompletableFuture.supplyAsync(() ->
|
|
|
|
|
- familyMapper.selectCount(null), taskExecutor);
|
|
|
|
|
- CompletableFuture<Long> parentFuture = CompletableFuture.supplyAsync(() ->
|
|
|
|
|
- userMapper.selectCount(new QueryWrapper<User>().eq("role", "parent")), taskExecutor);
|
|
|
|
|
- CompletableFuture<Long> childFuture = CompletableFuture.supplyAsync(() ->
|
|
|
|
|
- familyMemberMapper.selectCount(null), taskExecutor);
|
|
|
|
|
- CompletableFuture<Long> teacherFuture = CompletableFuture.supplyAsync(() ->
|
|
|
|
|
- userMapper.selectCount(new QueryWrapper<User>().eq("role", "teacher")), taskExecutor);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ CompletableFuture<Long> familyFuture = CompletableFuture.supplyAsync(() -> familyMapper.selectCount(null), taskExecutor);
|
|
|
|
|
+ CompletableFuture<Long> parentFuture = CompletableFuture.supplyAsync(() -> userMapper.selectCount(new QueryWrapper<User>().eq("role", "parent")), taskExecutor);
|
|
|
|
|
+ CompletableFuture<Long> childFuture = CompletableFuture.supplyAsync(() -> familyMemberMapper.selectCount(null), taskExecutor);
|
|
|
|
|
+ CompletableFuture<Long> teacherFuture = CompletableFuture.supplyAsync(() -> userMapper.selectCount(new QueryWrapper<User>().eq("role", "teacher")), taskExecutor);
|
|
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
String monthStart = sdf.format(new Date()).substring(0, 7) + "-01";
|
|
String monthStart = sdf.format(new Date()).substring(0, 7) + "-01";
|
|
|
Date monthStartDate = sdf.parse(monthStart);
|
|
Date monthStartDate = sdf.parse(monthStart);
|
|
|
CompletableFuture<Long> monthFamiliesFuture = CompletableFuture.supplyAsync(() ->
|
|
CompletableFuture<Long> monthFamiliesFuture = CompletableFuture.supplyAsync(() ->
|
|
|
- familyMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<Family>()
|
|
|
|
|
- .ge(Family::getCreatedAt, monthStartDate)), taskExecutor);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ familyMapper.selectCount(new LambdaQueryWrapper<Family>().ge(Family::getCreatedAt, monthStartDate)), taskExecutor);
|
|
|
CompletableFuture<Long> monthParentsFuture = CompletableFuture.supplyAsync(() ->
|
|
CompletableFuture<Long> monthParentsFuture = CompletableFuture.supplyAsync(() ->
|
|
|
- userMapper.selectCount(new QueryWrapper<User>()
|
|
|
|
|
- .eq("role", "parent")
|
|
|
|
|
- .ge("created_at", monthStartDate)), taskExecutor);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ userMapper.selectCount(new QueryWrapper<User>().eq("role", "parent").ge("created_at", monthStartDate)), taskExecutor);
|
|
|
CompletableFuture<Long> pendingGuideFuture = CompletableFuture.supplyAsync(() ->
|
|
CompletableFuture<Long> pendingGuideFuture = CompletableFuture.supplyAsync(() ->
|
|
|
- userMapper.selectCount(new QueryWrapper<User>()
|
|
|
|
|
- .eq("role", "teacher").eq("teacher_status", "pending")), taskExecutor);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ userMapper.selectCount(new QueryWrapper<User>().eq("role", "teacher").eq("teacher_status", "pending")), taskExecutor);
|
|
|
CompletableFuture<Long> pendingPackageFuture = CompletableFuture.supplyAsync(() ->
|
|
CompletableFuture<Long> pendingPackageFuture = CompletableFuture.supplyAsync(() ->
|
|
|
- guidePackageMapper.selectCount(new QueryWrapper<com.etotem.cfc.entity.GuidePackage>()
|
|
|
|
|
- .eq("status", "pending")), taskExecutor);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ guidePackageMapper.selectCount(new QueryWrapper<com.etotem.cfc.entity.GuidePackage>().eq("status", "pending")), taskExecutor);
|
|
|
Calendar cal = Calendar.getInstance();
|
|
Calendar cal = Calendar.getInstance();
|
|
|
cal.add(Calendar.DAY_OF_MONTH, -7);
|
|
cal.add(Calendar.DAY_OF_MONTH, -7);
|
|
|
CompletableFuture<Long> activeUsersFuture = CompletableFuture.supplyAsync(() ->
|
|
CompletableFuture<Long> activeUsersFuture = CompletableFuture.supplyAsync(() ->
|
|
|
- userMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<User>()
|
|
|
|
|
- .ge(User::getUpdatedAt, cal.getTime())), taskExecutor);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ userMapper.selectCount(new LambdaQueryWrapper<User>().ge(User::getUpdatedAt, cal.getTime())), taskExecutor);
|
|
|
data.put("totalFamilies", familyFuture.get());
|
|
data.put("totalFamilies", familyFuture.get());
|
|
|
data.put("totalParents", parentFuture.get());
|
|
data.put("totalParents", parentFuture.get());
|
|
|
data.put("totalChildren", childFuture.get());
|
|
data.put("totalChildren", childFuture.get());
|
|
@@ -216,99 +132,88 @@ public class StatsController {
|
|
|
return Result.success(data);
|
|
return Result.success(data);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // ── 任务完成率 ──
|
|
|
|
|
+
|
|
|
|
|
+ @Operation(summary = "孩子任务完成率")
|
|
|
|
|
+ @PostMapping("/child-completion")
|
|
|
|
|
+ public Result<Map<String, Object>> childCompletion(
|
|
|
|
|
+ @RequestAttribute("userId") Long userId,
|
|
|
|
|
+ @RequestBody Map<String, Object> params) {
|
|
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
|
|
+ if (user == null) return Result.error("用户不存在");
|
|
|
|
|
+ Long memberId = ParamUtils.getLong(params.get("memberId"));
|
|
|
|
|
+ Date startDate = parseDate(params.get("startDate"));
|
|
|
|
|
+ Date endDate = parseDate(params.get("endDate"));
|
|
|
|
|
+ return Result.success(taskStatsService.getChildStats(memberId, startDate, endDate));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Operation(summary = "家庭任务完成率")
|
|
|
|
|
+ @PostMapping("/family-completion")
|
|
|
|
|
+ public Result<Map<String, Object>> familyCompletion(
|
|
|
|
|
+ @RequestAttribute("userId") Long userId,
|
|
|
|
|
+ @RequestBody Map<String, Object> params) {
|
|
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
|
|
+ if (user == null) return Result.error("用户不存在");
|
|
|
|
|
+ if (user.getFamilyId() == null) return Result.noFamily("请先创建或加入家庭");
|
|
|
|
|
+ Date startDate = parseDate(params.get("startDate"));
|
|
|
|
|
+ Date endDate = parseDate(params.get("endDate"));
|
|
|
|
|
+ return Result.success(taskStatsService.getFamilyStats(user.getFamilyId(), startDate, endDate));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ── 收入统计 ──
|
|
|
|
|
+
|
|
|
@Operation(summary = "收入统计")
|
|
@Operation(summary = "收入统计")
|
|
|
@PostMapping("/revenue")
|
|
@PostMapping("/revenue")
|
|
|
public Result<Map<String, Object>> getRevenueStats(@RequestBody Map<String, Object> params) {
|
|
public Result<Map<String, Object>> getRevenueStats(@RequestBody Map<String, Object> params) {
|
|
|
Map<String, Object> data = new HashMap<>();
|
|
Map<String, Object> data = new HashMap<>();
|
|
|
try {
|
|
try {
|
|
|
Calendar cal = Calendar.getInstance();
|
|
Calendar cal = Calendar.getInstance();
|
|
|
- cal.set(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
|
- cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
|
|
- cal.set(Calendar.MINUTE, 0);
|
|
|
|
|
- cal.set(Calendar.SECOND, 0);
|
|
|
|
|
|
|
+ cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
|
|
+ cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0);
|
|
|
Date monthStart = cal.getTime();
|
|
Date monthStart = cal.getTime();
|
|
|
- CompletableFuture<Double> monthCommissionFuture = CompletableFuture.supplyAsync(() -> {
|
|
|
|
|
- try {
|
|
|
|
|
- List<CommissionRecord> records = commissionRecordMapper.selectList(
|
|
|
|
|
- new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<CommissionRecord>()
|
|
|
|
|
- .ge(CommissionRecord::getCreatedAt, monthStart)
|
|
|
|
|
- .eq(CommissionRecord::getStatus, "settled"));
|
|
|
|
|
- return records.stream()
|
|
|
|
|
- .filter(r -> r.getCommissionAmount() != null)
|
|
|
|
|
- .mapToDouble(CommissionRecord::getCommissionAmount)
|
|
|
|
|
- .sum();
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- return 0.0;
|
|
|
|
|
- }
|
|
|
|
|
- }, taskExecutor);
|
|
|
|
|
-
|
|
|
|
|
- CompletableFuture<Double> totalCommissionFuture = CompletableFuture.supplyAsync(() -> {
|
|
|
|
|
- try {
|
|
|
|
|
- List<CommissionRecord> records = commissionRecordMapper.selectList(
|
|
|
|
|
- new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<CommissionRecord>()
|
|
|
|
|
- .eq(CommissionRecord::getStatus, "settled"));
|
|
|
|
|
- return records.stream()
|
|
|
|
|
- .filter(r -> r.getCommissionAmount() != null)
|
|
|
|
|
- .mapToDouble(CommissionRecord::getCommissionAmount)
|
|
|
|
|
- .sum();
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- return 0.0;
|
|
|
|
|
- }
|
|
|
|
|
- }, taskExecutor);
|
|
|
|
|
-
|
|
|
|
|
- CompletableFuture<Double> pendingCommissionFuture = CompletableFuture.supplyAsync(() -> {
|
|
|
|
|
- try {
|
|
|
|
|
- List<CommissionRecord> records = commissionRecordMapper.selectList(
|
|
|
|
|
- new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<CommissionRecord>()
|
|
|
|
|
- .eq(CommissionRecord::getStatus, "pending"));
|
|
|
|
|
- return records.stream()
|
|
|
|
|
- .filter(r -> r.getCommissionAmount() != null)
|
|
|
|
|
- .mapToDouble(CommissionRecord::getCommissionAmount)
|
|
|
|
|
- .sum();
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- return 0.0;
|
|
|
|
|
- }
|
|
|
|
|
- }, taskExecutor);
|
|
|
|
|
-
|
|
|
|
|
- data.put("monthCommission", monthCommissionFuture.get() / 100.0);
|
|
|
|
|
- data.put("totalCommission", totalCommissionFuture.get() / 100.0);
|
|
|
|
|
- data.put("pendingCommission", pendingCommissionFuture.get() / 100.0);
|
|
|
|
|
|
|
+ CompletableFuture<Double> monthFuture = CompletableFuture.supplyAsync(() -> sumCommission(commissionRecordMapper, monthStart, true), taskExecutor);
|
|
|
|
|
+ CompletableFuture<Double> totalFuture = CompletableFuture.supplyAsync(() -> sumCommission(commissionRecordMapper, null, true), taskExecutor);
|
|
|
|
|
+ CompletableFuture<Double> pendingFuture = CompletableFuture.supplyAsync(() -> sumCommission(commissionRecordMapper, null, false), taskExecutor);
|
|
|
|
|
+ data.put("monthCommission", monthFuture.get() / 100.0);
|
|
|
|
|
+ data.put("totalCommission", totalFuture.get() / 100.0);
|
|
|
|
|
+ data.put("pendingCommission", pendingFuture.get() / 100.0);
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
- data.put("monthCommission", 0.0);
|
|
|
|
|
- data.put("totalCommission", 0.0);
|
|
|
|
|
- data.put("pendingCommission", 0.0);
|
|
|
|
|
|
|
+ data.put("monthCommission", 0.0); data.put("totalCommission", 0.0); data.put("pendingCommission", 0.0);
|
|
|
}
|
|
}
|
|
|
return Result.success(data);
|
|
return Result.success(data);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private double sumCommission(CommissionRecordMapper mapper, Date since, boolean settled) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ LambdaQueryWrapper<CommissionRecord> q = new LambdaQueryWrapper<CommissionRecord>();
|
|
|
|
|
+ if (since != null) q.ge(CommissionRecord::getCreatedAt, since);
|
|
|
|
|
+ if (settled) q.eq(CommissionRecord::getStatus, "settled"); else q.eq(CommissionRecord::getStatus, "pending");
|
|
|
|
|
+ return mapper.selectList(q).stream()
|
|
|
|
|
+ .filter(r -> r.getCommissionAmount() != null)
|
|
|
|
|
+ .mapToDouble(CommissionRecord::getCommissionAmount).sum();
|
|
|
|
|
+ } catch (Exception e) { return 0.0; }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ── 会员分布 ──
|
|
|
|
|
+
|
|
|
@Operation(summary = "会员分布统计")
|
|
@Operation(summary = "会员分布统计")
|
|
|
@PostMapping("/membership")
|
|
@PostMapping("/membership")
|
|
|
public Result<Map<String, Object>> getMembershipStats() {
|
|
public Result<Map<String, Object>> getMembershipStats() {
|
|
|
Map<String, Object> data = new HashMap<>();
|
|
Map<String, Object> data = new HashMap<>();
|
|
|
try {
|
|
try {
|
|
|
CompletableFuture<Long> freeFuture = CompletableFuture.supplyAsync(() ->
|
|
CompletableFuture<Long> freeFuture = CompletableFuture.supplyAsync(() ->
|
|
|
- userMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<User>()
|
|
|
|
|
- .or().eq(User::getMemberLevel, "FREE").isNull(User::getMemberLevel)), taskExecutor);
|
|
|
|
|
|
|
+ userMapper.selectCount(new LambdaQueryWrapper<User>().or().eq(User::getMemberLevel, "FREE").isNull(User::getMemberLevel)), taskExecutor);
|
|
|
CompletableFuture<Long> familyFuture = CompletableFuture.supplyAsync(() ->
|
|
CompletableFuture<Long> familyFuture = CompletableFuture.supplyAsync(() ->
|
|
|
- userMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<User>()
|
|
|
|
|
- .eq(User::getMemberLevel, "FAMILY")), taskExecutor);
|
|
|
|
|
|
|
+ userMapper.selectCount(new LambdaQueryWrapper<User>().eq(User::getMemberLevel, "FAMILY")), taskExecutor);
|
|
|
CompletableFuture<Long> providerFuture = CompletableFuture.supplyAsync(() ->
|
|
CompletableFuture<Long> providerFuture = CompletableFuture.supplyAsync(() ->
|
|
|
- userMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<User>()
|
|
|
|
|
- .eq(User::getMemberLevel, "PROVIDER")), taskExecutor);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ userMapper.selectCount(new LambdaQueryWrapper<User>().eq(User::getMemberLevel, "PROVIDER")), taskExecutor);
|
|
|
CompletableFuture<Long> trialFuture = CompletableFuture.supplyAsync(() ->
|
|
CompletableFuture<Long> trialFuture = CompletableFuture.supplyAsync(() ->
|
|
|
- trialMembershipMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<TrialMembership>()
|
|
|
|
|
- .eq(TrialMembership::getStatus, "ACTIVE")), taskExecutor);
|
|
|
|
|
|
|
+ trialMembershipMapper.selectCount(new LambdaQueryWrapper<TrialMembership>().eq(TrialMembership::getStatus, "ACTIVE")), taskExecutor);
|
|
|
Calendar cal = Calendar.getInstance();
|
|
Calendar cal = Calendar.getInstance();
|
|
|
- cal.set(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
|
- cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
|
|
- cal.set(Calendar.MINUTE, 0);
|
|
|
|
|
- cal.set(Calendar.SECOND, 0);
|
|
|
|
|
|
|
+ cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0);
|
|
|
CompletableFuture<Long> monthUpgradesFuture = CompletableFuture.supplyAsync(() ->
|
|
CompletableFuture<Long> monthUpgradesFuture = CompletableFuture.supplyAsync(() ->
|
|
|
- memberUpgradeRecordMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MemberUpgradeRecord>()
|
|
|
|
|
- .eq(MemberUpgradeRecord::getToLevel, "FAMILY")
|
|
|
|
|
- .ge(MemberUpgradeRecord::getCreatedAt, cal.getTime())), taskExecutor);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ memberUpgradeRecordMapper.selectCount(new LambdaQueryWrapper<MemberUpgradeRecord>()
|
|
|
|
|
+ .eq(MemberUpgradeRecord::getToLevel, "FAMILY").ge(MemberUpgradeRecord::getCreatedAt, cal.getTime())), taskExecutor);
|
|
|
data.put("freeCount", freeFuture.get());
|
|
data.put("freeCount", freeFuture.get());
|
|
|
data.put("familyCount", familyFuture.get());
|
|
data.put("familyCount", familyFuture.get());
|
|
|
data.put("providerCount", providerFuture.get());
|
|
data.put("providerCount", providerFuture.get());
|
|
@@ -320,6 +225,8 @@ public class StatsController {
|
|
|
return Result.success(data);
|
|
return Result.success(data);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // ── 每日趋势 ──
|
|
|
|
|
+
|
|
|
@Operation(summary = "每日数据趋势(近30天)")
|
|
@Operation(summary = "每日数据趋势(近30天)")
|
|
|
@PostMapping("/trend")
|
|
@PostMapping("/trend")
|
|
|
public Result<Map<String, Object>> getTrend(@RequestBody Map<String, Object> params) {
|
|
public Result<Map<String, Object>> getTrend(@RequestBody Map<String, Object> params) {
|
|
@@ -328,9 +235,7 @@ public class StatsController {
|
|
|
Calendar cal = Calendar.getInstance();
|
|
Calendar cal = Calendar.getInstance();
|
|
|
cal.add(Calendar.DAY_OF_MONTH, -30);
|
|
cal.add(Calendar.DAY_OF_MONTH, -30);
|
|
|
Date thirtyDaysAgo = cal.getTime();
|
|
Date thirtyDaysAgo = cal.getTime();
|
|
|
-
|
|
|
|
|
- List<Family> families = familyMapper.selectList(
|
|
|
|
|
- new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<Family>().ge(Family::getCreatedAt, thirtyDaysAgo));
|
|
|
|
|
|
|
+ List<Family> families = familyMapper.selectList(new LambdaQueryWrapper<Family>().ge(Family::getCreatedAt, thirtyDaysAgo));
|
|
|
Map<String, Long> familyTrend = new LinkedHashMap<>();
|
|
Map<String, Long> familyTrend = new LinkedHashMap<>();
|
|
|
SimpleDateFormat dayFmt = new SimpleDateFormat("MM-dd");
|
|
SimpleDateFormat dayFmt = new SimpleDateFormat("MM-dd");
|
|
|
for (Family f : families) {
|
|
for (Family f : families) {
|
|
@@ -340,9 +245,7 @@ public class StatsController {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
data.put("familyTrend", familyTrend);
|
|
data.put("familyTrend", familyTrend);
|
|
|
-
|
|
|
|
|
- List<User> users = userMapper.selectList(
|
|
|
|
|
- new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<User>().ge(User::getCreatedAt, thirtyDaysAgo));
|
|
|
|
|
|
|
+ List<User> users = userMapper.selectList(new LambdaQueryWrapper<User>().ge(User::getCreatedAt, thirtyDaysAgo));
|
|
|
Map<String, Long> userTrend = new LinkedHashMap<>();
|
|
Map<String, Long> userTrend = new LinkedHashMap<>();
|
|
|
for (User u : users) {
|
|
for (User u : users) {
|
|
|
if (u.getCreatedAt() != null) {
|
|
if (u.getCreatedAt() != null) {
|
|
@@ -357,4 +260,131 @@ public class StatsController {
|
|
|
}
|
|
}
|
|
|
return Result.success(data);
|
|
return Result.success(data);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // ── 孩子数据概览 ──
|
|
|
|
|
+
|
|
|
|
|
+ @Operation(summary = "孩子数据概览")
|
|
|
|
|
+ @PostMapping("/child/overview")
|
|
|
|
|
+ public Result<Map<String, Object>> childOverview(@RequestBody Map<String, Object> body) {
|
|
|
|
|
+ Long memberId = ParamUtils.getLong(body.get("memberId"));
|
|
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
|
|
+ FamilyMember child = familyMemberMapper.selectById(memberId);
|
|
|
|
|
+ result.put("totalPoints", child != null ? child.getTotalPoints() : 0);
|
|
|
|
|
+
|
|
|
|
|
+ Calendar monthCal = Calendar.getInstance();
|
|
|
|
|
+ monthCal.set(Calendar.DAY_OF_MONTH, 1); monthCal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
|
|
+ monthCal.set(Calendar.MINUTE, 0); monthCal.set(Calendar.SECOND, 0); monthCal.set(Calendar.MILLISECOND, 0);
|
|
|
|
|
+ Date monthStart = monthCal.getTime();
|
|
|
|
|
+ List<PointsLog> monthLogs = pointsLogMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<PointsLog>().eq(PointsLog::getChildId, memberId)
|
|
|
|
|
+ .ge(PointsLog::getCreatedAt, monthStart).orderByAsc(PointsLog::getCreatedAt));
|
|
|
|
|
+ Set<String> activeDays = new HashSet<>();
|
|
|
|
|
+ for (PointsLog log : monthLogs) {
|
|
|
|
|
+ if (log.getCreatedAt() != null) activeDays.add(new SimpleDateFormat("yyyy-MM-dd").format(log.getCreatedAt()));
|
|
|
|
|
+ }
|
|
|
|
|
+ result.put("monthActiveDays", activeDays.size());
|
|
|
|
|
+
|
|
|
|
|
+ List<Task> tasks = taskService.getAllTasksByChild(memberId);
|
|
|
|
|
+ long taskTotal = tasks.size();
|
|
|
|
|
+ long taskCompleted = tasks.stream().filter(t -> "completed".equals(t.getStatus())).count();
|
|
|
|
|
+ long taskPending = tasks.stream().filter(t -> "pending".equals(t.getStatus())).count();
|
|
|
|
|
+ long taskInReview = tasks.stream().filter(t -> "review".equals(t.getStatus())).count();
|
|
|
|
|
+ Map<String, Object> taskStats = new HashMap<>();
|
|
|
|
|
+ taskStats.put("total", taskTotal); taskStats.put("completed", taskCompleted);
|
|
|
|
|
+ taskStats.put("pending", taskPending); taskStats.put("inReview", taskInReview);
|
|
|
|
|
+ taskStats.put("completionRate", taskTotal > 0 ? (int)(taskCompleted * 10000 / taskTotal) / 100 : 0);
|
|
|
|
|
+ result.put("taskStats", taskStats);
|
|
|
|
|
+
|
|
|
|
|
+ List<GameRecord> records = gameRecordService.listByChildId(memberId);
|
|
|
|
|
+ int gameTotalPoints = records.stream().mapToInt(GameRecord::getPointsEarned).sum();
|
|
|
|
|
+ int bestScore = records.stream().mapToInt(GameRecord::getScore).max().orElse(0);
|
|
|
|
|
+ int totalTime = records.stream().mapToInt(GameRecord::getCompletionTime).sum();
|
|
|
|
|
+ Map<String, Object> gameStats = new HashMap<>();
|
|
|
|
|
+ gameStats.put("total", records.size()); gameStats.put("totalPoints", gameTotalPoints);
|
|
|
|
|
+ gameStats.put("bestScore", bestScore); gameStats.put("totalTime", totalTime);
|
|
|
|
|
+ result.put("gameStats", gameStats);
|
|
|
|
|
+
|
|
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
|
|
+ cal.add(Calendar.DAY_OF_YEAR, -7);
|
|
|
|
|
+ List<PointsLog> recentLogs = pointsLogMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<PointsLog>().eq(PointsLog::getChildId, memberId)
|
|
|
|
|
+ .ge(PointsLog::getCreatedAt, cal.getTime()).orderByAsc(PointsLog::getCreatedAt));
|
|
|
|
|
+ List<Map<String, Object>> pointsTrend = new ArrayList<>();
|
|
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
|
|
+ for (int i = 6; i >= 0; i--) {
|
|
|
|
|
+ Calendar dayCal = Calendar.getInstance();
|
|
|
|
|
+ dayCal.add(Calendar.DAY_OF_YEAR, -i);
|
|
|
|
|
+ dayCal.set(Calendar.HOUR_OF_DAY, 0); dayCal.set(Calendar.MINUTE, 0);
|
|
|
|
|
+ dayCal.set(Calendar.SECOND, 0); dayCal.set(Calendar.MILLISECOND, 0);
|
|
|
|
|
+ Date dayStart = dayCal.getTime();
|
|
|
|
|
+ dayCal.set(Calendar.HOUR_OF_DAY, 23); dayCal.set(Calendar.MINUTE, 59); dayCal.set(Calendar.SECOND, 59);
|
|
|
|
|
+ Date dayEnd = dayCal.getTime();
|
|
|
|
|
+ int dayPoints = recentLogs.stream()
|
|
|
|
|
+ .filter(l -> l.getCreatedAt() != null && l.getCreatedAt().after(dayStart) && l.getCreatedAt().before(dayEnd))
|
|
|
|
|
+ .mapToInt(PointsLog::getAmount).sum();
|
|
|
|
|
+ Map<String, Object> point = new HashMap<>();
|
|
|
|
|
+ point.put("date", sdf.format(dayStart)); point.put("points", dayPoints);
|
|
|
|
|
+ pointsTrend.add(point);
|
|
|
|
|
+ }
|
|
|
|
|
+ result.put("pointsTrend", pointsTrend);
|
|
|
|
|
+ return Result.success(result);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ── 家庭汇总 ──
|
|
|
|
|
+
|
|
|
|
|
+ @Operation(summary = "家庭汇总统计")
|
|
|
|
|
+ @PostMapping("/family/summary")
|
|
|
|
|
+ public Result<Map<String, Object>> familySummary(@RequestBody Map<String, Object> body,
|
|
|
|
|
+ @RequestAttribute(value = "familyId", required = false) Long familyId) {
|
|
|
|
|
+ if (familyId == null) return Result.noFamily("请先创建或加入家庭");
|
|
|
|
|
+ List<FamilyMember> children = familyMemberMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<FamilyMember>().eq(FamilyMember::getFamilyId, familyId));
|
|
|
|
|
+ List<Map<String, Object>> childrenStats = new ArrayList<>();
|
|
|
|
|
+ int familyTotalTasks = 0, familyCompletedTasks = 0, familyTotalGames = 0;
|
|
|
|
|
+ for (FamilyMember child : children) {
|
|
|
|
|
+ Map<String, Object> s = new HashMap<>();
|
|
|
|
|
+ s.put("memberId", child.getId()); s.put("childName", child.getNickname());
|
|
|
|
|
+ List<Task> tasks = taskService.getAllTasksByChild(child.getId());
|
|
|
|
|
+ long c = tasks.stream().filter(t -> "completed".equals(t.getStatus())).count();
|
|
|
|
|
+ s.put("taskTotal", tasks.size()); s.put("taskCompleted", c); s.put("points", child.getTotalPoints());
|
|
|
|
|
+ s.put("gameTotal", gameRecordService.listByChildId(child.getId()).size());
|
|
|
|
|
+ familyTotalTasks += tasks.size(); familyCompletedTasks += c;
|
|
|
|
|
+ familyTotalGames += ((Number)s.get("gameTotal")).intValue();
|
|
|
|
|
+ childrenStats.add(s);
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
|
|
+ result.put("children", childrenStats);
|
|
|
|
|
+ result.put("familyTotalTasks", familyTotalTasks);
|
|
|
|
|
+ result.put("familyCompletedTasks", familyCompletedTasks);
|
|
|
|
|
+ result.put("familyCompletionRate", familyTotalTasks > 0 ? (int)(familyCompletedTasks * 10000 / familyTotalTasks) / 100 : 0);
|
|
|
|
|
+ result.put("familyTotalGames", familyTotalGames);
|
|
|
|
|
+ result.put("childCount", children.size());
|
|
|
|
|
+ return Result.success(result);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ── 积分排行 ──
|
|
|
|
|
+
|
|
|
|
|
+ @Operation(summary = "积分排行榜(家庭内)")
|
|
|
|
|
+ @PostMapping("/points/ranking")
|
|
|
|
|
+ public Result<List<Map<String, Object>>> pointsRanking(@RequestBody Map<String, Object> body,
|
|
|
|
|
+ @RequestAttribute(value = "familyId", required = false) Long familyId) {
|
|
|
|
|
+ if (familyId == null) return Result.noFamily("请先创建或加入家庭");
|
|
|
|
|
+ List<FamilyMember> children = familyMemberMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<FamilyMember>().eq(FamilyMember::getFamilyId, familyId).orderByDesc(FamilyMember::getTotalPoints));
|
|
|
|
|
+ List<Map<String, Object>> ranking = new ArrayList<>();
|
|
|
|
|
+ int rank = 1;
|
|
|
|
|
+ for (FamilyMember child : children) {
|
|
|
|
|
+ Map<String, Object> item = new HashMap<>();
|
|
|
|
|
+ item.put("rank", rank++); item.put("memberId", child.getId());
|
|
|
|
|
+ item.put("childName", child.getNickname()); item.put("points", child.getTotalPoints());
|
|
|
|
|
+ ranking.add(item);
|
|
|
|
|
+ }
|
|
|
|
|
+ return Result.success(ranking);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Date parseDate(Object value) {
|
|
|
|
|
+ if (value == null) return null;
|
|
|
|
|
+ try { return new SimpleDateFormat("yyyy-MM-dd").parse(value.toString()); }
|
|
|
|
|
+ catch (ParseException e) { return null; }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|