用户在自己线上环境传专业发展附件时接口 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。
对齐 ProfessionView/src/views/profession/RecordEdit.vue: - profession.publish 字段改为 moduleId/title/grade/subject/ifOpen/remark/contents/fileIds (POST_FORM /api/profession/Create;附件走 /api/file/Upload;fullForm 全字段渲染) - 新增 4 个字典工具:profession.grades / subjects / moduleFields / areaTree, 并恢复上一轮重写时误删的 profession.myPoints / profession.pointsRank - ToolDef:新增 extraFields* 声明(字典工具 + 参数名 + 「查询参数 → 表单字段」占位符映射 + 前置锚点) - FormRequest:新增 ExtraFields(含 dependsOn / param / before / areaCall) - AgentOrchestrator:抽出共用的 buildReadCall,据此下发 extraFields(栏目字段定义与区划树) - ModuleFormParityTest:新增 4 条契约测试(字段顺序 / 提交方式 / 控件形态 / 动态字段声明)
- com.yunhai.agent,端口 8093,context-path /agent,制品 target/agent.jar;与 NoticeService 同构 - CAS 单点登录 + 三层角色权限(学生/教师/管理员/访客)+ 会话与工具调用审计落库 - DeepSeek 大模型编排 + 7 个业务模块工具(请假/公告/宿舍/活动/作业/资源库/教师发展) - 浏览器代执行协议(服务端挂起 → 前端用登录态代调 → toolResult 续跑)+ 调用失败深链兜底 - 登出修复:服务端真正 session.invalidate() + 前端两步登出 + logout-guard - 保留 projman 注入的 .gitea/workflows/build.yml 与 docker/Dockerfile;docker/application-prod.yml、run-app.sh 随工程入库