api-method-refactor-plan.md 18 KB

接口方法规范改造计划(禁用 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):

// 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,行为不变:

// before
@DeleteMapping("/delete")
public Result<Void> delete(Long id) { ... }

// after
@GetMapping("/delete")
public Result<Void> 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 处)

// #1 before
return aiHttp.putRaw<void>(`/chat/sessions/${sessionId}/title`, undefined, { title });
// after
return aiHttp.getRaw<void>(`/chat/sessions/${sessionId}/title`, { title });

// #2 before
return aiHttp.deleteRaw<void>(`/chat/sessions/${sessionId}`);
// after(路径加 /delete,原因见 §1.5)
return aiHttp.getRaw<void>(`/chat/sessions/${sessionId}/delete`);

// #3 before
return aiHttp.putRaw<void>(`/chat/sessions/${sessionId}/pin`);
// after
return aiHttp.getRaw<void>(`/chat/sessions/${sessionId}/pin`);

// #4 before
return aiHttp.deleteRaw<void>(`/chat/messages/${messageId}`);
// after
return aiHttp.getRaw<void>(`/chat/messages/${messageId}`);

// #5 before
return aiHttp.putRaw<void>(`/chat/messages/${messageId}/star`);
// after
return aiHttp.getRaw<void>(`/chat/messages/${messageId}/star`);

3.2 src/ai/api/modelApi.ts(2 处)

// #6 before
return aiHttp.delete<void>(`/models/${id}`);
// after
return aiHttp.get<void>(`/models/${id}`);

// #7 before
return aiHttp.putJson<void>(`/models/${id}/default`);
// after
return aiHttp.get<void>(`/models/${id}/default`);

modelApi.ts:27 的 updateModel(aiHttp.putJson)保持不动,对应后端 §4.1 豁免项。

3.3 其余模块(8 处)

// #8  src/graph/insight/api.ts:62
// before
return aiHttp.deleteRaw<void>(`${BASE}/sessions/${id}`, { caseId });
// after(路径加 /delete,原因见 §1.5)
return aiHttp.getRaw<void>(`${BASE}/sessions/${id}/delete`, { caseId });

// #9  src/person/api/personApi.ts:99
// before  defHttp.delete<void>({ url: adminPath + '/pbi/delete', params: { personName } }, {...})
// after   defHttp.get<void>({ url: adminPath + '/pbi/delete', params: { personName } }, {...})

// #10 src/person/api/personApi.ts:196
// before  defHttp.delete<void>({ url: adminPath + '/pg/remPerson', params: { relId } }, {...})
// after   defHttp.get<void>({ url: adminPath + '/pg/remPerson', params: { relId } }, {...})

// #11 src/plat/api/tpl/tplApi.ts:75
// before  defHttp.delete<void>({ url: adminPath + '/tpl/delete', params }, { skipSuccessMessage: true });
// after   defHttp.get<void>({ url: adminPath + '/tpl/delete', params }, { skipSuccessMessage: true });

// #12 src/plat/api/case/caseApi.ts:166
// before  defHttp.delete<void>({ url: adminPath + '/case/delete', params: { id } });
// after   defHttp.get<void>({ url: adminPath + '/case/delete', params: { id } });

// #13 src/trans/api/transApi.ts:68
// before  defHttp.delete<void>({ url: adminPath + '/trans/cashKey/delete', params: { id } });
// after   defHttp.get<void>({ url: adminPath + '/trans/cashKey/delete', params: { id } });

// #14 src/trans/api/transApi.ts:88
// before  defHttp.delete<void>({ url: adminPath + '/trans/financialKey/delete', params: { id } });
// after   defHttp.get<void>({ url: adminPath + '/trans/financialKey/delete', params: { id } });

// #15 src/trans/api/transApi.ts:108
// before  defHttp.delete<void>({ url: adminPath + '/fixedDepositKey/delete', params: { id } });
// after   defHttp.get<void>({ 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)。