云之家表单数据解析与构建
概述
云之家表单结构在推送数据和发起审批接口中是完全一致的,只是数据流向不同:
- 推送解析:
widgetMap→ 业务字段(读取数据) - 发起审批: 业务字段 →
widgetMap(写入数据)
表单整体结构
{
"formInfo": {
"widgetMap": { /* 主表单控件 */ },
"detailMap": { /* 明细表控件 */ }
},
"basicInfo": { /* 基本信息 */ }
}
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)
widgetMap 是一个 Map,key 为控件的 codeId。
通用字段
每个控件都包含:
codeId: 控件唯一标识title: 控件标题type: 控件类型value: 控件值extendFieldMap: 扩展配置
控件类型详解
1. 文本控件
| type | 说明 | value类型 | 示例 |
|---|---|---|---|
textWidget | 单行文本 | String | "一行文字" |
textAreaWidget | 多行文本 | String | "多行文字" |
解析: 直接读取 value
构建: 直接设置 value
// 解析
String text = widget.getValue().toString();
// 构建
widget.setValue("文本内容");
2. 日期控件
| type | 说明 | value类型 |
|---|---|---|
dateWidget | 日期 | Long (毫秒时间戳) |
dateRangeWidget | 日期区间 | Long[] [开始,结束] |
解析: 时间戳转Date 构建: Date转时间戳
// 解析
Long timestamp = (Long) widget.getValue();
Date date = new Date(timestamp);
// 构建
widget.setValue(date.getTime());
3. 数字/金额控件
| type | 说明 | value类型 |
|---|---|---|
numberWidget | 数字 | String |
moneyWidget | 金额 | String |
arithmeticWidget | 运算 | String |
注意: value 是字符串类型!
// 解析
BigDecimal amount = new BigDecimal(widget.getValue().toString());
// 构建
widget.setValue("100.50");
4. 单选控件 (radioWidget)
数据结构:
{
"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
5. 多选控件 (checkboxWidget)
数据结构:
{
"type": "checkboxWidget",
"value": ["AaBaCcDd", "EeFfGgHh"], // 选中的key数组
"options": [
{ "key": "AaBaCcDd", "value": "选项1", "checked": true },
{ "key": "EeFfGgHh", "value": "选项2", "checked": true }
]
}
解析: value 是 key 数组,遍历匹配 构建: 设置 key 数组
6. 人员选择控件 (personSelectWidget)
数据结构:
{
"type": "personSelectWidget",
"option": "single", // single或multi
"personInfo": [
{
"name": "张三",
"userId": "xxx",
"oid": "xxx",
"image": "头像URL"
}
],
"value": ["xxx"] // oid数组
}
解析: 从 personInfo 获取详细信息
构建: 设置 value 为 oid 数组,同时填充 personInfo
7. 部门选择控件 (departmentSelectWidget)
数据结构:
{
"type": "departmentSelectWidget",
"option": "single",
"deptInfo": [
{
"name": "财务部",
"orgId": "xxx",
"longName": "公司/中心/部门"
}
],
"value": ["xxx"] // orgId数组
}
8. 附件控件 (attachmentWidget)
数据结构:
{
"type": "attachmentWidget",
"maximum": 200,
"value": [
{
"fileId": "69f06679d0c3b700014ec175",
"fileName": "document.pdf",
"fileSize": "482",
"fileExt": "pdf",
"fileType": "application/pdf"
}
]
}
9. 图片控件 (imageWidget)
{
"type": "imageWidget",
"maximum": 12,
"value": ["fileId1", "fileId2"]
}
注意: 图片控件的 value 是文件ID数组。
10. 关联审批控件 (relatedWidget)
{
"type": "relatedWidget",
"value": [
{
"flowInstId": "xxx",
"formInstId": "xxx",
"formDefId": "xxx",
"formCodeId": "xxx",
"title": "关联单据标题"
}
]
}
11. 流水号控件 (serialNumWidget)
{
"type": "serialNumWidget",
"value": "1-20171023-002" // 系统自动生成
}
明细表控件 (detailMap)
详见 references/detail-widget.md
数据结构:
{
"Dd_0": {
"codeId": "Dd_0",
"title": "明细表名称",
"type": "detailedWidget",
"buttonName": "添加明细",
"widgetVos": { /* 列定义 */ },
"widgetValue": [ /* 行数据 */ ]
}
}
widgetVos (列定义)
定义明细表中每列的控件结构,与主表控件结构相同:
{
"Te_1": {
"codeId": "Te_1",
"type": "textWidget",
"title": "姓名"
}
}
widgetValue (行数据)
数组,每行是一个对象,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 是字符串 - 时间戳: 日期控件的 value 是毫秒级时间戳
- 选项key: 单选/多选的 value 是选项的 key,不是显示文本
- 明细表行ID: 每行有
_id_字段,构建时可不设置 - 数组类型: 多选、人员、部门、附件的 value 都是数组