Install
openclaw skills install yzj-form-parser云之家(YunZhijia)表单数据解析与构建技能。用于解析云之家表单JSON结构,支持双向转换:推送数据解析和审批发起数据构建。触发场景:(1) 解析云之家审批推送数据 (2) 构建发起审批接口的表单数据 (3) 理解云之家表单widgetMap和detailMap结构 (4) 处理各类控件:文本、单选、多选、日期、人员、部门、明细表等。
openclaw skills install yzj-form-parser云之家表单结构在推送数据和发起审批接口中是完全一致的,只是数据流向不同:
widgetMap → 业务字段(读取数据)widgetMap(写入数据){
"formInfo": {
"widgetMap": { /* 主表单控件 */ },
"detailMap": { /* 明细表控件 */ }
},
"basicInfo": { /* 基本信息 */ }
}
| 字段 | 类型 | 说明 |
|---|---|---|
eventId | String | 推送事件ID,全局唯一 |
formInstId | String | 表单实例ID |
flowInstId | String | 流程实例ID |
formDefId | String | 表单模版ID |
formCodeId | String | 表单模版codeId |
dataType | Integer | 0=测试数据,1=正式数据 |
actionType | String | reach/agree/submit/delete/abandon/withdraw/disagree |
title | String | 表单标题 |
eventTime | Long | 时间戳(毫秒) |
widgetMap 是一个 Map,key 为控件的 codeId。
每个控件都包含:
codeId: 控件唯一标识title: 控件标题type: 控件类型value: 控件值extendFieldMap: 扩展配置| type | 说明 | value类型 | 示例 |
|---|---|---|---|
textWidget | 单行文本 | String | "一行文字" |
textAreaWidget | 多行文本 | String | "多行文字" |
解析: 直接读取 value
构建: 直接设置 value
// 解析
String text = widget.getValue().toString();
// 构建
widget.setValue("文本内容");
| type | 说明 | value类型 |
|---|---|---|
dateWidget | 日期 | Long (毫秒时间戳) |
dateRangeWidget | 日期区间 | Long[] [开始,结束] |
解析: 时间戳转Date 构建: Date转时间戳
// 解析
Long timestamp = (Long) widget.getValue();
Date date = new Date(timestamp);
// 构建
widget.setValue(date.getTime());
| type | 说明 | value类型 |
|---|---|---|
numberWidget | 数字 | String |
moneyWidget | 金额 | String |
arithmeticWidget | 运算 | String |
注意: value 是字符串类型!
// 解析
BigDecimal amount = new BigDecimal(widget.getValue().toString());
// 构建
widget.setValue("100.50");
数据结构:
{
"type": "radioWidget",
"value": "AaBaCcDd", // 选中的key
"options": [
{ "key": "AaBaCcDd", "value": "选项1", "checked": true },
{ "key": "EeFfGgHh", "value": "选项2", "checked": false }
]
}
解析流程:
value(key) → options匹配 → value(显示文本) → 字典匹配 → 业务值
构建流程:
业务值 → 字典匹配 → 显示文本 → options匹配 → key → 设置value
数据结构:
{
"type": "checkboxWidget",
"value": ["AaBaCcDd", "EeFfGgHh"], // 选中的key数组
"options": [
{ "key": "AaBaCcDd", "value": "选项1", "checked": true },
{ "key": "EeFfGgHh", "value": "选项2", "checked": true }
]
}
解析: value 是 key 数组,遍历匹配 构建: 设置 key 数组
数据结构:
{
"type": "personSelectWidget",
"option": "single", // single或multi
"personInfo": [
{
"name": "张三",
"userId": "xxx",
"oid": "xxx",
"image": "头像URL"
}
],
"value": ["xxx"] // oid数组
}
解析: 从 personInfo 获取详细信息
构建: 设置 value 为 oid 数组,同时填充 personInfo
数据结构:
{
"type": "departmentSelectWidget",
"option": "single",
"deptInfo": [
{
"name": "财务部",
"orgId": "xxx",
"longName": "公司/中心/部门"
}
],
"value": ["xxx"] // orgId数组
}
数据结构:
{
"type": "attachmentWidget",
"maximum": 200,
"value": [
{
"fileId": "69f06679d0c3b700014ec175",
"fileName": "document.pdf",
"fileSize": "482",
"fileExt": "pdf",
"fileType": "application/pdf"
}
]
}
{
"type": "imageWidget",
"maximum": 12,
"value": ["fileId1", "fileId2"]
}
注意: 图片控件的 value 是文件ID数组。
{
"type": "relatedWidget",
"value": [
{
"flowInstId": "xxx",
"formInstId": "xxx",
"formDefId": "xxx",
"formCodeId": "xxx",
"title": "关联单据标题"
}
]
}
{
"type": "serialNumWidget",
"value": "1-20171023-002" // 系统自动生成
}
详见 references/detail-widget.md
数据结构:
{
"Dd_0": {
"codeId": "Dd_0",
"title": "明细表名称",
"type": "detailedWidget",
"buttonName": "添加明细",
"widgetVos": { /* 列定义 */ },
"widgetValue": [ /* 行数据 */ ]
}
}
定义明细表中每列的控件结构,与主表控件结构相同:
{
"Te_1": {
"codeId": "Te_1",
"type": "textWidget",
"title": "姓名"
}
}
数组,每行是一个对象,key 为控件的 codeId:
[
{
"_id_": "1", // 行ID
"Te_1": "张三", // 文本值
"Ra_0": "AaBaCcDd", // 单选key
"Da_0": 1508688000000 // 日期时间戳
}
]
// 1. 解析主表
Map<String, WidgetInfo> widgetMap = formInfo.getWidgetMap();
// 文本
String title = (String) widgetMap.get("_S_TITLE").getValue();
// 日期
Long timestamp = (Long) widgetMap.get("Da_0").getValue();
Date date = new Date(timestamp);
// 单选
WidgetInfo radio = widgetMap.get("Ra_0");
String key = (String) radio.getValue();
String label = getOptionLabel(radio.getOptions(), key);
// 人员
WidgetInfo person = widgetMap.get("Ps_0");
String name = person.getPersonInfo().get(0).getName();
// 2. 解析明细表
DetailWidget detail = detailMap.get("Dd_0");
Map<String, WidgetInfo> widgetVos = detail.getWidgetVos(); // 列定义
List<Map<String, Object>> rows = detail.getWidgetValue(); // 行数据
for (Map<String, Object> row : rows) {
String name = (String) row.get("Te_1");
String radioKey = (String) row.get("Ra_0");
// 从widgetVos获取options进行匹配
}
// 1. 构建主表
Map<String, Object> widgetMap = new HashMap<>();
// 文本
widgetMap.put("_S_TITLE", createTextWidget("标题内容"));
// 日期
widgetMap.put("Da_0", createDateWidget(System.currentTimeMillis()));
// 单选
widgetMap.put("Ra_0", createRadioWidget("AaBaCcDd", options));
// 人员
widgetMap.put("Ps_0", createPersonWidget(oid, name));
// 2. 构建明细表
List<Map<String, Object>> rows = new ArrayList<>();
for (Item item : items) {
Map<String, Object> row = new HashMap<>();
row.put("Te_1", item.getName());
row.put("Ra_0", item.getRadioKey());
rows.add(row);
}
numberWidget、moneyWidget 的 value 是字符串_id_ 字段,构建时可不设置