对齐 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 条契约测试(字段顺序 / 提交方式 / 控件形态 / 动态字段声明)
This commit is contained in:
@@ -53,6 +53,8 @@ class ModuleFormParityTest {
|
||||
// 于是「附件上传地址拼错」这个 bug 在测试里也被一并复制、永远测不出来。
|
||||
settings.getModules().put("homeleave", "http://base.yunhaixingtong.com/home-leave");
|
||||
settings.getModules().put("coursehomework", "http://base.yunhaixingtong.com/coursehomework");
|
||||
// 专业发展:模块挂在自己的路径前缀下(/profession),代取地址与附件上传都要用它
|
||||
settings.getModules().put("profession", "http://base.yunhaixingtong.com/profession");
|
||||
registry = new ToolRegistry(settings);
|
||||
registry.init();
|
||||
executor = new ToolExecutor(settings, new RestTemplate());
|
||||
@@ -282,8 +284,120 @@ class ModuleFormParityTest {
|
||||
assertTrue(body.contains("\"submitType\":[\"image\"]"), "标量要包成数组:" + body);
|
||||
}
|
||||
|
||||
/* ==================== ⑥ 教师专业发展:发布记录(RecordEdit.vue) ==================== */
|
||||
|
||||
/**
|
||||
* 发布字段与模块页 {@code ProfessionView/src/views/profession/RecordEdit.vue} 逐项一致。
|
||||
*
|
||||
* <p>★ 这条钉的是一个**真实提交出错**的差异:早先助手只发 {@code content},
|
||||
* 而模块页发的是 {@code remark}(正文)—— 字段名不对,正文根本没存进去,
|
||||
* 同时「年级段 / 学科 / 是否公开 / 附件」四项完全缺失,发出去的记录与在系统里发的天然不同。
|
||||
*/
|
||||
@Test
|
||||
void professionPublishFieldsMatchRecordEditForm() {
|
||||
ToolDef def = registry.get("profession.publish");
|
||||
assertNotNull(def, "要有「发布专业发展记录」工具");
|
||||
assertEquals(Arrays.asList(
|
||||
"moduleId", "title", "grade", "subject", "ifOpen", "remark", "contents", "fileIds"),
|
||||
paramNames(def),
|
||||
"字段与顺序都要对齐模块页:标题 → 年级段/学科 → 是否公开 →(栏目动态字段)→ 正文 → 附件");
|
||||
assertNull(param(def, "content"), "正文的字段名是 remark —— 模块页不叫 content");
|
||||
}
|
||||
|
||||
@Test
|
||||
void professionPublishIsPostFormFullFormAndStaffOnly() {
|
||||
ToolDef def = registry.get("profession.publish");
|
||||
assertEquals("POST_FORM", def.getHttpMethod(), "模块页 create() 走 postFormUrl(form-urlencoded)");
|
||||
assertEquals("/api/profession/Create", def.getPath());
|
||||
assertTrue(def.isFormAll(), "发布页要渲染全部字段(与模块页「发布」页一致)");
|
||||
assertFalse(def.isReadOnly(), "发布是写操作");
|
||||
assertEquals("/record/new", def.getLandingPath());
|
||||
assertTrue(def.allowsAny(new java.util.HashSet<>(Arrays.asList(com.yunhai.agent.auth.AgentRole.TEACHER))),
|
||||
"专业发展是教职工工具");
|
||||
assertFalse(def.allowsAny(new java.util.HashSet<>(Arrays.asList(com.yunhai.agent.auth.AgentRole.STUDENT))),
|
||||
"学生不应看到专业发展发布");
|
||||
}
|
||||
|
||||
@Test
|
||||
void professionPublishFieldControlsMatchModulePage() {
|
||||
ToolDef def = registry.get("profession.publish");
|
||||
|
||||
ToolParam title = param(def, "title");
|
||||
assertTrue(title.isRequired(), "标题必填");
|
||||
assertEquals(200, title.getMaxLength(), "模块页 :maxlength=\"200\"");
|
||||
assertEquals("请输入记录标题", title.getPlaceholderText(), "占位文案照搬模块页");
|
||||
|
||||
// 年级段 / 学科:两个可空下拉,选项都来自模块字典(服务端取不到 → 前端带登录态代取)
|
||||
assertEquals("profession.grades", param(def, "grade").getOptionsFrom());
|
||||
assertEquals(Arrays.asList("value", "label"), optionsKeysOf(param(def, "grade")),
|
||||
"年级段字典是 {value,label},不是平台通用的 {k,v}");
|
||||
assertEquals("profession.subjects", param(def, "subject").getOptionsFrom());
|
||||
assertEquals(Arrays.asList("subId", "subName"), optionsKeysOf(param(def, "subject")),
|
||||
"学科字典是 {subId,subName}");
|
||||
|
||||
// 是否公开:单选按钮组,默认「公开」(模块页 ifOpen 初值 1)
|
||||
ToolParam ifOpen = param(def, "ifOpen");
|
||||
assertEquals("radio", ifOpen.getWidget());
|
||||
assertEquals(Arrays.asList("1", "0"), ifOpen.getEnumValues());
|
||||
assertEquals(Arrays.asList("公开", "不公开"), ifOpen.getEnumLabels());
|
||||
assertEquals("1", ifOpen.getDefaultValue());
|
||||
|
||||
// 正文:多行文本,占位文案与模块页的富文本编辑器一致
|
||||
assertEquals("textarea", param(def, "remark").getWidget());
|
||||
assertEquals("请输入正文内容…", param(def, "remark").getPlaceholderText());
|
||||
|
||||
// 附件:上传接口与其余模块**不同**(/api/file/Upload,不是 /api/base/FileUpload)
|
||||
ToolParam fileIds = param(def, "fileIds");
|
||||
assertEquals("file", fileIds.getWidget());
|
||||
assertEquals(",", fileIds.getJoinSeparator(), "fileIds 在 wire 上是逗号串");
|
||||
assertEquals("/api/file/Upload", fileIds.getUploadPathOverride());
|
||||
}
|
||||
|
||||
/**
|
||||
* 栏目动态字段(contents)的代取声明。
|
||||
*
|
||||
* <p>这组字段由**栏目配置**决定、构建期枚举不出来 —— 模块页是
|
||||
* {@code GET /api/moudule/Getdatelist?SerID=<栏目>} 取回后就地渲染,
|
||||
* 提交时打包成 {@code contents}。助手必须走同一条路,否则发到有自定义字段的栏目会缺数据。
|
||||
*/
|
||||
@Test
|
||||
void professionPublishDeclaresColumnDynamicFields() {
|
||||
ToolDef def = registry.get("profession.publish");
|
||||
assertEquals("profession.moduleFields", def.getExtraFieldsFrom());
|
||||
assertEquals("contents", def.getExtraFieldsParam());
|
||||
assertEquals("remark", def.getExtraFieldsBefore(),
|
||||
"模块页把栏目字段排在正文之前,不锚定会掉到附件后面");
|
||||
assertEquals("moduleId", def.getExtraFieldsDepends().get("SerID"),
|
||||
"模块侧查询参数叫 SerID,表单字段叫 moduleId —— 必须显式映射");
|
||||
assertEquals("profession.areaTree", def.getExtraFieldsAreaFrom(),
|
||||
"级联字段(province_city)要能取到区划树");
|
||||
assertTrue(param(def, "contents").isHidden(),
|
||||
"contents 由表单打包生成,不作为字段渲染、也不让模型填");
|
||||
|
||||
// 代取地址要带模块真实前缀,且把依赖留成占位符(由前端替换后再发)
|
||||
ToolDef dict = registry.get("profession.moduleFields");
|
||||
assertEquals("/api/moudule/Getdatelist", dict.getPath());
|
||||
assertTrue(param(dict, "SerID").isRequired(), "栏目字段接口必须指定栏目");
|
||||
BrowserToolRequest.BrowserCall call =
|
||||
executor.prepareOptionCall(dict, java.util.Collections.singletonList("SerID"));
|
||||
assertNotNull(call, "代取描述要能生成");
|
||||
assertEquals("/profession/api/moudule/Getdatelist?SerID={{SerID}}", call.getPathAndQuery());
|
||||
|
||||
// 附件与代取地址都用「配置里的真实前缀」,而不是模块 key 拼出来的
|
||||
assertEquals("/profession",
|
||||
AgentOrchestrator.pathPrefix("profession", "http://base.yunhaixingtong.com/profession"));
|
||||
}
|
||||
|
||||
/* ==================== 辅助 ==================== */
|
||||
|
||||
/** 字典返回对象里「值 / 显示名」取的是哪两个键(optionsKeys 的结果) */
|
||||
private List<String> optionsKeysOf(ToolParam p) {
|
||||
if (p == null) {
|
||||
return null;
|
||||
}
|
||||
return Arrays.asList(p.getOptionValueKey(), p.getOptionLabelKey());
|
||||
}
|
||||
|
||||
private ToolParam param(ToolDef def, String name) {
|
||||
if (def == null || def.getParams() == null) {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user