|
|
@@ -1479,7 +1479,34 @@ interactionType 取值:`PHONE` / `WECHAT` / `MEETING` / `OTHER`。
|
|
|
- 入选范围限定 `status IN ('draft','pending_review')`,按 `created_at` 倒序。
|
|
|
|
|
|
> **注意**:`health_plans.teacher_id` 列由迁移 251 建立(`DatabaseInitializer`),`schema.sql` 已同步。
|
|
|
-> 前端 `getPendingReviewPlans()`(`utils/api.js`)目前尚无页面消费,规划师审核页未接通。
|
|
|
+> 前端 `getPendingReviewPlans()`(`utils/api.js`)目前尚无页面消费,规划师审核页未接通;新审核工作台改用 `4.47` 的 `plans/pending` 端点,本节 4 个端点保留但不再被前端调用。
|
|
|
+
|
|
|
+> ⚠️ 2026-10-04:`/pending-review/approve` 响应由 `Result<HealthPlan>` 改为 `Result<PlanApproveResultDTO>`(新增 `taskGenerated` / `taskCount` / `message` 字段)。该端点无前端调用点(`health-plan-summary.vue` 仅有未调用的死导入),变更安全。
|
|
|
+
|
|
|
+> ✅ 2026-10-04:`/{familyId}/overview`、`/{familyId}/children/{memberId}/tasks`、`/{familyId}/children/{memberId}/pending-review`、`/{familyId}/tasks/{taskId}/review`、`/{familyId}/tasks/batch-review`、`/{familyId}/tasks/{taskId}`、`/{familyId}/tasks/{taskId}/delete` 共 7 个端点已接入 `guide_families` 绑定校验,修复跨家庭越权。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 4.47 规划师审核工作台(`/api/guide/families/*`)
|
|
|
+
|
|
|
+规划师侧审核工作台的统一入口,授权源为 `guide_families.status='binding'`(与 `GuideFamilyService.isBound(guideId, familyId)` 同源),不使用 `users.teacher_family_ids`。
|
|
|
+
|
|
|
+| 端点 | 权限 |
|
|
|
+|---|---|
|
|
|
+| `POST /api/guide/families/my-families` | teacher / admin |
|
|
|
+| `POST /api/guide/families/{familyId}/members` | teacher 需 isBound;admin 放行 |
|
|
|
+| `POST /api/guide/families/{familyId}/plans/pending` | teacher 需 isBound;admin 可传 teacherId 过滤 |
|
|
|
+| `POST /api/guide/families/{familyId}/plans/{planId}/update` | 同上 + planId IDOR 反查 |
|
|
|
+| `POST /api/guide/families/{familyId}/plans/{planId}/approve` | 同上 + planId IDOR 反查;返回 `PlanApproveResultDTO` |
|
|
|
+| `POST /api/guide/families/{familyId}/plans/{planId}/reject` | 同上 + planId IDOR 反查 |
|
|
|
+
|
|
|
+- **`my-families`** 不走 `checkFamilyAccess(role, userId, null)` —— 该路径下 `familyId == null` 会把 teacher 直接拒掉,故改为显式角色判断 + 按 `guide_id` 查本人绑定家庭,返回体含 `familyName`(取自 `Family.name`,注意字段名不是 `familyName`)。
|
|
|
+- **`members`** 返回该家庭可审核成员,供工作台「成员」选择器使用。
|
|
|
+- **未分配池规则**:`plans/pending` 复用 `listPendingReviewPlans()`,`teacherId` 非空时按 `(teacher_id = ? OR teacher_id IS NULL)` 过滤;teacher 无需传该参数,admin 可显式传以过滤。
|
|
|
+- **IDOR 防护**:`{planId}` 类端点先 `checkFamilyAccess(role, userId, familyId)`,再用 `getPlanForFamily(planId, familyId)` 按 familyId 反查,返回 `null` 即 403。控制器内 `resolvePlan` 的第二次 recheck 仅为纵深防御,`getPlanForFamily` 已按 familyId 过滤,**该 recheck 永不单独拦截请求**,不可依赖它替代首道校验。
|
|
|
+- **`approve` 返回 `PlanApproveResultDTO`**:`planId` / `status` / `taskGenerated` / `taskCount` / `message`。任务生成器 `generateDailyTasksFromPlan` 返回 `void`,故实现改为生成后按 `source_type='health_plan' AND source_id=planId` 统计实际条数;生成异常或计数为 0 时方案仍发布并返回 `taskGenerated=false`,属**部分成功**,前端须与失败区分提示。
|
|
|
+
|
|
|
+> **注意**:配套前端为 `pages/teacher/review-workbench.vue`、`review-family.vue`、`plan-detail.vue`,入口在 `pages/teacher/families.vue`。
|
|
|
|
|
|
---
|
|
|
|