package com.yunhai.agent.tool.defs; import com.yunhai.agent.tool.ToolDef; import com.yunhai.agent.tool.ToolParam; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * 教师专业发展模块工具 —— 后端 Profession,前缀 /api/profession、/api/moudule、/api/points、/api/stats。 * *
★ 发布表单的字段以模块前端源码为**权威契约**: * {@code ProfessionView/src/views/profession/RecordEdit.vue}(发布/编辑记录页)。 * 提交体(模块页 {@code save()} 逐行核对): *
* POST /api/profession/Create (application/x-www-form-urlencoded) * { moduleId, title, grade, subject, ifOpen, remark, * contents: JSON.stringify([{fieldId, value}]), // 栏目动态字段 * fileIds: "11,12" } // 逗号串 *
模块侧的栏目字段接口查询参数叫 {@code SerID},而表单字段叫 {@code moduleId} —— 两边名字不同。 */ private static Map moduleIdAs(String queryParam) { Map m = new LinkedHashMap<>(); m.put(queryParam, "moduleId"); return m; } /** * 「发布 / 编辑记录」页的完整字段。 * * ★ 与 {@code ProfessionView/src/views/profession/RecordEdit.vue} 的表单逐项对齐: * * 标题(必填,≤200) → 年级段 / 学科(两个可空下拉) → 是否公开(单选 公开/不公开) * → 栏目动态字段(按栏目渲染 → contents) → 正文内容(富文本 → remark) → 附件(fileIds) * * 唯一多出来的是「所属栏目」——模块页是从栏目列表点「发布」进来的(栏目由入口决定), * 助手没有这个上下文,所以必须让用户选一次。 */ private static ToolParam[] professionPublishParams() { List ps = new ArrayList<>(); ps.add(ToolParam.required("moduleId", "integer", "栏目 id,来自 profession.moduleTree") .label("所属栏目") .optionsFrom("profession.moduleTree")); ps.add(ToolParam.required("title", "string", "记录标题(200 字以内)") .label("记录标题").placeholder("请输入记录标题").maxLength(200) .eg("2026年秋季公开课教学设计")); ps.add(ToolParam.optional("grade", "string", "年级段,来自 profession.grades") .label("年级段") // 年级段字典是 {value,label}(不是平台通用的 {k,v} / 业务 VO 的 {id,name}),必须指认键名 .optionsFrom("profession.grades").optionsKeys("value", "label")); ps.add(ToolParam.optional("subject", "string", "学科,取值来自 profession.subjects 的 subId") .label("学科") .optionsFrom("profession.subjects").optionsKeys("subId", "subName")); ps.add(ToolParam.optional("ifOpen", "integer", "是否公开:1 公开 / 0 不公开") .label("是否公开").as("radio").dft("1") .options(new String[]{"1", "0"}, new String[]{"公开", "不公开"})); // ★ 富文本但**不带图片上传**:模块页 RecordEdit.vue 的 editorConfig 只有 placeholder // (toolbarConfig = {}),并没有配 MENU_CONF.uploadImage —— 该模块的 file 域只暴露了 // Upload / Preview / Down(见 profession.ts),**没有** UploadImage。 // 所以这里不调 .richText(...):上传地址为空时前端会把工具栏的「图片」组去掉, // 而不是留一个点了必失败的按钮。 ps.add(ToolParam.optional("remark", "string", "正文内容(富文本 HTML,对应模块页的 wangEditor;该模块不支持正文插图)") .label("正文内容").as("richtext").placeholder("请输入正文内容…")); // 栏目动态字段的打包结果:由表单取回字段定义、渲染后自行生成,用户不直接填 ps.add(ToolParam.optional("contents", "string", "栏目动态字段的取值(JSON 数组,由表单自动生成)") .label("栏目动态字段").hide()); ps.add(ToolParam.optional("fileIds", "array", "附件文件 id 列表(由表单的附件上传得到)") .label("添加附件").as("file").multiple(true).join(",") // ★ 专业发展的附件上传接口与其余模块不同:走 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]); } }
★ 与 {@code ProfessionView/src/views/profession/RecordEdit.vue} 的表单逐项对齐: *
* 标题(必填,≤200) → 年级段 / 学科(两个可空下拉) → 是否公开(单选 公开/不公开) * → 栏目动态字段(按栏目渲染 → contents) → 正文内容(富文本 → remark) → 附件(fileIds) *