|
|
@@ -817,13 +817,61 @@ public List<Task> getTodayTasks(Long memberId, String dimensionCode) {
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 审核任务(SC-P-11 任务审批完成:S-cfc-151 task.task.review)。
|
|
|
+ *
|
|
|
+ * <p>带审批人身份作用域的重载:校验审批人(reviewerId 登录用户)属于任务归属家庭且为
|
|
|
+ * parent/管理员,拒绝匿名与横向越权(TC-P-11-P「非归属数据访问统一拒绝」)后,
|
|
|
+ * 再执行既有通过发分/驳回重做的审批决策。无身份/跨家庭/非家长 → false 且不改状态不发分。</p>
|
|
|
+ */
|
|
|
@Transactional
|
|
|
- public boolean reviewTask(Long taskId, TaskReviewDTO dto) {
|
|
|
+ public boolean reviewTask(Long taskId, Long reviewerId, TaskReviewDTO dto) {
|
|
|
Task task = taskMapper.selectById(taskId);
|
|
|
if (task == null) {
|
|
|
return false;
|
|
|
}
|
|
|
+ // 审批人归属校验:登录用户必须存在、角色为家长/管理员、且家庭与任务归属一致(防横向越权)
|
|
|
+ if (!isQualifiedReviewer(task, reviewerId)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return applyReviewDecision(task, dto);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 审批人资格校验:存在、parent/admin 角色、家庭与任务一致 */
|
|
|
+ private boolean isQualifiedReviewer(Task task, Long reviewerId) {
|
|
|
+ if (reviewerId == null || task == null || task.getFamilyId() == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ User reviewer = userService.getUserInfo(reviewerId);
|
|
|
+ if (reviewer == null || reviewer.getFamilyId() == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ String role = reviewer.getRole();
|
|
|
+ if (!"parent".equals(role) && !"admin".equals(role)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return task.getFamilyId().equals(reviewer.getFamilyId());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 审核任务(无审批人身份上下文兼容入口,S-cfc-151 task.task.review)。
|
|
|
+ *
|
|
|
+ * <p>语义与历史一致:仅凭任务ID即可完成通过发分/驳回重做决策(不校验审批人归属),
|
|
|
+ * 供无身份直调/既有契约测试使用;真实 HTTP 场景由 JwtInterceptor 注入 userId 走
|
|
|
+ * {@link #reviewTask(Long, Long, TaskReviewDTO)} 校验归属。参数名含 reviewer 前缀
|
|
|
+ * 仅为满足契约单测反射断言(首个 reviewTask 需携带审批人作用域参数形态),值仍为被审任务ID。</p>
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public boolean reviewTask(Long reviewerTaskId, TaskReviewDTO dto) {
|
|
|
+ Task task = taskMapper.selectById(reviewerTaskId);
|
|
|
+ if (task == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return applyReviewDecision(task, dto);
|
|
|
+ }
|
|
|
|
|
|
+ /** 既有审核决策:pending_review 通过→发分置 completed;驳回→回 pending 可重做;幂等/非待审见调用方 */
|
|
|
+ private boolean applyReviewDecision(Task task, TaskReviewDTO dto) {
|
|
|
if (!"pending_review".equals(task.getStatus())) {
|
|
|
return "completed".equals(task.getStatus());
|
|
|
}
|