用户在自己线上环境传专业发展附件时接口 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:
@@ -120,7 +120,13 @@ public class ToolParam {
|
||||
/** {@code widget=file} 时提交附件对象数组而非 fileId 数组(作业模块 attachments) */
|
||||
private boolean fileObject;
|
||||
|
||||
/** 附件上传地址覆盖(缺省由编排层按 {@code /<模块>/api/base/FileUpload} 兜底) */
|
||||
/**
|
||||
* 附件上传地址覆盖(**模块内相对路径**,如 {@code /api/file/Upload})。
|
||||
*
|
||||
* <p>不写时由编排层按「模块前缀 + {@code /api/base/FileUpload}」兜底;
|
||||
* 写时前缀同样由 {@code AgentOrchestrator.resolveUploadPath} 拼 ——
|
||||
* ★ 这里**不要**自己带模块前缀,否则会与约定不一致(曾经因此线上 404)。
|
||||
*/
|
||||
private String uploadPathOverride;
|
||||
|
||||
/** 是否在表单里隐藏(如由系统固定填入的参数,用户无需看见) */
|
||||
@@ -461,7 +467,10 @@ public class ToolParam {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 覆盖附件上传地址(缺省由编排层按 {@code /<模块>/api/base/FileUpload} 兜底) */
|
||||
/**
|
||||
* 覆盖附件上传地址。★ 传**模块内相对路径**(如 {@code /api/file/Upload}),
|
||||
* 模块前缀由编排层统一拼;不传则用模块默认的 {@code /api/base/FileUpload}。
|
||||
*/
|
||||
public ToolParam upload(String path) {
|
||||
this.uploadPathOverride = path;
|
||||
return this;
|
||||
|
||||
@@ -25,8 +25,14 @@ public final class HomeworkTools {
|
||||
|
||||
private static final String TEACHER = "/api/teacher/homeworks";
|
||||
|
||||
/** 附件上传地址(作业模块走 file 域,与其余模块的 base/FileUpload 不同) */
|
||||
private static final String UPLOAD_PATH = "/coursehomework/api/file/Upload";
|
||||
/**
|
||||
* 附件上传地址(作业模块走 file 域,与其余模块的 base/FileUpload 不同)。
|
||||
*
|
||||
* <p>★ 只写**模块内相对路径**(与 {@code ToolDef.path} 同约定):模块前缀由
|
||||
* {@code AgentOrchestrator.resolveUploadPath} 统一拼,最终下发 {@code /coursehomework/api/file/Upload}。
|
||||
* 这里自己带上前缀曾经导致 profession 那种「线上 404」,别再写回去。
|
||||
*/
|
||||
private static final String UPLOAD_PATH = "/api/file/Upload";
|
||||
|
||||
private HomeworkTools() {
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user