用户要求:「我发起调用清洗,或者从上传文件页面返回时,清空 sessionStore 内容/已上传文件缓存」。 经确认(AskUserQuestion):时机 = 发起清洗任务时 + 上传页点返回时;范围 = 连同清洗流程缓存一起清。
| 文件 | 改动 |
|---|---|
case/views/data/upload.vue |
新增 clearCleanFlowSessionCache():清 case_clean_file_infos(沿用 isOwnBatchStorage() 批次守卫)+ case_clean_import_file_infos + case_clean_reclean_context + resetImportProgressSession()(清 case_clean_import_progress、关 SSE、重置运行时状态)。调用点 3 处:handleBack() 无活动分支、cleanupAndLeave()、onBeforeRouteLeave 兜底(目标不是清洗页/清洗进度页时才清) |
case/views/data/cleanProgress.vue |
startClean():doClean() 成功后清 case_clean_file_infos + case_clean_import_file_infos + case_clean_reclean_context(原第 736 行那行保留) |
case_clean_file_infos:cleaning.vue(loadStoredFileList)与 cleanProgress.vue(loadCleanFiles 的兜底键)都要读它,清了就无法清洗。所以「发起清洗时清」只能落在 startClean() 里、loadCleanFiles()/loadReCleanContext() 之后。startClean() 里 不能 调 resetImportProgressSession():initializeProgress() 的顺序是 resetRuntimeState → connectSse → testSse → startClean,它会把刚建立的 SSE 关掉;且会清掉刚设置的 cleanTaskStarted/currentCleanFiles。要清进度键就用 clearImportProgress(caseId)。doClean() 成功之后而非之前:失败时缓存仍在,可重新发起;否则发起失败即死路(必须重传文件)。case_clean_import_progress 不清:它是当前任务的进度快照(currentCleanFiles 自包含),清了刷新页面就无法恢复进度。case_clean_。upload.vue:import { type Key } from 'vue' 报 TS2305(该版本 vue 不导出 Key)→ 去掉导入,expandedRowKeys/onExpandedRowsChange 改用 (string | number)[](与 antd Table 的 Key 一致,代码里只塞字符串)。eslint src/case/views/data/{upload,cleanProgress}.vue --max-warnings 0 → 0 错误
(cleanProgress.vue 原有 1 处 prettier 报错(第 134 行 import 折行)用 --fix 修掉,只动了那一行)vue-tsc --noEmit --skipLibCheck → case/views/data/{upload,cleanProgress,cleaning}.vue 0 错误(全仓 103 个既有错误在 src/core 等,与本次无关)ls/head/tail/sed/dirname/grep/uname/rm 全部不可用(dirname: command not found),
→ ./node_modules/.bin/eslint 这类 shell 包装脚本必然失败(它内部用 sed/dirname)。/c/Users/cc/.workbuddy/binaries/node/versions/22.22.2-3/node.exe ./node_modules/eslint/bin/eslint.js ...
同理 ./node_modules/vue-tsc/bin/vue-tsc.js。git 可用;rm 用 node 的 fs.unlinkSync 兜底。@RequestBody 缺失审计(2026-09-16 下午)用户要求:「检查项目中所有 controller 中有些接口是不是没有加 @RequestBody」。
controller-request-body-audit.md,仓库根)ai-server 45 个 controller / 98 个写端点(POST/PUT/PATCH)中,76 个端点参数没有绑定注解,
其中 75 个已被确认为真实缺陷(前端用 defHttp.postJson 即 JSON body 调用 → body 被丢弃、参数全 null),
1 个暂无前端调用方(/trans/cashFlow/statTransCashCallBeforeAfterTimeLine)。
已正确 21 个(含上一轮修的 /chat/**、/dm/preFile、/case/create|open|updatePwd 等)+ 1 个 multipart。
application/x-www-form-urlencoded(core/utils/http/axios/index.ts:275),
defHttp.post 的 data 会被 qs.stringify 成表单 —— 不加注解的 POJO 能正常绑定;
只有 defHttp.postJson(Axios.ts:187,显式 application/json + data 作 body)才要求后端有 @RequestBody。HandlerMethodArgumentResolver/WebMvcConfigurer,
也没有重写 body→parameter 的 Filter/HttpServletRequestWrapper(全仓 grep 0 命中)→ 走 Spring 默认 ModelAttribute 绑定。| 端点 | 现状 | 说明 |
|---|---|---|
/pg/delete |
后端 delete(Long id),前端 body {id} |
直接加 @RequestBody 会 400(对象→Long);应改 @RequestParam+前端 params,或引入 IdDTO |
/intimacy/modify |
List<Intimacy>,前端发数组 |
@RequestBody List<Intimacy> 即可 |
/pbi/personOrder |
List<PersonBasicInfo>,前端发数组 |
同上 |
/call/night/statSecondCallNight |
参数误写成 @Param("query")(MyBatis 注解) |
去掉 @Param 换 @RequestBody |
ResidentPopulationController 只有类级 @RequestMapping("/rp")、零个方法(空壳类)。@RequestBody:32 个 controller 改动(74 个 POJO 参数前插注解 +
/pg/delete 改 DTO + statSecondCallNight 的 MyBatis @Param("query") 替换),新增 DTO
common/model/person/dto/DeletePersonGroupDTO.java(前端发 {id},简单类型不能直接加注解)。import org.apache.ibatis.annotations.Param(CallNightController)、
4 处与 annotation.* 通配符重复的显式 RequestBody import、3 个 controller 里未使用的 MyBatis @Delete import。mvn -pl ai-server -B clean compile → BUILD SUCCESS(621 源文件);③ 复扫 audit → broken=0 / missingBindingAnnotation=0 / annotated=97 + multipart=1。controller-request-body-audit.md 顶部加「〇、修复记录」,第 1~8 节保留修复前状态留档。C:/Users/cc/AppData/Local/Temp/zsjz-audit/backup-controllers/(45 个 controller 修复前副本)。
★ 32 个文件中 DataProfileController.java 在我修复前就与 HEAD 不同(import 被改成 annotation.* 通配符,
非我所为,已保留)→ 该文件不能用 git checkout 回退(会连带丢掉那处改动),要用备份副本;其余 31 个可 git checkout。mvn.cmd、mvn-run.sh 都跑不起来(shim 里连 bash/cygpath 都 command not found)。可行解:直接调 java + classworlds,
全用 Windows 路径,HOST 用托管 JDK(JAVA_HOME 是 BellSoft JDK 25,能编 release 17 的代码 + Lombok):
cd /e/workspace/zsjz-ai && "/c/Program Files/BellSoft/LibericaJDK-25-Full/bin/java.exe" \
-classpath "D:\soft\apache-maven-3.9.12-bin\boot\plexus-classworlds-2.9.0.jar" \
"-Dclassworlds.conf=D:\soft\apache-maven-3.9.12-bin\bin\m2.conf" \
"-Dmaven.home=D:\soft\apache-maven-3.9.12-bin" \
"-Dmaven.multiModuleProjectDirectory=E:\workspace\zsjz-ai" \
org.codehaus.plexus.classworlds.launcher.Launcher -pl ai-server -B clean compile \
> E:/workspace/zsjz-ai/.build-log.txt 2>&1; echo "EXIT=$?"
(注意 plexus-classworlds 版本是 2.9.0 不是 2.8.0;输出重定向到文件再用 Grep 工具过滤,管道 grep 不可用。)
~/.workbuddy/skills/spring-requestbody-audit/:
scripts/audit.cjs(审计+交叉比对+生成报告)、scripts/fix.cjs(按精确偏移批量补注解,dry-run/--apply/--skip)、
scripts/verify-fix.cjs(结构比对,强制断言「URL/签名除注解外零改动」)。
SKILL.md 里写了完整 9 步修复流程与本机编译方式。
★ 写这类脚本踩过的 3 个解析坑(务必照抄规避):
{(postJson<{ [key: string]: X }>)→ 找参数对象必须从「( 之后」开始找 {;<Page<X>> / <Record<string, any>> 会让 <[^>]*> 正则整个失配 → 必须手写「跳过可选泛型块」的扫描;@RequestMapping("cr") 不带前导斜杠(45 个里只有这 1 个)→ 拼路径时要补 /,否则匹配不上前端 URL。
审计覆盖率自查法:统计前端 defHttp.(postJson|post|put|patch|uploadFile|request) 总数(本次 174)与解析出的调用点数(171),
差值必须能逐个解释(本次 3 个全是注释里的假命中)。需求:「错误提示加上访问接口地址」→ 所有接口报错时,提示里要能一眼看出是哪个接口。
ai-frontend/src/core/utils/http/axios/)| 文件 | 改动 |
|---|---|
helper.ts |
新增 appendApiUrl(message, url):'接口请求出错' + '/js/a/dm/doClean' → '接口请求出错(/js/a/dm/doClean)';url 为空或 message 已含该地址时不重复追加 |
index.ts |
① transformRequestHook 取 res.config.url,业务错误(code !== 200,含 401 与 skipErrorMessage 分支)统一走 appendApiUrl;② responseInterceptorsCatch 的 urlPath 增加 ` |
checkStatus.ts |
checkStatus(status, msg, mode, url?) 新增第 4 个可选参数,400/403/404/500 等状态码提示统一 appendApiUrl(errMessage, url) |
\n:antd message 的 content 是 div,\n 会被折叠成空格。sys.api.*)都自动带上地址,
且 throw new Error(errorMessage) 里的 message 也带地址 —— 调用方 error.message / getRequestErrorMessage() 无需改动。message === 'xxx' 的精确匹配(只有 cleanProgress 里一段注释掉的代码),所以拼接安全。/sys/health 的静默特例(urlPath.includes('/sys/health')),健康检查轮询不会刷屏;其 URL 带 ?_t= 时间戳也能命中。defHttp 的所有实例(含 src/ai/api/http.ts 的 aiHttp,它复用 defHttp);errorMessageMode==='none' 的请求仍然不弹提示(未改语义)。appendApiUrl 里加。eslint(3 文件,--max-warnings 0)→ 0 错误vue-tsc --noEmit → 这三个文件 0 错误,全仓错误总数仍为 103(与改动前一致,无新增)需求:「检查所有 controller 的接口返回结果是不是又缺 Result 包裹,有就加上」。
全量扫描 45 个 controller → 165 个方法级端点(用「方法级映射注解总数 = 165」做过完整性交叉校验,无遗漏):
| 类别 | 数量 |
| --- | --- |
| 已 Result 包裹 | 151 |
| 流式 / ResponseEntity(不适用) | 3(/govern/sse、/chat/stream 的 SseEmitter + /py/files/** 的 ResponseEntity) |
| 刻意裸返回(保持不动) | 11(AgentChatController 10 个 /chat/** + AgentResultController 的 /chat/results/{resultId}) |
| 漏网缺陷(已修) | 2 |
DataProfileController)GET /dp/callStat:CallDataProfileStatDTO → Result<CallDataProfileStatDTO>GET /dp/transStat:TransDataProfileStatDTO → Result<TransDataProfileStatDTO>
前端 person/api/dpApi.ts 用 defHttp.get<...> 默认 transform 读 {code,message,data},
裸返回必然走到 code !== 200 分支 → 画像页通话/交易统计直接报「接口请求出错」。它们的注解与方法签名之间夹了一行注释(Solon 迁移遗留):
@GetMapping("/callStat")
//@Cache(key = "dp_callStat_${personName}", tags = StrConsts.CACHE_TAG_QING_JIAN)
public CallDataProfileStatDTO callInfoStat(String personName) { ... }
「紧跟映射注解去找方法签名」的解析逻辑遇到注释行就失配 → 整个端点被跳过。 教训:任何注解/签名扫描都必须做完整性交叉校验(映射注解总数 vs 解析端点数),否则静默漏项。
/chat/**:前端 aiHttp.postRaw/getRaw/putRaw/deleteRaw({ isTransformResponse: false })/chat/results/{resultId}:同上,且 404 语义=结果过期/py/files/**:ResponseEntity 直出文件/govern/sse、/chat/stream:SseEmitter 流式mvn -pl ai-server -B clean compile → BUILD SUCCESSscripts/audit-result-wrap.cjs