|
|
@@ -0,0 +1,109 @@
|
|
|
+package com.etotem.cfc.controller.guide;
|
|
|
+
|
|
|
+import com.etotem.cfc.common.Result;
|
|
|
+import com.etotem.cfc.dto.PlanApproveResultDTO;
|
|
|
+import com.etotem.cfc.entity.HealthPlan;
|
|
|
+import com.etotem.cfc.service.GuideFamilyAccessGuard;
|
|
|
+import com.etotem.cfc.service.HealthPlanService;
|
|
|
+import com.etotem.cfc.util.ParamUtils;
|
|
|
+import io.swagger.v3.oas.annotations.Operation;
|
|
|
+import io.swagger.v3.oas.annotations.tags.Tag;
|
|
|
+import org.springframework.web.bind.annotation.*;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+@Tag(name = "指导师-客户方案审核", description = "规划师审核其绑定客户的健康方案")
|
|
|
+@RestController
|
|
|
+@RequestMapping("/api/guide/families/{familyId}/plans")
|
|
|
+public class GuidePlanReviewController {
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private HealthPlanService healthPlanService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private GuideFamilyAccessGuard guideFamilyAccessGuard;
|
|
|
+
|
|
|
+ @Operation(summary = "待审核方案列表")
|
|
|
+ @PostMapping("/pending")
|
|
|
+ public Result<List<HealthPlan>> listPendingPlans(
|
|
|
+ @RequestBody(required = false) Map<String, Object> params,
|
|
|
+ @RequestAttribute("role") String role,
|
|
|
+ @RequestAttribute("userId") Long userId,
|
|
|
+ @PathVariable("familyId") Long familyId) {
|
|
|
+ Result<Void> denied = guideFamilyAccessGuard.checkFamilyAccess(role, userId, familyId);
|
|
|
+ if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
|
|
|
+
|
|
|
+ Object rawTeacherId = params == null ? null : params.get("teacherId");
|
|
|
+ Long teacherId = "admin".equals(role) ? ParamUtils.getLong(rawTeacherId) : userId;
|
|
|
+ return Result.success(healthPlanService.listPendingReviewPlans(familyId, teacherId));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Operation(summary = "编辑方案内容")
|
|
|
+ @PostMapping("/{planId}/update")
|
|
|
+ public Result<HealthPlan> updatePlan(
|
|
|
+ @RequestBody(required = false) Map<String, Object> params,
|
|
|
+ @RequestAttribute("role") String role,
|
|
|
+ @RequestAttribute("userId") Long userId,
|
|
|
+ @PathVariable("familyId") Long familyId,
|
|
|
+ @PathVariable("planId") Long planId) {
|
|
|
+ Result<Void> denied = resolvePlan(role, userId, familyId, planId);
|
|
|
+ if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
|
|
|
+
|
|
|
+ String planContent = params == null ? null : (String) params.get("planContent");
|
|
|
+ String planJson = params == null ? null : (String) params.get("planJson");
|
|
|
+ return Result.success(healthPlanService.updatePlanContent(planId, planContent, planJson));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Operation(summary = "审核通过并发布")
|
|
|
+ @PostMapping("/{planId}/approve")
|
|
|
+ public Result<PlanApproveResultDTO> approvePlan(
|
|
|
+ @RequestBody(required = false) Map<String, Object> params,
|
|
|
+ @RequestAttribute("role") String role,
|
|
|
+ @RequestAttribute("userId") Long userId,
|
|
|
+ @PathVariable("familyId") Long familyId,
|
|
|
+ @PathVariable("planId") Long planId) {
|
|
|
+ Result<Void> denied = resolvePlan(role, userId, familyId, planId);
|
|
|
+ if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
|
|
|
+
|
|
|
+ String comment = params == null ? null : (String) params.get("comment");
|
|
|
+ return Result.success(healthPlanService.approveAndPublish(planId, userId, comment));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Operation(summary = "驳回方案")
|
|
|
+ @PostMapping("/{planId}/reject")
|
|
|
+ public Result<HealthPlan> rejectPlan(
|
|
|
+ @RequestBody(required = false) Map<String, Object> params,
|
|
|
+ @RequestAttribute("role") String role,
|
|
|
+ @RequestAttribute("userId") Long userId,
|
|
|
+ @PathVariable("familyId") Long familyId,
|
|
|
+ @PathVariable("planId") Long planId) {
|
|
|
+ Result<Void> denied = resolvePlan(role, userId, familyId, planId);
|
|
|
+ if (denied != null) return Result.error(denied.getCode(), denied.getMessage());
|
|
|
+
|
|
|
+ String comment = params == null ? null : (String) params.get("comment");
|
|
|
+ return Result.success(healthPlanService.rejectPlan(planId, userId, comment));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * IDOR 防护:planId 必须真实属于路径 familyId,且当前角色对该 familyId 有绑定权限。
|
|
|
+ *
|
|
|
+ * @return null 表示放行;非 null 为 403 响应
|
|
|
+ */
|
|
|
+ private Result<Void> resolvePlan(String role, Long userId, Long familyId, Long planId) {
|
|
|
+ Result<Void> denied = guideFamilyAccessGuard.checkFamilyAccess(role, userId, familyId);
|
|
|
+ if (denied != null) return denied;
|
|
|
+
|
|
|
+ HealthPlan plan = healthPlanService.getPlanForFamily(planId, familyId);
|
|
|
+ if (plan == null) {
|
|
|
+ return Result.error(403, "无权访问该方案");
|
|
|
+ }
|
|
|
+ // 兜底重复校验:getPlanForFamily 已按 familyId 过滤,故此处 plan.getFamilyId() 必然等于
|
|
|
+ // 上方已校验的 familyId,本次调用实际不会拒绝任何请求。保留仅为纵深防御,勿依赖它拦截路径不一致。
|
|
|
+ Result<Void> recheck = guideFamilyAccessGuard.checkFamilyAccess(role, userId, plan.getFamilyId());
|
|
|
+ if (recheck != null) return recheck;
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+}
|