来源:
tests/test-cases/deep-dive-assessment-appointment.md的 F1–F7 + 2026-10-27 live 验证新增的 F8。 阅读对象:研发 / 测试 / 技术负责人。所有缺陷均附「缺陷五要素」+ 严重程度 + 优先级 + 修复建议。 实测环境:127.0.0.1:9082(后端存活;其依赖的测试库192.168.16.251本机不可达,故种子数据由测试经direct-register自写入)。 真实后端码值约定(实测校正):成功code=200,业务错误code=500(无家庭5001)。与原深挖文档假设的「0/非0」不同。
| 编号 | 标题 | 严重度 | 优先级 | 状态 |
|---|---|---|---|---|
| CFC-AP-001 | 取消预约接口未校验归属(越权取消) | 高 | P0 | 待修复 |
| CFC-AP-002 | 确认预约无状态守卫(非法回退) | 中 | P1 | 待修复 |
| CFC-AP-003 | 已完/未到状态无入口,状态机不完整 | 中 | P2 | 待澄清 |
| CFC-AP-004 | 创建预约无幂等(重复提交双预约+双能量) | 中 | P1 | 待修复 |
| CFC-AP-005 | assessor-list 无 role 校验(与 guide-list 不一致) | 低 | P3 | 待修复 |
| CFC-AP-006 | 预约日期/时间无格式与合理性校验 | 低 | P3 | 待修复 |
| CFC-AP-007 | 额度扣减非原子(并发超售风险) | 低 | P2 | 待修复 |
| CFC-AP-008 | 额度不存在时创建预约抛未捕获异常(500 内部错误) | 中 | P1 | 待修复 |
AssessmentAppointmentController.cancelAppointment(controller 第 112–116 行);AssessmentAppointmentService.cancelAppointment(service 第 88–94 行)POST /api/assessment/appointment/cancel/{X}(X 为用户 A 的预约 id)。X 的归属者/被指派者 ≠ B → 拒绝(401/403 或业务错误)。cancelAppointment 仅接收 @PathVariable Long id,不接收调用者身份,任何登录用户凭 id 即可取消他人预约,返回成功(status=3)。cancelAppointment 增加归属校验。userId(@RequestAttribute("userId")),校验 appointment.getUserId().equals(userId) || 是指派者(assessorId/guideId);否则返回业务错误。AssessmentAppointmentService.confirmAppointment(service 第 57–66 行)POST /api/assessment/appointment/confirm/{Y}。confirmAppointment 仅校验「是否是被指派者」,不校验当前 status,被取消/已完成预约再 confirm 仍被置为 1(已确认),破坏状态机。confirmAppointment 增加状态前置校验。if (status != 0) return false/抛错(仅 status=0 待确认可确认)。AssessmentAppointmentService.completeAppointment(service 第 68–86 行);status=4(未到) 全代码无 setterstatus=2 的方法在本控制器无对应 HTTP 接口;status=4(未到) 全代码无任何赋值点。状态机 0/1/3 可达,2/4 入口不明(疑由 DanAssessmentExecution / AssessmentResult 链路进入)。DanAssessmentExecution/AssessmentResult 是否写入 status=2;为「未到」补一个明确的业务动作(如规划师标记未到)。AssessmentAppointmentService.createAppointment(service 第 27–55 行)quotaId=Q 连续 POST /api/assessment/appointment/create 两次(参数相同)。createAppointment 增加幂等控制。AssessmentAppointmentController.assessorList(controller 第 140–146 行)POST /api/assessment/appointment/assessor-list。guide-list 一致做角色鉴权(仅 teacher 可见)。userId 查;影响面有限(按自身隔离),但与 guide-list 鉴权策略不一致,易误导维护。assessor-list 增加 role == "teacher" 校验,或在文档中明确其「仅按自身 userId 隔离、无需 role」的设计意图。AssessmentAppointmentController.create(controller 第 50–51 行,appointmentDate/Time 为自由字符串)create 传 appointmentDate="2000-01-01"(过去)或乱码时间字符串。create 入参校验。appointmentDate/Time 做日期格式解析 + 未来时间校验(不早于今天)。createAppointment 中 consumeQuota 调用顺序(先 consume 再取 quota 记录)create(均带 quotaId=Q)。consumeQuota 与后续读取非原子,可能都通过校验 → 超售(额度被用超)。consumeQuota 内用 UPDATE ... SET remain=remain-1 WHERE id=? AND remain>0(受影响行数=1 才算成功);或加乐观锁/行锁。AssessmentAppointmentService.createAppointment → consumeQuota(quotaId 不存在分支)quotaId(如 999999999)。POST /api/assessment/appointment/create 带 quotaId=999999999, guideId=<规划师id>。code=500, message="服务器内部错误"——consumeQuota 对不存在的额度抛了未捕获异常,被全局异常处理器兜底为「服务器内部错误」。consumeQuota 在额度查不到时未做前置判空/友好报错,可能与 CFC-AP-007 的并发竞态同源。原深挖文档预测的「额度不足或已过期」文案在 live 中并未出现。consumeQuota / createAppointment 的额度查不到分支。consumeQuota 先 selectById(quotaId);为 null 或 remain<=0 或过期 → 抛 IllegalArgumentException("额度不足或已过期") 并由 Result.error 返回(不要裸抛 NPE)。以上缺陷均为只读分析 + live 验证结论,修复由研发团队实施;本文件仅供测试与评审使用。