# 接口方法规范改造计划(禁用 PUT / DELETE) - 项目:`E:\workspace\zsjz-ai`(后端 `ai-server`,前端 `ai-frontend`) - 更新时间:2026-09-18 - 状态:**已执行完成**(后端编译 BUILD SUCCESS、前端构建成功;豁免项保持原样) - v2 修订:新增 §1.4 保持类豁免、§1.3 前端改动边界(禁止改封装工具) - v3 修订:新增 §1.5 的 2 处路径变更(GET 路由冲突),补充执行与验证实测结果 --- ## 1. 执行口径(硬性) ### 1.1 改造规则 1. 禁止 `@PutMapping` / `@DeleteMapping` / `@PatchMapping` / `@RequestMapping(method=PUT|DELETE|PATCH)`。 2. 方法带 `@RequestBody` → 必须 POST。 3. 无 `@RequestBody` 时:形参 > 3 → POST;≤ 3 → GET。 4. 形参个数按方法签名的形参计,不按 DTO 内部字段数计。 ### 1.2 保持类豁免(最高优先级,严格执行) **符合以下任一条件的接口,一律不改造,保持原样:** - **A 类**:当前已经是 `@GetMapping` 的接口(含绑定 Query / DTO 对象形参的 GET); - **B 类**:方法形参中已带 `@RequestBody` 的接口。 **本次实际改造范围 = 既不是 `@GetMapping`、又没有 `@RequestBody` 的接口。** ### 1.3 前端改动边界(严格执行) **只改「调用哪个请求方法」这一处,禁止改动任何已封装好的请求工具。** 以下文件一个字节都不动: | 文件 | 说明 | |---|---| | `src/core/utils/http/axios/Axios.ts` | 底层 `defHttp`,其中 `put()` / `delete()` 方法保留(豁免项仍在用 `put`) | | `src/core/utils/http/axios/index.ts` | `beforeRequestHook` / `transformRequestHook` 拦截逻辑 | | `src/ai/api/http.ts` | `aiHttp` 封装,`putRaw` / `deleteRaw` / `putJson` / `delete` / `withQuery` 全部保留原样 | 前端改动只落在 api 层调用点(§3):`defHttp.delete(...)` → `defHttp.get(...)`、`aiHttp.deleteRaw(...)` → `aiHttp.getRaw(...)` 这类**调用替换**,不涉及工具本身。 ### 1.4 过滤结果 | 类别 | 数量 | 处理 | |---|---|---| | PUT / DELETE 且**无** `@RequestBody` | **15** | 后端改 `@GetMapping` + 前端调用改 GET(清单见 §2) | | PUT / DELETE 且**带** `@RequestBody` | **1**(`AgentModelController#updateModel`) | **豁免,保持 `@PutMapping` 原样**(§4.1) | | 原本就是 `@GetMapping` | 47 | **豁免,不动** | | 原本就是 `@PostMapping`(含带/不带 `@RequestBody`) | 82 | **豁免,不动** | > 附带说明(不改造):5 个不带 `@RequestBody` 的 POST(`DmController.preFileUpload`、`AuthController.logout`、`SystemController.noNet`、`SystemController.uploadUpgrade`、`TowerController.covFile`)按规则 3 本是 GET 候选,但它们已经是 POST,不违反任何规则,保持不动。 ### 1.5 路径兼容性 改造不动 URL 与路径变量,只换 HTTP 方法。路由、拦截器、Sa-Token 鉴权配置全部不变。 **唯一例外(2 处,已执行)**:以下两个删除接口改 GET 后与既有 GET 详情接口同路径,会触发 Spring「Ambiguous mapping」启动异常,故加 `/delete` 后缀(与项目 `/case/delete`、`/tpl/delete` 风格一致),前端同步: | 接口 | 原路径(DELETE) | 新路径(GET) | 冲突对象 | |---|---|---|---| | `AgentChatController#deleteSession` | `/chat/sessions/{sessionId}` | `/chat/sessions/{sessionId}/delete` | 既有 `GET /chat/sessions/{sessionId}`(getSession) | | `InsightController#deleteSession` | `/insight/sessions/{id}` | `/insight/sessions/{id}/delete` | 既有 `GET /insight/sessions/{id}`(getSession) | 其余 13 处路径零变化。 --- ## 2. 后端改造清单(15 处) | # | 文件(相对 `ai-server/src/main/java/com/zsjz/ai/`) | 行 | 现状 | 接口 | 形参 | 目标 | 前端传参 | |---|---|---|---|---|---|---|---| | 1 | `module/agent/controller/AgentChatController.java` | 62 | PUT | `/chat/sessions/{sessionId}/title` updateSessionTitle | 2(path+param) | **GET** | query: `title` | | 2 | 同上 | 70 | DELETE | ~~`/chat/sessions/{sessionId}`~~ → `/chat/sessions/{sessionId}/delete` deleteSession | 1 | **GET** | — | | 3 | 同上 | 78 | PUT | `/chat/sessions/{sessionId}/pin` togglePinSession | 1 | **GET** | — | | 4 | 同上 | 102 | DELETE | `/chat/messages/{messageId}` deleteMessage | 1 | **GET** | — | | 5 | 同上 | 110 | PUT | `/chat/messages/{messageId}/star` toggleStarMessage | 1 | **GET** | — | | 6 | `module/agent/controller/AgentModelController.java` | 65 | DELETE | `/models/{id}` deleteModel | 1 | **GET** | — | | 7 | 同上 | 74 | PUT | `/models/{id}/default` setDefaultModel | 1 | **GET** | — | | 8 | `module/agent/insight/controller/InsightController.java` | 114 | DELETE | ~~`/insight/sessions/{id}`~~ → `/insight/sessions/{id}/delete` deleteSession | 2(path+param) | **GET** | query: `caseId` | | 9 | `module/person/controller/PersonBasicInfoController.java` | 35 | DELETE | `/pbi/delete` delete | 1(`String personName`) | **GET** | query: `personName` | | 10 | `module/person/controller/PersonGroupController.java` | 73 | DELETE | `/pg/remPerson` remPerson | 1(`Long relId`) | **GET** | query: `relId` | | 11 | `module/plat/controller/CaseInfoController.java` | 63 | DELETE | `/case/delete` delete | 1(`Long id`) | **GET** | query: `id` | | 12 | `module/plat/controller/TableInfoController.java` | 87 | DELETE | `/tpl/delete` delete | 1(`Integer id`) | **GET** | query: `id` | | 13 | `module/trans/controller/CashKeyController.java` | 55 | DELETE | `/trans/cashKey/delete` delete | 1(`Long id`) | **GET** | query: `id` | | 14 | `module/trans/controller/FinancialKeyController.java` | 49 | DELETE | `/trans/financialKey/delete` delete | 1(`Long id`) | **GET** | query: `id` | | 15 | `module/trans/controller/FixedDepositKeyController.java` | 51 | DELETE | `/fixedDepositKey/delete` delete | 1(`Long id`) | **GET** | query: `id` | ### 2.1 后端改动样板 单参数(#2/#4/#6/#11/#12/#13/#14/#15): ```java // before @DeleteMapping("/sessions/{sessionId}") public void deleteSession(@PathVariable Long sessionId) { ... } // after @GetMapping("/sessions/{sessionId}") public void deleteSession(@PathVariable Long sessionId) { ... } ``` 带简单类型参数(#1/#8/#9/#10)——顺手补 `@RequestParam`,行为不变: ```java // before @DeleteMapping("/delete") public Result delete(Long id) { ... } // after @GetMapping("/delete") public Result delete(@RequestParam Long id) { ... } ``` ### 2.2 import 清理 改为 GET 后删除无用 import 行 `import org.springframework.web.bind.annotation.DeleteMapping;`: `InsightController`、`CaseInfoController`、`TableInfoController`、`CashKeyController`、`FixedDepositKeyController`。 `AgentChatController`、`AgentModelController`、`PersonBasicInfoController`、`PersonGroupController`、`FinancialKeyController` 用的是 `web.bind.annotation.*` 通配,不动。 --- ## 3. 前端配合改造(15 处调用点) 只替换请求方法调用,不改任何封装工具(§1.3)。所有调用都走 api 层函数,视图组件只 import 函数名,改完 api 层后 `.vue` / store 层零改动(调用点见 §3.4)。 ### 3.1 `src/ai/api/chatApi.ts`(5 处) ```ts // #1 before return aiHttp.putRaw(`/chat/sessions/${sessionId}/title`, undefined, { title }); // after return aiHttp.getRaw(`/chat/sessions/${sessionId}/title`, { title }); // #2 before return aiHttp.deleteRaw(`/chat/sessions/${sessionId}`); // after(路径加 /delete,原因见 §1.5) return aiHttp.getRaw(`/chat/sessions/${sessionId}/delete`); // #3 before return aiHttp.putRaw(`/chat/sessions/${sessionId}/pin`); // after return aiHttp.getRaw(`/chat/sessions/${sessionId}/pin`); // #4 before return aiHttp.deleteRaw(`/chat/messages/${messageId}`); // after return aiHttp.getRaw(`/chat/messages/${messageId}`); // #5 before return aiHttp.putRaw(`/chat/messages/${messageId}/star`); // after return aiHttp.getRaw(`/chat/messages/${messageId}/star`); ``` ### 3.2 `src/ai/api/modelApi.ts`(2 处) ```ts // #6 before return aiHttp.delete(`/models/${id}`); // after return aiHttp.get(`/models/${id}`); // #7 before return aiHttp.putJson(`/models/${id}/default`); // after return aiHttp.get(`/models/${id}/default`); ``` > `modelApi.ts:27` 的 `updateModel`(`aiHttp.putJson`)**保持不动**,对应后端 §4.1 豁免项。 ### 3.3 其余模块(8 处) ```ts // #8 src/graph/insight/api.ts:62 // before return aiHttp.deleteRaw(`${BASE}/sessions/${id}`, { caseId }); // after(路径加 /delete,原因见 §1.5) return aiHttp.getRaw(`${BASE}/sessions/${id}/delete`, { caseId }); // #9 src/person/api/personApi.ts:99 // before defHttp.delete({ url: adminPath + '/pbi/delete', params: { personName } }, {...}) // after defHttp.get({ url: adminPath + '/pbi/delete', params: { personName } }, {...}) // #10 src/person/api/personApi.ts:196 // before defHttp.delete({ url: adminPath + '/pg/remPerson', params: { relId } }, {...}) // after defHttp.get({ url: adminPath + '/pg/remPerson', params: { relId } }, {...}) // #11 src/plat/api/tpl/tplApi.ts:75 // before defHttp.delete({ url: adminPath + '/tpl/delete', params }, { skipSuccessMessage: true }); // after defHttp.get({ url: adminPath + '/tpl/delete', params }, { skipSuccessMessage: true }); // #12 src/plat/api/case/caseApi.ts:166 // before defHttp.delete({ url: adminPath + '/case/delete', params: { id } }); // after defHttp.get({ url: adminPath + '/case/delete', params: { id } }); // #13 src/trans/api/transApi.ts:68 // before defHttp.delete({ url: adminPath + '/trans/cashKey/delete', params: { id } }); // after defHttp.get({ url: adminPath + '/trans/cashKey/delete', params: { id } }); // #14 src/trans/api/transApi.ts:88 // before defHttp.delete({ url: adminPath + '/trans/financialKey/delete', params: { id } }); // after defHttp.get({ url: adminPath + '/trans/financialKey/delete', params: { id } }); // #15 src/trans/api/transApi.ts:108 // before defHttp.delete({ url: adminPath + '/fixedDepositKey/delete', params: { id } }); // after defHttp.get({ url: adminPath + '/fixedDepositKey/delete', params: { id } }); ``` ### 3.4 调用点(不改,仅用于回归验证) | api 函数 | 调用方 | |---|---| | `updateSessionTitle` / `deleteSession` / `togglePinSession` / `deleteMessage` / `toggleStarMessage` | `src/ai/store/chatStream.ts` | | `modelApi.updateModel`(豁免项) | `src/ai/components/ModelFormModal.vue:82` | | `modelApi.setDefaultModel` / `deleteModel` | `src/ai/views/aiModel/index.vue` | | `deleteInsightSession` | `src/graph/insight/hooks/useInsightStream.ts` | | `deletePersonBasicInfo` / `remPersonFromGroup` | `src/case/views/dm/portrait.vue` | | `tplDelete` | `src/case/views/data/index.vue` | | `deleteById`(案件) | `src/plat/views/case/case.vue` | | `deleteCashKey` | `src/trans/views/cashTrans/index.vue` | | `deleteFinancialKey` | `src/trans/views/financialWealth/index.vue` | | `deleteFixedDepositKey` | `src/trans/views/fixedDeposit/index.vue` | ### 3.5 前端改动文件清单(仅这 7 个) `src/ai/api/chatApi.ts`、`src/ai/api/modelApi.ts`、`src/graph/insight/api.ts`、`src/person/api/personApi.ts`、`src/plat/api/tpl/tplApi.ts`、`src/plat/api/case/caseApi.ts`、`src/trans/api/transApi.ts`。 封装工具文件 `src/ai/api/http.ts`、`src/core/utils/http/axios/Axios.ts`、`src/core/utils/http/axios/index.ts` **不在改动清单内**。 --- ## 4. 豁免清单(明确不改造) ### 4.1 `AgentModelController#updateModel`(唯一带 body 的 PUT) - 后端 `AgentModelController.java:56`:`@PutMapping("/{id}")`,形参含 `@Valid @RequestBody UpdateModelDTO` → **命中 B 类豁免,保持 `PUT` + `@RequestBody` 原样**; - 前端 `src/ai/api/modelApi.ts:27`:`aiHttp.putJson` **保持不动**; - 结果:改造后 `@DeleteMapping` 为 0、`@PutMapping` 残留 1 处,此条为豁免白名单,§6 的 grep 校验需排除它。 ### 4.2 其余豁免 - 全部 47 个 `@GetMapping` 接口(含 `InsightController#listMessages`、`DataProfileController#getTransOtherNamePage`、`TableInfoController#page`、`SpecialDateController#delete` 这类 Query/DTO 对象形参的 GET); - 全部 82 个 `@PostMapping` 接口。 ### 4.3 附带记录(不在本次改造范围) `src/person/api/personApi.ts:79` 的 `deleteSpecialDateConfig` 用 `defHttp.delete` 调用 `/sdc/delete`,后端 `SpecialDateController.java:39` 是 `@GetMapping` —— 方法不匹配(现网表现为 405)。后端是 GET,命中 A 类豁免,本次不动;前端调用方式本批也不动,仅记录。 --- ## 5. 执行顺序 1. 前置校验:逐条确认待改方法既不是 `@GetMapping`、也没有 `@RequestBody`,命中任一即跳过。 2. 后端 15 处注解改为 `@GetMapping`(§2)+ 清理 5 个无用 `DeleteMapping` import。 3. 处理 2 处 GET 路由冲突:`AgentChatController#deleteSession`、`InsightController#deleteSession` 路径加 `/delete`(§1.5)。 4. 前端 15 处调用点替换请求方法(§3.1、§3.2、§3.3),只动 §3.5 列出的 7 个文件。 5. 编译与类型检查(§6)。 6. 全仓复扫:确认 `@DeleteMapping` 为 0、`@PutMapping` 仅剩 §4.1 白名单 1 处。 7. 豁免复核:`git diff` 中只应出现 §2 的后端文件 + §3.5 的 7 个前端文件;封装工具文件、47 个 GET 接口、82 个 POST 接口一个都不能被改到。 --- ## 6. 验证清单 **编译 / 类型** - 后端:`mvn -q -pl ai-server compile`,零错误。 - 前端:`vue-tsc --noEmit` + `pnpm build`,零错误。 - 部署时必须替换线上 `dist`,否则 api 层改动不生效。 **冒烟(浏览器逐一操作)** | # | 功能 | 页面 | 预期 | |---|---|---|---| | 1 | 会话重命名 | AI 数据分析左侧会话条 | 标题更新,Network 显示 GET | | 2 | 删除会话 | 同上 | 会话消失 | | 3 | 会话置顶/取消 | 同上 | 排序变化 | | 4 | 删除消息 | 对话区消息悬浮菜单 | 消息消失 | | 5 | 消息收藏/取消 | 同上 | 星标切换 | | 6 | 删除模型 | AI 模型管理 | 列表刷新(GET) | | 7 | 设为默认模型 | AI 模型管理 | 默认标记切换(GET) | | 8 | 编辑模型(豁免项,保持 PUT) | AI 模型管理 - 编辑弹窗 | 保存成功,Network 仍为 PUT | | 9 | 删除研判会话 | 对象关系分析 - 历史 | 会话消失 | | 10 | 删除人员 | 人物画像 `case/views/dm/portrait.vue` | 人员移除 | | 11 | 分组移除成员 | 同上 | 成员移除 | | 12 | 删除模板 | 数据管理 `case/views/data/index.vue` | 模板消失 | | 13 | 删除案件 | 案件管理 `plat/views/case/case.vue` | 案件消失 | | 14 | 删除现金关键字 | 资金交易 - 现金交易 | 列表刷新 | | 15 | 删除金融理财关键字 | 资金交易 - 金融理财 | 列表刷新 | | 16 | 删除定期理财关键字 | 资金交易 - 定期理财 | 列表刷新 | **交叉确认**(改造后已实测): - 后端 `@DeleteMapping`:**0**;`@PutMapping`:**1**(`AgentModelController.java:56`,白名单)。 - 前端 api 调用点中 `defHttp.delete` / `defHttp.put` / `aiHttp.deleteRaw` / `aiHttp.putRaw` / `aiHttp.delete`:**0**;`aiHttp.putJson`:**1**(`modelApi.ts:27`,白名单)。 - `src/ai/api/http.ts` 与 `src/core/utils/http/axios/*`:**零改动**。 **编译 / 构建实测结果(2026-09-18 19:26 / 19:28)** - 后端 `mvn -pl ai-server compile`:**BUILD SUCCESS**(686 个源文件,耗时 1m02s)。 - 前端 `pnpm build`:**成功**(vite build,耗时 1m39s,`dist` 已重新产出)。 - 前端 `vue-tsc --noEmit`:项目存在约 100+ 条**存量**类型错误(与本批改动无关,集中在 `graph/views/graph/index.vue`、`case/views/dm/portrait.vue`、`otg/utils/timeSeriesJump.ts` 等);本批改动的 7 个前端文件中**没有新增错误**,仅 `person/api/personApi.ts:119`(`isShowLoading`)一条既有报错,非本次改动行。 **改动文件清单(`git diff --name-only`,共 17 个)** 后端 10 个:`AgentChatController`、`AgentModelController`、`InsightController`、`PersonBasicInfoController`、`PersonGroupController`、`CaseInfoController`、`TableInfoController`、`CashKeyController`、`FinancialKeyController`、`FixedDepositKeyController`。 前端 7 个:`ai/api/chatApi.ts`、`ai/api/modelApi.ts`、`graph/insight/api.ts`、`person/api/personApi.ts`、`plat/api/case/caseApi.ts`、`plat/api/tpl/tplApi.ts`、`trans/api/transApi.ts`。 --- ## 7. 执行须知 1. 删除操作走 GET,请求会被浏览器历史与代理日志记录;将来若接入 WAF / 网关的「只读放行 GET」策略,这些删除接口会失去保护。 2. GET 的浏览器缓存不存在问题 —— 前端 `beforeRequestHook`(`core/utils/http/axios/index.ts:147-156`)对 GET 自动追加 `_t` 时间戳。 3. `/pbi/delete?personName=张三` 走 URL 传参,依赖服务端 URI 编码 UTF-8(Spring Boot 3 默认),回归时确认中文姓名能正常删除。 4. 本批接口参数最多 2 个,无 URL 超长风险。 5. `dist` 未重新构建会导致改动看似未生效。 --- ## 8. 规范条文(写入 `AI_AGENT.md`「接口方法规范」) 1. 只用 **GET / POST**,禁止 `PUT` / `DELETE` / `PATCH`。 2. 方法带 `@RequestBody` → 必须 POST。 3. 无 `@RequestBody` 时:形参 > 3 → POST;≤ 3 → GET。 4. **保持类(优先级高于以上全部)**:已经是 `@GetMapping` 的接口、形参已带 `@RequestBody` 的接口,**一律不再改造**。 5. 改造只动业务 api 层调用与 Controller 注解,**禁止改动已封装的请求工具**(`src/core/utils/http/**`、`src/ai/api/http.ts`)。