用户在自己线上环境传专业发展附件时接口 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:
@@ -643,7 +643,7 @@ public class AgentOrchestrator {
|
||||
}
|
||||
|
||||
/**
|
||||
* 模块在站点根下的**路径前缀**(`homeleave` → `/home-leave`)。
|
||||
* 由模块的**完整地址**推出它在站点根下的路径前缀(`homeleave` + `…/home-leave` → `/home-leave`)。
|
||||
*
|
||||
* <p>★ 不能用模块 key 直接拼:key 只是 `agent.modules` 的内部标识,真实路由前缀可能不同 ——
|
||||
* 请假正是这种情况,key 是 `homeleave` 而前缀是 `/home-leave`(差一个连字符)。
|
||||
@@ -651,16 +651,6 @@ public class AgentOrchestrator {
|
||||
* POST 到 `/homeleave/api/base/FileUpload`,被网关当成不存在的应用直接 **404**
|
||||
* (实测:`/home-leave/...` 回 302 走 CAS,`/homeleave/...` 回 caddy 404)。
|
||||
*
|
||||
* @param module 模块标识(`ToolDef.module`)
|
||||
* @return 路径前缀,以 `/` 开头且不以 `/` 结尾;模块未配置时退回 `/<module>`
|
||||
*/
|
||||
private String modulePathPrefix(String module) {
|
||||
return pathPrefix(module, settings.getModules().get(module));
|
||||
}
|
||||
|
||||
/**
|
||||
* 由模块的**完整地址**推出它在站点根下的路径前缀(`homeleave` + `…/home-leave` → `/home-leave`)。
|
||||
*
|
||||
* @param module 模块标识(仅用于兜底)
|
||||
* @param moduleBaseUrl `agent.modules` 里配置的地址,如 `http://host/home-leave`
|
||||
*/
|
||||
@@ -685,6 +675,34 @@ public class AgentOrchestrator {
|
||||
return path.isEmpty() ? "/" + module : path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 附件上传地址(下发到前端的**相对站点根**路径)。
|
||||
*
|
||||
* <p>★ 约定与 {@link ToolDef#getPath()} 完全一致:**参数只写模块内相对路径**,模块前缀一律在这里拼。
|
||||
* 早先把 {@code uploadPathOverride} 当成「完整路径」直接用,于是
|
||||
* {@code profession.publish} 声明 {@code /api/file/Upload} 就原样下发,
|
||||
* 线上打到 {@code http://base.yunhaixingtong.com/api/file/Upload} —— 站点根没有这个应用,**404**
|
||||
* (请假那次是「前缀用 key 拼错」,这次是「压根没拼」;同一个坑的两种形态)。
|
||||
*
|
||||
* <p>为了容错,对「已经带了前缀」的写法保持幂等({@code /profession/api/file/Upload} 不会再被
|
||||
* 拼成 {@code /profession/profession/...}),调用方不必猜该写哪一种。
|
||||
*
|
||||
* @param module 模块标识
|
||||
* @param moduleBaseUrl {@code agent.modules} 里配置的地址(用来推路径前缀)
|
||||
* @param override {@link ToolParam#getUploadPathOverride()},为空时用模块默认的 {@code /api/base/FileUpload}
|
||||
*/
|
||||
public static String resolveUploadPath(String module, String moduleBaseUrl, String override) {
|
||||
String prefix = pathPrefix(module, moduleBaseUrl);
|
||||
String rel = StringUtils.isNotBlank(override) ? override.trim() : "/api/base/FileUpload";
|
||||
if (!rel.startsWith("/")) {
|
||||
rel = "/" + rel;
|
||||
}
|
||||
if (rel.equals(prefix) || rel.startsWith(prefix + "/")) {
|
||||
return rel;
|
||||
}
|
||||
return prefix + rel;
|
||||
}
|
||||
|
||||
/** 由工具定义生成补全表单 */
|
||||
private FormRequest buildForm(ToolDef def, Map<String, Object> args, List<ToolParam> missing,
|
||||
CallerContext caller) {
|
||||
@@ -716,12 +734,11 @@ public class AgentOrchestrator {
|
||||
for (ToolParam p : shown) {
|
||||
FormField f = p.toFormField();
|
||||
// 附件控件:补上传地址(模块的附件上传只认浏览器 CAS 会话,必须前端带登录态 POST)。
|
||||
// 缺省按「模块真实前缀 + /api/base/FileUpload」,个别模块上传接口不同(作业是 /api/file/Upload)
|
||||
// 时由参数覆盖。★ 前缀必须取自 agent.modules 的配置,不能用模块 key 拼(见 modulePathPrefix)。
|
||||
// 缺省「模块前缀 + /api/base/FileUpload」;个别模块上传接口不同(作业/专业发展是 /api/file/Upload)
|
||||
// 时由参数覆盖 —— **覆盖值也只写模块内相对路径**,前缀一律由 resolveUploadPath 拼。
|
||||
if ("file".equals(f.getWidget())) {
|
||||
f.setUploadPath(StringUtils.isNotBlank(p.getUploadPathOverride())
|
||||
? p.getUploadPathOverride()
|
||||
: modulePathPrefix(def.getModule()) + "/api/base/FileUpload");
|
||||
f.setUploadPath(resolveUploadPath(def.getModule(),
|
||||
settings.getModules().get(def.getModule()), p.getUploadPathOverride()));
|
||||
}
|
||||
// 字典类字段(如请假类型、班级、学生):优先交给前端用**用户自己的登录态**代取。
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user