GuidePlanReviewController.java 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. package com.etotem.cfc.controller.guide;
  2. import com.etotem.cfc.common.Result;
  3. import com.etotem.cfc.dto.PlanApproveResultDTO;
  4. import com.etotem.cfc.entity.HealthPlan;
  5. import com.etotem.cfc.service.GuideFamilyAccessGuard;
  6. import com.etotem.cfc.service.HealthPlanService;
  7. import com.etotem.cfc.util.ParamUtils;
  8. import io.swagger.v3.oas.annotations.Operation;
  9. import io.swagger.v3.oas.annotations.tags.Tag;
  10. import org.springframework.web.bind.annotation.*;
  11. import javax.annotation.Resource;
  12. import java.util.List;
  13. import java.util.Map;
  14. @Tag(name = "指导师-客户方案审核", description = "规划师审核其绑定客户的健康方案")
  15. @RestController
  16. @RequestMapping("/api/guide/families/{familyId}/plans")
  17. public class GuidePlanReviewController {
  18. @Resource
  19. private HealthPlanService healthPlanService;
  20. @Resource
  21. private GuideFamilyAccessGuard guideFamilyAccessGuard;
  22. @Operation(summary = "待审核方案列表")
  23. @PostMapping("/pending")
  24. public Result<List<HealthPlan>> listPendingPlans(
  25. @RequestBody(required = false) Map<String, Object> params,
  26. @RequestAttribute("role") String role,
  27. @RequestAttribute("userId") Long userId,
  28. @PathVariable("familyId") Long familyId) {
  29. Result<Void> denied = guideFamilyAccessGuard.checkFamilyAccess(role, userId, familyId);
  30. if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
  31. Object rawTeacherId = params == null ? null : params.get("teacherId");
  32. Long teacherId = "admin".equals(role) ? ParamUtils.getLong(rawTeacherId) : userId;
  33. return Result.success(healthPlanService.listPendingReviewPlans(familyId, teacherId));
  34. }
  35. @Operation(summary = "编辑方案内容")
  36. @PostMapping("/{planId}/update")
  37. public Result<HealthPlan> updatePlan(
  38. @RequestBody(required = false) Map<String, Object> params,
  39. @RequestAttribute("role") String role,
  40. @RequestAttribute("userId") Long userId,
  41. @PathVariable("familyId") Long familyId,
  42. @PathVariable("planId") Long planId) {
  43. Result<Void> denied = resolvePlan(role, userId, familyId, planId);
  44. if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
  45. String planContent = params == null ? null : (String) params.get("planContent");
  46. String planJson = params == null ? null : (String) params.get("planJson");
  47. return Result.success(healthPlanService.updatePlanContent(planId, planContent, planJson));
  48. }
  49. @Operation(summary = "审核通过并发布")
  50. @PostMapping("/{planId}/approve")
  51. public Result<PlanApproveResultDTO> approvePlan(
  52. @RequestBody(required = false) Map<String, Object> params,
  53. @RequestAttribute("role") String role,
  54. @RequestAttribute("userId") Long userId,
  55. @PathVariable("familyId") Long familyId,
  56. @PathVariable("planId") Long planId) {
  57. Result<Void> denied = resolvePlan(role, userId, familyId, planId);
  58. if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
  59. String comment = params == null ? null : (String) params.get("comment");
  60. return Result.success(healthPlanService.approveAndPublish(planId, userId, comment));
  61. }
  62. @Operation(summary = "驳回方案")
  63. @PostMapping("/{planId}/reject")
  64. public Result<HealthPlan> rejectPlan(
  65. @RequestBody(required = false) Map<String, Object> params,
  66. @RequestAttribute("role") String role,
  67. @RequestAttribute("userId") Long userId,
  68. @PathVariable("familyId") Long familyId,
  69. @PathVariable("planId") Long planId) {
  70. Result<Void> denied = resolvePlan(role, userId, familyId, planId);
  71. if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
  72. String comment = params == null ? null : (String) params.get("comment");
  73. return Result.success(healthPlanService.rejectPlan(planId, userId, comment));
  74. }
  75. /**
  76. * IDOR 防护:planId 必须真实属于路径 familyId,且当前角色对该 familyId 有绑定权限。
  77. *
  78. * @return null 表示放行;非 null 为 403 响应
  79. */
  80. private Result<Void> resolvePlan(String role, Long userId, Long familyId, Long planId) {
  81. Result<Void> denied = guideFamilyAccessGuard.checkFamilyAccess(role, userId, familyId);
  82. if (denied != null) return denied;
  83. HealthPlan plan = healthPlanService.getPlanForFamily(planId, familyId);
  84. if (plan == null) {
  85. return Result.error(403, "无权访问该方案");
  86. }
  87. // 兜底重复校验:getPlanForFamily 已按 familyId 过滤,故此处 plan.getFamilyId() 必然等于
  88. // 上方已校验的 familyId,本次调用实际不会拒绝任何请求。保留仅为纵深防御,勿依赖它拦截路径不一致。
  89. Result<Void> recheck = guideFamilyAccessGuard.checkFamilyAccess(role, userId, plan.getFamilyId());
  90. if (recheck != null) return recheck;
  91. return null;
  92. }
  93. }