| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- package com.etotem.cfc.controller.guide;
- import javax.annotation.Resource;
- import io.swagger.v3.oas.annotations.responses.ApiResponse;
- import io.swagger.v3.oas.annotations.responses.ApiResponses;
- import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
- import com.etotem.cfc.common.Result;
- import com.etotem.cfc.dto.TaskReviewDTO;
- import com.etotem.cfc.entity.FamilyMember;
- import com.etotem.cfc.entity.GuideFamily;
- import com.etotem.cfc.entity.Task;
- import com.etotem.cfc.entity.User;
- import com.etotem.cfc.mapper.FamilyMemberMapper;
- import com.etotem.cfc.mapper.TaskMapper;
- import com.etotem.cfc.mapper.UserMapper;
- import com.etotem.cfc.service.GuideFamilyAccessGuard;
- import com.etotem.cfc.service.GuideFamilyService;
- import com.etotem.cfc.service.MembershipService;
- import com.etotem.cfc.service.TaskService;
- import io.swagger.v3.oas.annotations.Operation;
- import io.swagger.v3.oas.annotations.tags.Tag;
- import org.springframework.web.bind.annotation.*;
- import java.time.LocalDate;
- import java.time.ZoneId;
- import java.util.*;
- import java.util.stream.Collectors;
- import com.etotem.cfc.util.ParamUtils;
- @Tag(name = "指导师-家庭任务管理", description = "指导师查看家庭任务、审核任务等接口")
- @RestController
- @RequestMapping("/api/guide/families")
- public class GuideFamilyTaskController {
- @Resource
- private TaskService taskService;
- @Resource
- private FamilyMemberMapper familyMemberMapper;
- @Resource
- private TaskMapper taskMapper;
- @Resource
- private UserMapper userMapper;
- @Resource
- private MembershipService membershipService;
- @Resource
- private GuideFamilyService guideFamilyService;
- @Resource
- private GuideFamilyAccessGuard guideFamilyAccessGuard;
- /**
- * 规划师解除家庭绑定(仅限解除属于自己的绑定)
- */
- @Operation(summary = "规划师解除家庭绑定")
- @PostMapping("/unbind")
- public Result<Map<String, Object>> unbindFamily(@RequestAttribute("userId") Long guideId,
- @RequestAttribute("role") String role,
- @RequestBody Map<String, Object> params) {
- if (!"teacher".equals(role)) {
- return Result.error("Access denied");
- }
- Long id = ParamUtils.getLong(params.get("id"));
- if (id == null) {
- return Result.error("缺少绑定记录ID");
- }
- try {
- GuideFamily gf = guideFamilyService.unbindByGuide(id, guideId);
- Map<String, Object> result = new HashMap<>();
- result.put("familyId", gf.getFamilyId());
- result.put("serviceType", gf.getServiceType());
- return Result.success(result);
- } catch (RuntimeException e) {
- return Result.error(400, e.getMessage());
- }
- }
- /**
- * 会员任务校验:task.memberOnly==1 且家庭非会员 → 拒绝(提示开通会员)
- */
- private Result<?> checkMemberOnly(Long taskId) {
- Task task = taskMapper.selectById(taskId);
- if (task == null) {
- return Result.error("任务不存在");
- }
- if (task.getMemberOnly() != null && task.getMemberOnly() == 1) {
- com.etotem.cfc.dto.MembershipLevelDTO level = membershipService.getCurrentLevel(task.getFamilyId());
- boolean isMember = level != null && !"FREE".equals(level.getLevelCode());
- if (!isMember) {
- return Result.error("该任务是会员任务,请先开通会员后再操作");
- }
- }
- return null;
- }
- /**
- * 获取指导师关联的家庭列表(带简要任务统计)
- */
- @Operation(summary = "获取关联家庭列表")
- @ApiResponses({
- @ApiResponse(responseCode = "200", description = "成功返回家庭列表"),
- @ApiResponse(responseCode = "403", description = "Access denied - 非教师"),
- @ApiResponse(responseCode = "401", description = "未登录")
- })
- @PostMapping("/bound-families")
- public Result<List<Map<String, Object>>> getBoundFamilies(@RequestAttribute("userId") Long guideId, @RequestAttribute("role") String role) {
- if (!"teacher".equals(role)) { return Result.error("Access denied"); }
- // 获取当前指导师信息
- User guide = userMapper.selectById(guideId);
- if (guide == null || guide.getTeacherFamilyIds() == null || guide.getTeacherFamilyIds().isEmpty()) {
- return Result.success(new ArrayList<>());
- }
- // 解析家庭ID列表(逗号分隔)
- List<Long> familyIds = Arrays.stream(guide.getTeacherFamilyIds().split(","))
- .map(String::trim)
- .filter(s -> !s.isEmpty())
- .map(Long::parseLong)
- .collect(Collectors.toList());
- if (familyIds.isEmpty()) {
- return Result.success(new ArrayList<>());
- }
- List<Map<String, Object>> result = new ArrayList<>();
- for (Long familyId : familyIds) {
- // 获取家庭的家长用户
- List<User> parents = userMapper.selectList(
- new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<User>()
- .eq("family_id", familyId)
- .eq("role", "parent")
- );
- if (parents.isEmpty()) {
- continue;
- }
- User parent = parents.get(0);
- Map<String, Object> item = new HashMap<>();
- item.put("familyId", familyId);
- item.put("familyName", "家庭" + familyId);
- item.put("parentName", parent.getNickname());
- // 获取家庭孩子列表
- List<FamilyMember> children = familyMemberMapper.selectList(
- new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<FamilyMember>()
- .eq("family_id", familyId)
- );
- item.put("childrenCount", children.size());
- // 获取待审核任务数量
- long pendingCount = 0;
- for (FamilyMember child : children) {
- List<Task> pendingTasks = taskService.getPendingReviewTasksForFamilyMember(child.getId());
- pendingCount += pendingTasks.size();
- }
- item.put("pendingReviewCount", pendingCount);
- result.add(item);
- }
- return Result.success(result);
- }
- /**
- * 获取指定家庭的所有孩子任务概览
- */
- @Operation(summary = "获取家庭任务概览")
- @ApiResponses({
- @ApiResponse(responseCode = "200", description = "成功返回概览"),
- @ApiResponse(responseCode = "403", description = "Access denied"),
- @ApiResponse(responseCode = "401", description = "未登录")
- })
- @PostMapping("/{familyId}/overview")
- public Result<Map<String, Object>> getFamilyTaskOverview(
- @RequestAttribute("userId") Long guideId,
- @RequestAttribute("role") String role,
- @PathVariable Long familyId) {
- if (!"teacher".equals(role)) { return Result.error("Access denied"); }
- Result<Void> denied = requireBound(guideId, familyId);
- if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
- List<FamilyMember> children = familyMemberMapper.selectList(
- new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<FamilyMember>()
- .eq("family_id", familyId)
- );
- List<Map<String, Object>> childTaskList = new ArrayList<>();
- int totalPending = 0;
- int totalCompleted = 0;
- int totalOverdue = 0;
- for (FamilyMember child : children) {
- Map<String, Object> childInfo = new HashMap<>();
- childInfo.put("memberId", child.getId());
- childInfo.put("childName", child.getNickname());
- childInfo.put("totalPoints", child.getTotalPoints());
- childInfo.put("streakDays", child.getStreakDays());
- // 获取今日任务
- List<Task> todayTasks = taskService.getTodayTasks(child.getId());
- childInfo.put("todayTaskCount", todayTasks.size());
- childInfo.put("todayCompletedCount", todayTasks.stream()
- .filter(t -> "completed".equals(t.getStatus()))
- .count());
- // 获取待审核任务
- List<Task> pendingTasks = taskService.getPendingReviewTasksForFamilyMember(child.getId());
- childInfo.put("pendingReviewCount", pendingTasks.size());
- totalPending += pendingTasks.size();
- // 统计已完成和超时
- List<Task> allTasks = taskService.getAllTasksByFamilyMember(child.getId());
- totalCompleted += allTasks.stream()
- .filter(t -> "completed".equals(t.getStatus()))
- .count();
- totalOverdue += allTasks.stream()
- .filter(t -> "overdue".equals(t.getStatus()))
- .count();
- childTaskList.add(childInfo);
- }
- Map<String, Object> result = new HashMap<>();
- result.put("familyId", familyId);
- result.put("children", childTaskList);
- result.put("totalFamilyMemberren", children.size());
- result.put("totalPendingReview", totalPending);
- result.put("totalCompleted", totalCompleted);
- result.put("totalOverdue", totalOverdue);
- return Result.success(result);
- }
- /**
- * 获取家庭孩子的任务列表
- */
- @Operation(summary = "获取孩子任务列表")
- @ApiResponses({
- @ApiResponse(responseCode = "200", description = "成功返回分页任务"),
- @ApiResponse(responseCode = "403", description = "Access denied"),
- @ApiResponse(responseCode = "401", description = "未登录")
- })
- @PostMapping("/{familyId}/children/{memberId}/tasks")
- public Result<Page<Task>> getFamilyMemberTasks(
- @RequestAttribute("userId") Long guideId,
- @PathVariable Long familyId,
- @PathVariable Long memberId,
- @RequestAttribute("role") String role,
- @RequestBody(required = false) Map<String, Object> params) {
- if (!"teacher".equals(role)) { return Result.error("Access denied"); }
- Result<Void> denied = requireBound(guideId, familyId);
- if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
- Integer page = params != null && params.get("page") != null ? Integer.valueOf(params.get("page").toString()) : 1;
- Integer size = params != null && params.get("size") != null ? Integer.valueOf(params.get("size").toString()) : 10;
- String status = params != null && params.get("status") != null ? params.get("status").toString() : null;
- Page<Task> tasks = taskService.getTaskHistoryByFamilyMember(memberId, page, size, status);
- return Result.success(tasks);
- }
- /**
- * 获取家庭孩子的待审核任务
- */
- @Operation(summary = "获取待审核任务列表")
- @ApiResponses({
- @ApiResponse(responseCode = "200", description = "成功返回待审核任务"),
- @ApiResponse(responseCode = "403", description = "Access denied"),
- @ApiResponse(responseCode = "401", description = "未登录")
- })
- @PostMapping("/{familyId}/children/{memberId}/pending-review")
- public Result<List<Task>> getPendingReviewTasks(
- @RequestAttribute("userId") Long guideId,
- @PathVariable Long familyId,
- @PathVariable Long memberId,
- @RequestAttribute("role") String role) {
- if (!"teacher".equals(role)) { return Result.error("Access denied"); }
- Result<Void> denied = requireBound(guideId, familyId);
- if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
- List<Task> tasks = taskService.getPendingReviewTasksForFamilyMember(memberId);
- return Result.success(tasks);
- }
- /**
- * 审核任务(指导师)
- */
- @Operation(summary = "审核任务")
- @ApiResponses({
- @ApiResponse(responseCode = "200", description = "审核成功返回布尔值"),
- @ApiResponse(responseCode = "403", description = "Access denied"),
- @ApiResponse(responseCode = "401", description = "未登录")
- })
- @PostMapping("/{familyId}/tasks/{taskId}/review")
- public Result<Boolean> reviewTask(
- @RequestAttribute("userId") Long guideId,
- @PathVariable Long familyId,
- @PathVariable Long taskId,
- @RequestAttribute("role") String role,
- @RequestBody TaskReviewDTO dto) {
- if (!"teacher".equals(role)) { return Result.error("Access denied"); }
- Result<Void> denied = requireBound(guideId, familyId);
- if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
- Result<?> memberCheck = checkMemberOnly(taskId);
- if (memberCheck != null) {
- return Result.error(memberCheck.getMessage());
- }
- boolean success = taskService.reviewTask(taskId, dto);
- return Result.success(success);
- }
- /**
- * 批量审核任务
- */
- @Operation(summary = "批量审核任务")
- @ApiResponses({
- @ApiResponse(responseCode = "200", description = "批量审核成功返回统计"),
- @ApiResponse(responseCode = "403", description = "Access denied"),
- @ApiResponse(responseCode = "401", description = "未登录")
- })
- @PostMapping("/{familyId}/tasks/batch-review")
- public Result<Map<String, Object>> batchReviewTasks(
- @RequestAttribute("userId") Long guideId,
- @PathVariable Long familyId,
- @RequestAttribute("role") String role,
- @RequestBody Map<String, Object> params) {
- @SuppressWarnings("unchecked")
- List<Object> rawTaskIds = (List<Object>) params.get("taskIds");
- List<Long> taskIds = new ArrayList<>();
- if (rawTaskIds != null) {
- for (Object taskId : rawTaskIds) {
- taskIds.add(Long.valueOf(taskId.toString()));
- }
- }
- Boolean approved = params.get("approved") != null ? Boolean.valueOf(params.get("approved").toString()) : Boolean.FALSE;
- if (!"teacher".equals(role)) { return Result.error("Access denied"); }
- Result<Void> denied = requireBound(guideId, familyId);
- if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
- int successCount = 0;
- int failCount = 0;
- for (Long taskId : taskIds) {
- try {
- // 会员任务校验:审核通过会员任务需家庭为会员
- Result<?> memberCheck = checkMemberOnly(taskId);
- if (memberCheck != null) {
- failCount++;
- continue;
- }
- TaskReviewDTO dto = new TaskReviewDTO();
- dto.setApproved(approved);
- dto.setComment(approved ? "指导师审核通过" : "指导师审核拒绝");
- if (taskService.reviewTask(taskId, dto)) {
- successCount++;
- } else {
- failCount++;
- }
- } catch (Exception e) {
- failCount++;
- }
- }
- Map<String, Object> result = new HashMap<>();
- result.put("successCount", successCount);
- result.put("failCount", failCount);
- return Result.success(result);
- }
- /**
- * 指导师修改任务
- */
- @Operation(summary = "修改任务")
- @ApiResponses({
- @ApiResponse(responseCode = "200", description = "修改成功"),
- @ApiResponse(responseCode = "403", description = "Access denied"),
- @ApiResponse(responseCode = "404", description = "任务不存在")
- })
- @PostMapping("/{familyId}/tasks/{taskId}")
- public Result<Boolean> updateTask(
- @RequestAttribute("userId") Long guideId,
- @PathVariable Long familyId,
- @PathVariable Long taskId,
- @RequestAttribute("role") String role,
- @RequestBody Map<String, Object> taskData) {
-
- if (!"teacher".equals(role)) { return Result.error("Access denied"); }
- Result<Void> denied = requireBound(guideId, familyId);
- if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
-
- Task task = taskMapper.selectById(taskId);
- if (task == null) {
- return Result.error("任务不存在");
- }
-
- // 验证任务属于该家庭
- if (!familyId.equals(task.getFamilyId())) {
- return Result.error("任务不属于该家庭");
- }
-
- // 更新任务字段
- if (taskData.containsKey("title")) {
- task.setTitle((String) taskData.get("title"));
- }
- if (taskData.containsKey("description")) {
- task.setDescription((String) taskData.get("description"));
- }
- if (taskData.containsKey("points")) {
- task.setPoints((Integer) taskData.get("points"));
- }
- if (taskData.containsKey("deadline")) {
- task.setDeadline(new java.util.Date((Long) taskData.get("deadline")));
- }
- if (taskData.containsKey("status")) {
- task.setStatus((String) taskData.get("status"));
- }
-
- task.setUpdatedAt(new java.util.Date());
- taskMapper.updateById(task);
-
- return Result.success(true);
- }
- /**
- * 指导师删除任务
- */
- @Operation(summary = "删除任务")
- @ApiResponses({
- @ApiResponse(responseCode = "200", description = "删除成功"),
- @ApiResponse(responseCode = "403", description = "Access denied"),
- @ApiResponse(responseCode = "404", description = "任务不存在")
- })
- @PostMapping("/{familyId}/tasks/{taskId}/delete")
- public Result<Boolean> deleteTask(
- @RequestAttribute("userId") Long guideId,
- @PathVariable Long familyId,
- @PathVariable Long taskId,
- @RequestAttribute("role") String role) {
-
- if (!"teacher".equals(role)) { return Result.error("Access denied"); }
- Result<Void> denied = requireBound(guideId, familyId);
- if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
-
- Task task = taskMapper.selectById(taskId);
- if (task == null) {
- return Result.error("任务不存在");
- }
-
- // 验证任务属于该家庭
- if (!familyId.equals(task.getFamilyId())) {
- return Result.error("任务不属于该家庭");
- }
-
- // 指导师可以删除任何任务(不只是创建者)
- task.setStatus("cancelled");
- task.setUpdatedAt(new java.util.Date());
- taskMapper.updateById(task);
-
- return Result.success(true);
- }
- /**
- * 获取规划师首页统计(实际数据)
- */
- @Operation(summary = "获取规划师首页统计")
- @PostMapping("/dashboard-stats")
- public Result<Map<String, Object>> getDashboardStats(@RequestAttribute("userId") Long guideId, @RequestAttribute("role") String role) {
- if (!"teacher".equals(role)) { return Result.error("Access denied"); }
- User guide = userMapper.selectById(guideId);
- long totalFamilies = 0;
- long todayCompleted = 0;
- long pendingReview = 0;
- if (guide != null && guide.getTeacherFamilyIds() != null && !guide.getTeacherFamilyIds().isEmpty()) {
- List<Long> familyIds = Arrays.stream(guide.getTeacherFamilyIds().split(","))
- .map(String::trim).filter(s -> !s.isEmpty()).map(Long::parseLong)
- .collect(Collectors.toList());
- totalFamilies = familyIds.size();
- // 今天0点
- Date todayStart = Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant());
- for (Long familyId : familyIds) {
- List<FamilyMember> children = familyMemberMapper.selectList(
- new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<FamilyMember>()
- .eq(FamilyMember::getFamilyId, familyId));
- for (FamilyMember child : children) {
- // 今日完成任务:status=completed 且 completedAt >= todayStart
- long completed = taskMapper.selectCount(
- new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<Task>()
- .eq(Task::getFamilyMemberId, child.getId())
- .eq(Task::getStatus, "completed")
- .ge(Task::getCompletedAt, todayStart));
- todayCompleted += completed;
- // 待审核任务
- long pending = taskMapper.selectCount(
- new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<Task>()
- .eq(Task::getFamilyMemberId, child.getId())
- .eq(Task::getNeedReview, 1)
- .eq(Task::getStatus, "pending_review"));
- pendingReview += pending;
- }
- }
- }
- Map<String, Object> stats = new HashMap<>();
- stats.put("totalFamilies", totalFamilies);
- stats.put("todayCompletedTasks", todayCompleted);
- stats.put("pendingReviewTasks", pendingReview);
- return Result.success(stats);
- }
- /**
- * 家庭归属校验;返回 null 表示放行,非 null 为 403 响应。
- */
- private Result<Void> requireBound(Long guideId, Long familyId) {
- return guideFamilyAccessGuard.checkBinding(guideId, familyId);
- }
- }
|