fix(upload): 附件上传地址统一由编排层拼模块前缀,修专业发展上传 404
build / build-push (push) Successful in 52s

用户在自己线上环境传专业发展附件时接口 404。根因:`ToolParam.upload()` 的覆盖值被当成
**完整路径**原样下发,于是 `profession.publish` 声明 `/api/file/Upload` 就打到站点根
`http://base.yunhaixingtong.com/api/file/Upload` —— 那里没有应用。
(请假那次是「前缀拿模块 key 拼错」,这次是「压根没拼」,同一个坑的两种形态。)

- `AgentOrchestrator` 新增 `resolveUploadPath(module, baseUrl, override)`:
  约定与 `ToolDef.path` 完全一致 —— **参数只写模块内相对路径**,模块前缀一律在这里拼;
  对「已经带了前缀」的写法保持幂等,调用方不必猜该写哪一种。顺带删掉只剩一处调用的
  `modulePathPrefix`,把注释合并到 `pathPrefix` 上。
- `HomeworkTools.UPLOAD_PATH` 改为模块内相对路径 `/api/file/Upload`(下发值不变:
  `/coursehomework/api/file/Upload`),与 `ProfessionTools` 统一写法。
- `ToolParam.upload()` 的文档写清约定,避免下一个人再写完整路径。

测试(`ModuleFormParityTest`,替换掉原先「测试自己重算一遍拼接逻辑」的弱断言):
- `everyPublishToolUploadPathMatchesItsModuleRealEndpoint`:逐条钉住 6 个模块的线上真实地址
  (对照各模块发布页源码:NoticeUpload.vue / AttachmentUpload.vue / RecordEdit.vue),
  其中 profession 那条正是这次线上 404 的回归用例;
- `noUploadPathIsEverEmittedWithoutItsModulePrefix`:**通用不变量** —— 遍历整个注册表,
  任何附件字段的最终地址都必须以所属模块前缀开头、不许出现裸 `/api/…` 与双斜杠,
  且 `.upload()` 里不许自带前缀。以后新增模块/附件字段会自动被覆盖。

全量 121/121 BUILD SUCCESS。
This commit is contained in:
lijing
2026-10-14 09:56:21 +08:00
parent 8cfbb949b6
commit 42a7a8030d
5 changed files with 149 additions and 29 deletions
@@ -185,7 +185,9 @@ public final class ProfessionTools {
.label("栏目动态字段").hide());
ps.add(ToolParam.optional("fileIds", "array", "附件文件 id 列表(由表单的附件上传得到)")
.label("添加附件").as("file").multiple(true).join(",")
// ★ 专业发展的附件上传接口与其余模块不同:/api/file/Upload(不是 /api/base/FileUpload)
// ★ 专业发展的附件上传接口与其余模块不同:走 file 域(不是 base/FileUpload)。
// 只写模块内相对路径,前缀由 AgentOrchestrator.resolveUploadPath 拼 → /profession/api/file/Upload
// (ProfessionView/src/views/profession/RecordEdit.vue 的 uploadAction = fileUploadUrl)
.upload("/api/file/Upload"));
return ps.toArray(new ToolParam[0]);
}