迅捷应用平台 · AI 表单设计
把用户的表单设计需求转成 DesignSpec DSL 提交给迅捷应用平台,由用户在表单设计器中加载应用(近实时可见),保存永远由设计器侧完成。
何时触发
- 用户要求修改已有业务单据的表单设计:加字段/加明细表/加标题区块/改字段属性/删字段/调整分组
- 用户要求新建一张业务单据并设计其结构(先 create_form 建空单,再提交设计)
- 用户给出图片、文档、口述需求,希望"照这个做个单据"
- 用户要求创建/修改统计报表:新建单表统计(实体统计,按分组维度对单据数据做计数/求和并出图表)、修改已有统计的列树/排序/筛选
- 用户要求创建/修改关联查询(多表 join 或多表数据合并/纵向合并)、报表指标(把统计列注册成可投放的指标卡片)
不要触发
- 填单、查单、发起流程、审批、附件、关联选择 → 用 xj-form-agent skill
- 执行报表取数/查统计结果/统计下钻 → 用 xj-form-agent skill(
search_single_stat等运行时取数工具在那边;本 skill 只负责报表的创建与修改)。空间投放组件配置(把指标挂到分析空间大屏)→ 需在设计器手工配置,无 Agent 工具 - 用户未绑定第三方 Agent 身份、或明确不想通过 Agent 操作 → 停止
接入方式(先读这里,决定要不要走「一、配置与认证」)
本技能有两种运行方式,凭据处理完全不同,先认清自己在哪种:
| 运行方式 | 谁持有 bindSecret | Agent 要做什么 |
|---|---|---|
| A · MCP 宿主/连接器(如 WorkBuddy 等) | 用户在 MCP 宿主侧已配置并连接 xunjie MCP 服务(连接时填了 HHD_BIND_SECRET),h-design-mcp 自动 exchange 换 token 并注入 |
先探测(见下方判定)确认 MCP 可用后,直接调用「三、可用工具」。不要引导用户重新创建绑定、不要写 scripts/config.json、不要跑 agent.mjs auth、不要持久化 bindSecret。报「未认证」时让用户在宿主的连接配置中重新填/刷新 bindSecret |
B · 自托管 CLI(Claude Code 等非 MCP 宿主,走 scripts/agent.mjs / curl) |
Agent 侧持有:须手动绑定 + agent.mjs 持久化后复用 |
按「一、配置与认证」手动完成绑定后才能调用 |
> 判定(先探测,不要假设自己处于哪种方式):
> 1. 查 MCP 服务:检查本会话是否已连接名为 xunjie 的 MCP 服务(宿主的 MCP/连接器列表里能找到,且能列出本 skill 的平台工具)。也可以直接试调一个无参轻量读工具(如 get_totp_status / list_stat_categories)——返回正常数据即方式 A 可用,整节跳过「一、配置与认证」,直接看「三、可用工具」及各 SOP。
> 2. 探测不到就走方式 B:没有 xunjie MCP、服务未连接、试调报「无可用工具/超时/未认证且无法刷新」——视为 MCP 不可用,走方式 B(自托管 CLI):按「一、配置与认证」完成绑定后经 scripts/agent.mjs 调用。
> 3. 两种方式下工具名与参数完全一致,仅认证方式不同。
>
> 工具范围(scope):MCP 连接配置的「工具范围」字段填 designer 时,连接只暴露本 skill 的设计类工具(表单/报表设计,含 shared)——推荐本 skill 的连接统一填 designer,避免无关工具干扰。留空=全部工具;/call 不做强制隔离,误调 agent 侧工具只是浪费不会破坏。
一、配置与认证(仅方式 B:自托管 CLI 需要;方式 A(MCP) 已由连接器完成,跳过本节)
与本平台 xj-form-agent 的认证机制相同,但凭证不互通(本 skill 有独立的加密绑定存储,需各自 auth 一次):
- 引导用户生成绑定:让用户在 web 端「第三方Agent绑定」菜单创建绑定并复制,复制内容同时包含连接地址与 bindSecret(形如
HHD_BASE_URL=…、HHD_BIND_SECRET=…) - 写入连接地址:把复制的连接地址写入本 skill 的
scripts/config.json(文件不存在则创建):
json { "baseUrl": "<复制的地址>" }
优先级:config.json> 环境变量HHD_BASE_URL> 默认值。首次配置后必须写入,否则可能连到错误环境。 - 换取 token 并持久化:
bash node scripts/agent.mjs auth "<bindSecret>"
也可从环境变量HHD_BIND_SECRET读取。token 默认 30 分钟,过期后 agent.mjs 会用本地绑定自动重换。 - 之后所有调用统一走:
bash node scripts/agent.mjs call --name <工具名> --args '<json>' # 大 payload 用 --args-file <路径> 或 stdin 管道 - 验证登录:随便调一个读工具(如
list_my_designable_forms)返回 success 即可。
二、前置条件(向用户说明清楚)
- 新建:用
create_form在指定应用包下创建,随后同流程提交设计 - 修改已有单据:目标表单的创建者必须就是当前绑定的用户
- 迅捷令牌(TOTP)二次认证:设计类写操作工具(create_form / submit_form_design_spec / save_form_enum / save_form_code / create_form_folder / move_node_to_folder 及统计报表的 create/update/delete)受 TOTP 保护——未通过时调用会被拒绝。建议首次提交写操作前先用
get_totp_status预检;未绑定或未通过时,提示用户登录系统完成一次迅捷令牌口令认证,认证通过后即可正常调用(无需 Agent 侧做任何认证操作) - 设计必须由用户在设计器中打开该表单并确认加载后才会应用到画布;保存始终由用户手动点击设计器保存按钮完成——应用后请提醒用户检查效果并保存,用户保存前 stamp 不会前进
- 未打开设计器时指令挂起 10 分钟,过期作废
三、可用工具
| 工具 | 用途 | 关键参数 |
|---|---|---|
list_my_designable_forms |
列出可设计的表单(意图确认用;⚠️ 新建未挂菜单的单据不在此列表,用 create_form 返回的 id) | businessSpaceId? |
create_form |
在指定应用包下新建空单据(返回 formDefinitionId) | formName, appId, folderId?, remark? |
get_form_design |
读表单完整定义(修改前必做) | formDefinitionId |
get_form_design_stamp |
读设计 updateTime(乐观比对/确认落库) | formDefinitionId |
submit_form_design_spec |
提交 DesignSpec DSL | formDefinitionId, spec, baseUpdateTime?(推荐 get_form_design_stamp 的 updateTimeMillis) |
get_design_command_status |
查指令应用状态 pending/applied/failed | commandId |
list_form_enums |
列出应用枚举(公共+本单位)及选项明细,dict 类字段设计前必查;重名枚举认准 dict 值 | keyword?(模糊)/name?(精确)/enumId?, scope?(public/unit/all) |
save_form_enum |
创建应用枚举/默认建到本单位枚举(公共枚举由管理员维护,只查用不新建)/向已有枚举追加选项 | name, items:[{title, value?(可省略自动生成)}], enumId?, scope?(默认 unit) |
list_form_codes |
列出单据编码规则(流水号),formCode 字段设计前必查 | keyword? |
save_form_code |
创建单据编码规则(前缀/日期段/流水号位数等) | codeName, codePrefix?, dateRule?, resetRule?, initCode?, fixedLength? |
list_sys_roles |
列出系统角色,add_flow 审批人(type=role)需要 roleId | keyword? |
list_sys_users |
按姓名/账号搜索用户,add_flow 审批人(type=user)需要 userId | keyword, limit? |
list_app_forms_tree |
列出应用包下完整单据树(文件夹+单据层级),新建前先查归属位置 | appId |
create_form_folder |
在应用包下创建文件夹(可指定上级、同时挂入已有单据) | name, appId, parentId?, formIds?[] |
move_node_to_folder |
把单据/文件夹移动到指定文件夹下 | nodeId, folderId |
get_totp_status |
查询迅捷令牌(TOTP)绑定与校验状态(bound/verified + hint),设计类写操作受 TOTP 保护,首次提交写操作前建议预检 | 无参数 |
辅助(shared 工具,两种 scope 都可见):agent_form_get_dict(按编码查字典项)、list_my_menus(列我的菜单,用于确认 appId)。
统计报表(14 工具)——创建/维护三类报表:单表统计(前端叫「实体统计」)、关联查询(join 与数据合并)、报表指标。结构、枚举与完整 payload 契约见 references/report-design.md(必读后再构造 stat/query 大对象)。
| 工具 | 用途 | 关键参数 |
|---|---|---|
list_stat_categories |
列出报表分类树(单表统计的 statCategory / 关联查询的 queryCategory 取值来源;无合适分类时引导用户在平台建,首期无创建分类工具) | keyword?(按分类名过滤) |
list_single_stats |
列出应用下已有单表统计(重名检查/修改前意图确认),返回 id/statTitle/statCategory/chartType | appId, keyword? |
get_single_stat |
读取完整统计定义(含列树/数据源/排序/筛选)。修改前必读 | singleStatId |
create_single_stat |
一次性整树提交创建(列树用 label 引用字段)。同步返回,含 label→fieldId 解析对照表 columnIdMap | appId, stat(object) |
update_single_stat |
⚠️ 整树覆盖更新:未传的子结构会被删除,必须先 get_single_stat 再全量提交 |
singleStatId, stat(object) |
delete_single_stat |
删除单表统计(其下全部配置一并删除)。删除前与用户确认 | singleStatId |
list_form_queries |
列出应用下已有关联查询(join 与数据合并均返回,含 queryType) | appId, keyword?, queryType? |
get_form_query |
读取完整关联查询定义(relation:sources/joinConditions/columns/orders/searchFields;merge:统一列+各源字段映射)。修改前必读 | queryId |
create_form_query |
创建关联查询。queryType=relation(join 关联)或 merge(数据合并),结构见 report-design.md §6 | appId, query(object) |
update_form_query |
⚠️ 整树覆盖更新,必须先 get_form_query 再全量提交 |
queryId, query(object) |
delete_form_query |
删除关联查询。删除前与用户确认 | queryId |
list_report_targets |
列出报表指标(按应用或按单表统计),返回 id/targetName/singleStatId/columnId | appId?, singleStatId?(至少传一个) |
save_report_target |
创建/更新指标(upsert:传 id 即更新)。columnId 从 create_single_stat 返回的 columnIdMap 取 |
targetName, appId, singleStatId, columnId, id? |
delete_report_target |
删除报表指标。删除前与用户确认 | id |
> preview_single_stat(对已保存统计试算前 N 行)属二期可选工具,一期不注册——试算效果请引导用户到「报表分析空间」查看。
curl 通用模板:
curl -X POST "$HHD_BASE_URL/api/agent/form/call" \
-H "X-Access-Token: <token>" -H "X-Tenant-Id: <tenantId>" -H "Content-Type: application/json" \
-d '{"name":"submit_form_design_spec","arguments":{"formDefinitionId":"...","spec":{...}}}'
四、核心概念
- DesignSpec DSL:你只产出设计意图 JSON(ops 列表),不生成 HTML。结构见
references/design-spec.md,组件取值见references/components-catalog.md。 - 字段定位:在已有字段前/后插入用
insert_fields(锚点 + before/after,后续字段自动顺延)、调整字段顺序用move_field(同区块内)。 - 动作自动带出:应用设计时系统自动创建「填写/审批/显示」三个默认动作(含全字段权限),无需在 DSL 中表达;
required: true的字段自动写入动作必填配置。 - 业务列表用 add_menu 且必须分两段:字段/布局变更保存落库后才能提交 add_menu(系统强制);可指定列表显示列(showFields)、查询项(searchFields)、排序(orderFields)、按钮(buttons)、条件查询公式(searchFormula)、详情视图配置(detailViews),均按字段 label 匹配;缺省项有合理默认。
- 业务单据关系用 add_relation / edit_relation:三种类型 manual(关联选择按钮,选候选单据带出字段,如报销单选发票)/ auto(本表字段变化自动匹配带出)/ details(目标表数据刷新插入本表明细行)。目标表单字段 id 先
get_form_design(目标表单)查询;过滤公式变量带 a_/b_ 前缀(a_=本表字段、b_=目标表字段,明细{明细表id.字段id},见 design-spec.md §12);manual 支持授权(authes)、树形选择器(treeConfigId,引用已有配置)、选择器扩展按钮(buttons);应用成功即生效(与 add_menu/add_trigger 一样必须分段提交)。修改用 edit_relation(find 定位后按参数增量合并;换目标单据需全量重建,见 design-spec.md §14)。 - 触发器用 add_trigger / edit_trigger(⚠️ 应用即生效):notify 消息通知 / create 新建目标单据 / update 按条件更新目标单据 / addOrUpdate 按关键字段新建或更新 / updateSelf 更新本表单;触发点支持流程状态(pass/withdraw/toForm/end)、节点、每次审核等,支持 cron 定时(如台账到期提醒)。生产标配
operator: {"field": "经办人"}(决定以谁的身份写目标单据);回写累计金额用 formula 源 binds({a_源字段}+{b_目标字段},与 source 二选一);source支持"processInstanceTitle"(流程标题);update 支持insertDetail(插入目标明细行)与preWriteBinds(发起流程时预写目标单据,如预占指标/置在途);notify 支持通知穿透视图(viewConfigs)。与 add_menu 一样必须分段提交,应用成功即落库生效、无需用户保存。结构见 design-spec.md §13(含真实业务场景对照表)。修改用 edit_trigger(find 定位后按参数增量合并;换触发类型需删除重建,见 design-spec.md §15)。 - 审批流程用 add_flow / edit_flow(支持分支网关/条件路由/嵌套):审批人支持 发起人/角色/指定人/部门/表单部门字段,userId/roleId 用 list_sys_users/list_sys_roles 查询;条件公式语法见 design-spec.md(⚠️ 公式变量类型只允许 number/string,部门等业务字段用 string,不要写 :depart:);编辑流程图 = 读现状后用 edit_flow.nodes 全量重建;应用+用户手动保存即注册到流程引擎。
- 表单现状:
get_form_design返回的 formDefinition 是只读参考(结构见references/formdefinition-schema.md),你的所有修改必须基于它做增量,绝不凭空整表回写。 - 双确认:动手前向用户复述设计计划;删除字段在设计器端还有一道用户确认弹窗。
- 统计报表三件套(独立的报表体系,结构见
references/report-design.md): - 单表统计(create_single_stat)/ 关联查询(create_form_query)/ 报表指标(save_report_target) 依赖顺序固定:先建单表统计,再建指标(指标引用统计的 columnId,从创建返回的 columnIdMap 取);关联查询也可反过来作为单表统计的数据源。
- 列树规则一句话版:group(分组)/stat(统计项)只能做末级,field(字段列)/text(自定义列)必须挂 children 才有下一级,formula(公式列)首期不支持;statType 要按源字段组件类型选(number 用 mean 不是 avg),日期分组粒度 groupType 只有 day/month/season/year。
- 字段用 label 引用,与 add_menu 按字段 label 匹配同一心智:Agent 只写字段中文名,agent 层解析成 fieldId;歧义/未命中会报错并附候选字段列表,照报错消歧即可,不要自造 fieldId。⚠️ 实测坑:label 匹配对尾部空格敏感(源字段 label 带尾空格会解析失败,改传 fieldId);orders 的 label 只能排列树里已有的节点,不能排不在列树里的字段。
- 整树一次性提交:创建与更新都是全量 payload,同步返回(无指令轮询环节);⚠️ 更新未传的子结构会被删除。
五、典型 SOP —— 修改表单设计
- 收集需求:从文本/图片/文档提取字段清单(中文名、类型、必填、选项、是否明细表)。
- 确认目标:新建还是改哪张单据?
- 新建:问清归属的应用包(appId,用户不确定时用list_my_menus辅助确认)→ 先list_app_forms_tree看应用包结构(是否需要建文件夹归类)→ 需要create_form_folder→create_form(formName, appId, folderId?)拿 formDefinitionId。⚠️ 用户描述里包含审批/流转/多人审批需求的单据,create_form 必须传hasFlow: true(虽然 add_flow 应用时也会自动切换,但一开始就声明能保证菜单按钮、页签都按流程单生成)
- 修改:用户不确定时调list_my_designable_forms列候选让用户选。⚠️ 不要擅自替用户决定 - 读现状:修改场景先
get_form_design(formDefinitionId)→ 梳理现有 fields/subForms 的 label 与 id、hasFlow、布局概况(新建场景跳过)。 - 复述设计计划:把计划(几栏、哪些块、明细表、字段清单、删除项、业务列表的显示列/查询项/按钮)讲给用户确认。流程表(hasFlow=true)或已启用表单要提示影响。
- 取时间戳:
get_form_design_stamp记下 updateTimeMillis(毫秒数,推荐;updateTime 格式化串也兼容)作 baseUpdateTime。 - 产出 DesignSpec:按
references/design-spec.md构造 ops;dict 类字段(radio/checkbox/selectS/selectM)先list_form_enums查已有枚举复用,没有合适的用save_form_enum创建;formCode 字段先list_form_codes查编码规则、没有再save_form_code创建,字段必须带 formCodeId;自检references/field-naming-rules.md的清单;大 payload 写入临时文件用--args-file提交。 - 提交:
submit_form_design_spec(formDefinitionId, spec, baseUpdateTime)→ 记下 commandId。 - 轮询结果:
get_design_command_status(commandId)
-applied→ 复述 applied 明细,提醒用户在设计器中检查效果并手动保存(列宽/合并等微调也需手动拖拽)→ 结束或继续迭代
-pending→ 提示用户在设计器打开该表单确认加载;10 分钟内有效,过期需重新提交
-failed→ 向用户原样转述 errors,修复后回到第 5 步 - 验证落库:用户手动保存后
get_form_design_stampupdateTime 前进即已落库;若 stamp 未前进,说明用户尚未保存,礼貌提醒即可,不要反复催促
> ⚠️ 业务列表(add_menu)、关联(add_relation/edit_relation)、触发器(add_trigger/edit_trigger)必须分两段提交:字段/布局变更保存落库后才能操作(它们引用的字段必须是已保存字段)。第一段提交字段/布局 → 用户手动保存(stamp 前进)→ 第二段单独提交。混提会被系统直接拒绝。
> ⚠️ 并发保护:提交被拒 表单已在读取后被修改 时,说明有其他人改了表单——重新读现状与时间戳后再提交,不要盲目重试。
> ⚠️ 完整端到端样例(含真实 payload)见 references/design-worked-example.md。
六、典型 SOP —— 创建统计报表
> 适用于单表统计(实体统计);关联查询(join/数据合并)流程同构,把 create_single_stat 换成 create_form_query 即可。列树/枚举/payload 细节一律以 references/report-design.md 为准。
- 收集需求:统计什么(分组维度/统计值/图表形态)、数据源是哪张单据(是否统计明细表)、归属哪个应用(appId)。
- 确认目标:新建还是改已有?改则先
list_single_stats(appId, keyword?)列候选让用户选,不要擅自替用户决定;确认 appId。 - 摸底数据源:
list_app_forms_tree确认源表单及 formDefinitionId;统计明细表时确认主从关系(sources 传 主表+明细表 一对,明细的 subFormDefinitionId 必须属于该主表)。 - 查分类:
list_stat_categories取分类节点 id 作 statCategory;无合适分类时引导用户在平台「报表」页签建(首期无创建分类的 Agent 工具),不要编造分类 id。 - 复述计划:分组列/统计列/统计方式(sum/count/…)/图表类型/分类归属,向用户确认后再动手。
- 产出 payload:按
references/report-design.md构造stat对象;自检列树规则(group/stat 只能末级、statType 与字段组件匹配)与 §8 自检清单;大 payload 用--args-file提交。 - 提交:
create_single_stat(appId, stat)→ 同步返回。检查返回的columnIdMap(label→columnId/fieldId 对照表),确认每个统计列都解析成功;后续建指标要用里面的 columnId。 - 迭代修错:报错带列树路径定位(如
columns[0].children[1]: 金额字段不支持 group),按路径修 payload 重提;已创建成功后要改 → ⚠️update_single_stat是整树覆盖,必须先get_single_stat读全量再改,未传的子结构会被删除。 - 后续:需要指标卡片 →
save_report_target(targetName, appId, singleStatId, columnId)(columnId 从第 7 步回显取);提醒用户到「报表分析空间」查看效果,空间投放组件需在设计器手工配置。
七、权限与安全边界(必读)
- 认证:bindSecret 换取的 token 即绑定用户身份;仅表单创建者可通过 Agent 提交设计指令,非创建者会被拒绝
- 设计指令不会直接改动表单:必须由用户在设计器中加载确认,保存也由设计器完成并经过完整校验
- 删除字段是高危操作:需要用户在设计器二次确认,且删除后相关配置与历史数据无法自动恢复——仅在用户明确要求时使用
- 不要自造或修改 fieldName(由系统自动生成);改名请引导用户在设计器中操作
- 单据的表名、业务标识等系统属性无权修改
八、已知约束(联调记录)
- 新建场景:
create_form建空单 →submit_form_design_spec提交设计 → 用户在设计器打开加载确认,应用前单据没有任何字段 list_my_designable_forms依赖授权菜单,新建未挂菜单的单据查不到——用 create_form 返回的 id 直接操作- 暂不支持(布局类,需在设计器手工调整):页签/卡片布局、字段跨行合并、明细表结构调整、移动端视图、动作的自定义扩展(默认三动作已够用)。配置类能力(关联授权/树形/扩展按钮、菜单条件公式/详情视图、通知穿透视图)均已支持。
- DSL 一次提交建议 ≤10 个操作 / ≤30 个字段
- token 30 分钟过期由 agent.mjs 自动处理
- 统计报表(首期):
- 公式列(columnType=formula)不支持——公式 token 引用其他列 id,与 label 解析存在先后依赖,待二期设计后再放开
- 空间投放组件配置(把指标挂到分析空间大屏)依赖前端视图 id,需在设计器手工配置,无 Agent 工具
- 列树/报表定义修改是整树覆盖:update 未传的子结构会被删除,务必先 get 再全量提交
- 单表统计无创建者隔离:它是应用级资产,同应用内互相可见、可改;删除前务必与用户确认
九、接入调试(排查顺序)
node scripts/agent.mjs status看绑定状态 → 失效则重新auth- 调
/api/agent/form/tools(node agent.mjs tools --full可带完整参数 Schema)确认工具列表里含submit_form_design_spec(需部署配套服务版本) - HTTP 405 → 用 POST;401 → token 过期,重 auth
get_design_command_status一直 pending → 用户没在设计器加载;超过 10 分钟说明过期,重新提交- applied 但画布没变化 → 让用户在设计器开「AI 协作」或手动刷新页面
十、配套文件
scripts/agent.mjs—— 统一执行入口(auth/call/get/post/tools/upload 等子命令,与 xj-form-agent 同款实现)references/design-spec.md—— ★ DesignSpec DSL 契约(最重要)references/components-catalog.md—— 组件类型枚举与必填配置references/field-naming-rules.md—— 命名与校验铁律references/formdefinition-schema.md—— formDefinition 结构参考(读现状用)references/design-worked-example.md—— 端到端实战样例references/report-design.md—— ★ 统计报表 payload 契约(单表统计列树 DSL / 关联查询 relation+merge / 报表指标)