Files
TARE-Learn/exams/exam-kit/reference-answer.md
T
smart 67837df8d0 docs: 更新TRA编程培训课程材料、课件与AI技能交付包
- 新增全套考试卷、评估卷、参考答案与考试培训包模板
- 修正slidev培训课件:调整模块标题、链路图缩放,替换工单示例为通用业务对象
- 修正课堂红线表述为证据红线,将小组作业调整为个人作业要求
- 新增权限、状态流转、审计日志、看板指标四类可复用业务规则块
- 整理AI工程技能交付包,迁移旧资产至客户专属交付包并统一术语
- 新增考试输出模板与TRAE使用记录文档
2026-08-08 11:00:36 +08:00

169 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TRAE 编程培训考试评估卷 - 阅卷参考答案
> 阅卷专用。对应 `jeecgboot/JeecgBoot` 标签 `v3.9.2`、提交 `7df07a823fd558be857d0208ccae96342539fbc1` 及 `review-diff.patch`。学生不需要逐字复述本答案,但所有核心结论都必须有等价的源码依据。
## A 卷
### 任务 1:Prompt 改写(15 分)
下列内容是一份达到满分要求的参考 Prompt。允许更换表述,但不能省略范围、约束、交付格式和验收条件。
```text
你是负责 JeecgBoot 系统用户模块的后端工程师。请基于 GitHub 仓库
jeecgboot/JeecgBoot 的 v3.9.2(提交 7df07a823fd558be857d0208ccae96342539fbc1),
审查“批量冻结/解冻用户”的后端链路是否允许非法状态值进入数据层。
范围:只分析
1. SysUserController.frozenBatch;
2. ISysUserService.updateStatus 和 SysUserServiceImpl.updateStatus;
3. CommonConstant 中的用户状态常量;
4. 与本接口直接相关的权限、管理员保护、缓存和测试入口。
目标:给出最小改造建议,使 status 只允许 1(解冻)或 2(冻结),并在数据库写入前拒绝
空白或其他值。不要修改接口路径、权限 system:user:frozenBatch、管理员保护或缓存失效策略。
请按以下格式输出:
- 调用链和每个结论的“文件路径 + 方法/符号”;
- 变更位置、伪代码和不改动项;
- 空白/非法状态、冻结、解冻、无权限、管理员保护和缓存回归的测试矩阵;
- 仍需人工确认的问题。事实、推断和待确认项必须分别标识。
验收:每个结论均可回溯到上述固定提交;不编造测试结果;信息不足时先提出待确认项。
```
判分关键:背景与目标(3 分)、范围(2 分)、约束(4 分)、输出格式(3 分)、验收和证据要求(3 分)。只写“帮我检查有没有问题”不能得高分。
### 任务 2:AI 输出辨析(15 分)
题干中的 AI 结论“页面已限制状态值,因此 Service 不需要校验;直接调用接口也不会有问题”应判为错误,理由如下:
1. 前端限制不是服务端的可信边界。调用者可以绕过页面直接发送 `PUT /sys/user/frozenBatch` 请求。
2. `SysUserController.frozenBatch` 从 JSON 读取 `status` 后逐个调用 `sysUserService.updateStatus(id, status)`;原版 `updateStatus` 会直接执行 `Integer.parseInt(status)` 并更新数据库。
3. Service 入口是数据写入前的最后业务保护点。正确做法是在 `updateStatus` 内、`Integer.parseInt` 和 `userMapper.update` 之前,仅允许 `CommonConstant.USER_UNFREEZE`(1)和 `CommonConstant.USER_FREEZE`(2)。`null`、空串、空白串、`3` 及其他值均应抛出业务异常。
4. 不能因此删除接口权限、管理员保护或缓存注解;这些分别处理调用授权、关键账号保护和用户缓存一致性。
## B 卷任务 1:项目理解与影响分析(25 分)
### 满分结论摘要
1. 批量冻结/解冻的 HTTP 入口是 `PUT /sys/user/frozenBatch`,由 `SysUserController.frozenBatch` 处理,受 `system:user:frozenBatch` 权限保护。
2. Controller 从请求体读取 `ids`、先调用 `checkUserAdminRejectDel(ids)`,再读取 `status`,对每个非空 ID 调用 `updateStatus(id, status)`。
3. `updateStatus` 是最终写入入口:它将状态转换为整数,通过 `userMapper.update` 更新 `SysUser.status`,并用 `@CacheEvict(CacheConstant.SYS_USERS_CACHE, allEntries = true)` 失效用户缓存。
4. 用户状态常量中,`1` 表示解冻,`2` 表示冻结,`3` 表示注销;本题只允许前两种状态。
5. 冻结状态会影响登录有效性判断:服务在读取到 `USER_FREEZE` 时返回“该用户已冻结”。
### 至少应出现的证据
| 结论 | 文件路径与符号 | 证明内容 | 判断类型 |
|---|---|---|---|
| 接口、HTTP 方法和权限 | `.../controller/SysUserController.java`,`frozenBatch` | `@RequiresPermissions("system:user:frozenBatch")` 与 `@RequestMapping(... "/frozenBatch", PUT)` | 事实 |
| Controller 到 Service 调用链 | 同文件 `frozenBatch` | 读取 `ids`、管理员保护、读取 `status`,循环调用 `updateStatus` | 事实 |
| 服务契约 | `.../service/ISysUserService.java`,`updateStatus(String id, String status)` | 状态更新是服务接口的一部分 | 事实 |
| 数据库与缓存副作用 | `.../service/impl/SysUserServiceImpl.java`,`updateStatus` | `@CacheEvict`、`Integer.parseInt(status)`、`userMapper.update` | 事实 |
| 合法状态枚举 | `.../common/constant/CommonConstant.java`,`USER_UNFREEZE`、`USER_FREEZE`、`USER_QUIT` | 取值分别为 1、2、3 | 事实 |
| 管理员保护 | `.../service/impl/SysUserServiceImpl.java`,`checkUserAdminRejectDel` | 目标 ID 中含用户名 `admin` 时抛出业务异常;`frozenBatch` 会调用它 | 事实 |
| 登录影响 | `.../service/impl/SysUserServiceImpl.java`,登录有效性检查中对 `USER_FREEZE` 的分支 | 冻结用户不能正常登录 | 事实 |
### 影响和保护边界
- 非法状态若进入原版 Service,会在 `Integer.parseInt` 处异常,或在可解析但不受业务允许的值(如 `3`)场景写入不符合本题要求的状态;因此应在 Service 入口先拒绝。
- 校验位于 `userMapper.update` 前;抛出异常时不会完成本次用户状态写入。`@CacheEvict` 默认在方法成功返回后生效,因此异常路径不应把一次失败校验伪装为成功变更。
- 必须保持:接口路径和 PUT 方法、`system:user:frozenBatch` 权限、`checkUserAdminRejectDel(ids)` 调用、逐个用户更新的方式、缓存失效注解。
- 可接受的待确认项:批量操作是否要求原子性、前端是否会发送 `null` 或空白状态、缓存是否有跨节点配置。不得把这些未查证项写成事实。
## B 卷任务 2:最小改造与测试安全网(25 分)
### 改造位置和参考实现
文件:`jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysUserServiceImpl.java`。
方法:`updateStatus(String id, String status)`。
位置:保留已有 `@Override` 和 `@CacheEvict`,在 `Integer.parseInt(status)`、`userMapper.update(...)` 之前加入校验:
```java
if (!String.valueOf(CommonConstant.USER_UNFREEZE).equals(status)
&& !String.valueOf(CommonConstant.USER_FREEZE).equals(status)) {
throw new JeecgBootException("账号状态仅支持冻结或解冻");
}
```
该写法同时拒绝 `null`、空串、空白串、`3` 和其他非 `1`/`2` 的值;允许 `"1"` 与 `"2"`。它没有改变原有数据更新、缓存失效、权限或管理员保护路径。
### 必须明确的不改动项
| 不改动项 | 原因和代码依据 |
|---|---|
| 权限 | `SysUserController.frozenBatch` 的 `@RequiresPermissions("system:user:frozenBatch")` 仍是接口授权边界。 |
| 管理员保护 | Controller 继续先调用 `sysUserService.checkUserAdminRejectDel(ids)`;状态校验不能替代该保护。 |
| 缓存失效 | `SysUserServiceImpl.updateStatus` 的 `@CacheEvict(value = {CacheConstant.SYS_USERS_CACHE}, allEntries = true)` 保留,以保证成功修改后的用户缓存刷新。 |
| 数据写入方式 | 合法状态仍走原有 `userMapper.update(new SysUser().setStatus(...), ...)`。 |
### 参考测试矩阵
| 场景 | 预期结果 | 验证入口 | 执行状态的正确写法 |
|---|---|---|---|
| `null`、空串、空白串、`3` | 抛出 `JeecgBootException`;不调用 mapper,不成功失效缓存 | `SysUserServiceImpl.updateStatus` 单元测试 | 未实际运行时必须写“未执行”和原因;不能凭空写通过 |
| `"1"` 解冻 | 调用 mapper,写入状态 1;成功返回后缓存失效 | Service 单元测试或集成测试 | 记录实际命令和结果 |
| `"2"` 冻结 | 调用 mapper,写入状态 2;成功返回后缓存失效 | Service 单元测试或集成测试 | 记录实际命令和结果 |
| 无权限请求 | 被权限框架拒绝,不能执行状态写入 | `PUT /sys/user/frozenBatch` 的接口/集成测试 | 记录测试账号、HTTP 结果或未执行原因 |
| 目标包含 `admin` | `checkUserAdminRejectDel` 拒绝,不能更新管理员 | 接口或 Controller 集成测试 | 记录结果或未执行原因 |
| 冻结后登录 | 读取冻结用户时走“该用户已冻结”分支 | 登录集成测试 | 可标为待环境验证,不得臆造 |
`review-diff.patch` 已提供一个针对 `"3"` 的最小单元测试。它覆盖非法数值在访问 mapper 前被拒绝;高质量答案还应补充空白值和两个合法值的测试。项目父 POM 默认配置 `maven-surefire-plugin` 跳过测试,因此学员若未调整本地测试配置,应如实标记“未执行”,并给出启用测试后的计划命令,而不是伪造成功结果。
## B 卷任务 3:Review、PR 与资产沉淀(20 分)
### 参考 Review 结论
**结论:可以合入,但建议在合入前补充测试用例。**
检查范围是 `review-diff.patch` 的两处改动:`SysUserServiceImpl.updateStatus` 和新增的 `SysUserServiceImplTest`。
| 级别 | 结论 | 证据 | 建议 |
|---|---|---|---|
| 通过 | 校验位于 `Integer.parseInt` 和 `userMapper.update` 前,阻止非法状态写入 | 服务实现新增条件与业务异常 | 保持 |
| 通过 | 仅允许常量 1/2,`null` 和空白均不匹配,状态 3 也被拒绝 | 两个常量比较均通过 `String.valueOf` | 保持 |
| 通过 | 没有删除缓存失效注解、接口权限或管理员保护 | diff 只改 Service 校验和新增测试 | 保持 |
| 建议 | 新测试仅覆盖 `"3"`,未直接覆盖 `null`、空白、`"1"`、`"2"` 和 mapper 未调用 | 新增测试类只有一个 `assertThrows` | 加参数化负例和合法值的 mapper 交互断言 |
| 残余风险 | Controller 对异常统一返回的语义、批量更新原子性和集群缓存行为不属于本 diff 的直接变更 | 原始 Controller 和框架配置 | 作为后续集成测试范围,不阻断本次最小修复 |
不能仅写“代码没问题”。若给出“未发现阻断问题”,仍须写清检查范围、证据和残余风险。
### 参考 PR 说明
**背景**:系统用户批量冻结/解冻接口会将请求中的 `status` 传入 Service。前端选项限制不能替代服务端边界,非法值应在数据写入前被拒绝。
**实现**:在 `SysUserServiceImpl.updateStatus` 增加状态白名单,仅允许解冻(1)和冻结(2);非法值抛出 `JeecgBootException`。保留原缓存失效、Controller 权限和管理员保护。
**测试**:新增非法状态 `"3"` 的单元测试。合入前建议补充 `null`、空白、`"1"`、`"2"` 及接口权限/管理员保护的测试;每项测试状态必须按实际执行情况填写。
**风险与回退**:该变更会拒绝过去可能被错误接受的状态值,属于预期的业务收紧。出现兼容性问题时可回退本次 Service 白名单提交;不能通过放宽为任意整数解决。
### 参考可复用 Prompt
```text
基于指定 Git 提交审查一个状态更新 diff。先输出 Controller -> Service -> Mapper/缓存的调用链,
再判断校验是否发生在所有数据写入和可见副作用之前。只接受代码中已定义的状态常量;列出保留的
权限、关键账号保护、缓存和审计逻辑。最后按“阻断问题 / 建议 / 残余风险 / 测试矩阵”输出,
每项都给出文件路径和符号。没有执行的测试必须标为未执行,不能推断为通过。
```
验收方式:输出必须能标出校验位置、常量来源、未改动保护和至少一项负例及正例测试;不能凭空声称测试成功。
### 参考 Checklist 设计卡
| 项目 | 内容 |
|---|---|
| 名称 | 状态变更服务端边界 Checklist |
| 适用场景 | 新增或修改“启用/停用/冻结/解冻/审批状态”等服务端状态写入逻辑时 |
| 输入 | 固定提交、变更 diff、状态常量定义、调用入口 |
| 检查项 | 1. 合法状态是否白名单化;2. 校验是否早于解析、写库和副作用;3. 权限和关键账号保护是否保留;4. 缓存/审计/事件是否明确;5. 正负例和接口授权测试是否如实记录 |
| 输出 | Review 结论、证据表、测试矩阵、残余风险 |
| 边界 | 只辅助检查,不能替代实际权限配置、集成测试和人工业务审批 |
| 验收 | 每项结论都有文件路径和符号;未执行测试有原因与后续验证计划 |
## AI 使用记录的判分口径
不提供统一的“标准 Prompt 列表”。满分记录应至少能够说明:使用 AI 或 TRAE 做了什么、建议是什么、哪些被采纳或否决、人工为什么这样判断、相应证据或测试是否真的执行。工具名称可以不同;缺少 `trae-usage-record.md` 时,本题“AI 使用记录”2 分为 0 分。