现在统一推荐函数模式:按泛微、蓝凌、致远、云之家分类的 OA 待办消息模板库,覆盖审批待办、业务待办(BPM / 阶段任务)、消息提醒的新增 / 处理 / 删除模板与配套函数实现,复制即可使用。
本文不是产品概述,而是把线上正在运行的 OA 待办连接器拆成可直接复制的配置块:每个 OA 系统固定四段——连接配置、场景参数、配套函数、验收清单。所有域名、注册码、appId、secret、企业 EI、客户名称都已替换为占位符。
使用前必读(免责声明)
本文档根据线上已正式启用的 OA 待办连接器配置和当前生效的 APL 函数整理总结,暂未逐项经过真实 OA 系统联调校验。不同 OA 版本、租户定制和接口升级都可能造成端点、字段名或状态位差异;如发现文档与现场不一致,请联系集成平台值班同学反馈修改。
怎么用(5 分钟)
- 先按 OA 系统找到对应章节,照 1.1 / 2.1 / 3.1 / 4.1 配连接。
- 按待办类型复制新增、处理、删除三个场景模板,URL 填该系统的端点。
- 把本章的 oaRequest / dataDetail 函数建好,再到连接页「函数参数配置」点「重新设置」挂上;无入参时保持“未设置”。
- 用一条测试待办走完新增 → 处理 → 删除,再批量上线。
线上配置总表(先看结论)
| OA 系统 / 路线 | 常用端点 | 结果解析(典型值) | 函数 | 新增 → 处理 → 删除 | 删除关键写法 |
| 泛微 / 标准接口 | /rest/ofs/ReceiveRequestInfoByJson | operResult · message · 1 | oaRequest + dataDetail | isremark 0 → 2;viewtype 0 → 1 | deleteUserRequestInfoByJson 或 deleteRequestInfoByJson |
| 泛微 / 预置接口 | /rest/ofs/fxiaokeReceiveRequestInfoByJson | operResult · message · 1 | oaRequest + dataDetail | isremark 0 → 2;type 1/2/3/4 | isremark 0 + ifdelete=1,viewtype 保持 0 |
| 泛微 / E10 | /papi/openapi/api/open_intunifytodo/server_engine/receiveRequestInfo | operResult · message · 1 | oaRequest + dataDetail | isremark 0 → 2 | isremark 4 |
| 蓝凌 / 新版 notifybus | /openapi/sys-notifybus/v2/main/send | success · msg · true | dataDetail | send → done → removeTodo | removeTodo |
| 蓝凌 / 旧版 sendTodo | /api/sys-notify/sysNotifyTodoRestService/sendTodo | returnState · message · 2 | dataDetail | sendTodo → setTodoDone → deleteTodo | deleteTodo |
| 致远 | /seeyon/rest/thirdpartyPending/receive/pendings | errcode · errmsg · 200 | oaRequest + dataDetail | state 0 → 1 | flagTax + subState 2 |
| 云之家 | /gateway/newtodo/open/generatetodo.json?accessToken= | errorCode · error · 200 | oaRequest + dataDetail | generatetodo → action.json | actiontype {"delete":1} |
结果解析只是线上典型值,不同 OA 版本可能不同;复制后先用一条真实数据看返回体,再确定 codeName / msgName / successCode。
〇、通用规则
0.1 连接配置页(按页面顺序)
连接配置页从上到下就是三块:基础配置、通用参数配置(Header + 结果格式配置)、函数参数配置。按下面顺序逐块填,不要跳步。
| 页面区域 | 页面字段 | 对应小节 |
| 基础配置 | 连接名称、*对接的系统名称 | 0.1.1 |
| 通用参数配置 | Header、结果格式配置 | 0.1.2、0.1.3 |
| 函数参数配置 | 函数参数 | 0.1.4 |
0.1.1 基础配置
| 页面字段 | 填写内容 |
| 连接名称 | 自定义,建议带 OA 系统名,如 泛微OA待办 / 蓝凌OA待办 |
| *对接的系统名称 | 必填,按实际 OA 填写,如 泛微 OA |
0.1.2 通用参数配置:Header
页面就是两列 KEY / VALUE。线上多数连接显示“暂无数据”,说明 Header 不是必填;只有 OA 明确要求固定请求头时,才按下表加行。
| KEY | VALUE |
| Content-Type | application/json |
| accept | application/json |
| Authorization | Basic <Basic认证串> |
说明:Content-Type 多数接口需要,平台已自动带时可不加;accept 部分泛微接口可选;Authorization 仅蓝凌等接口要求,线上多数蓝凌连接使用。不要三行全抄,按 OA 文档只保留需要的。
- 在 Header 表中新增一行,KEY 填请求头名称,VALUE 填固定值。
- 只放固定值;动态 token、签名、时间戳不要写在这里,统一放 oaRequest 函数。
- secret、appSecret 不写进连接页面;页面配置对租户管理员可见。
- 保存前核对 KEY 与 OA 文档一致,不要多空格。
0.1.3 通用参数配置:结果格式配置
页面就是 KEY / VALUE 两列,固定五行:codeName、msgName、dataName、successCode、xmlJsonField。下面按 OA 系统给出可直接照填的五项值,先找到自己的系统,再把 VALUE 抄到对应行。
【泛微 / 标准接口、预置接口】
| KEY | VALUE |
| codeName | operResult |
| msgName | message |
| dataName | (留空) |
| successCode | 1 |
| xmlJsonField | (留空) |
【泛微 / E10】
| KEY | VALUE |
| codeName | $.message.errcode |
| msgName | $.message.errmsg |
| dataName | data |
| successCode | 200 |
| xmlJsonField | (留空) |
【泛微 / ESB】
| KEY | VALUE |
| codeName | RTNCODE |
| msgName | RTNMSG |
| dataName | RTNDATA |
| successCode | S |
| xmlJsonField | (留空) |
【蓝凌 / 新版 notifybus】
| KEY | VALUE |
| codeName | success |
| msgName | msg |
| dataName | (留空) |
| successCode | true |
| xmlJsonField | (留空) |
【蓝凌 / 旧版 sendTodo】
| KEY | VALUE |
| codeName | returnState |
| msgName | message |
| dataName | data |
| successCode | 2 |
| xmlJsonField | (留空) |
【致远 / 主流返回】
| KEY | VALUE |
| codeName | success |
| msgName | errorMsgs |
| dataName | (留空) |
| successCode | true |
| xmlJsonField | (留空) |
【致远 / errcode 返回】
| KEY | VALUE |
| codeName | errcode |
| msgName | errmsg |
| dataName | data |
| successCode | 200 |
| xmlJsonField | (留空) |
【云之家 / 标准返回】
| KEY | VALUE |
| codeName | success |
| msgName | error |
| dataName | data |
| successCode | true |
| xmlJsonField | (留空) |
【云之家 / 不解析返回】
| KEY | VALUE |
| codeName | (留空) |
| msgName | (留空) |
| dataName | (留空) |
| successCode | (留空) |
| xmlJsonField | (留空) |
- codeName / msgName 支持 $. 开头的 JSONPath;线上有用 $.data.operResult、$.data.message 的写法,按实际返回层级选。
- successCode 前后不要有空格或制表符;线上出现过填成“\t 2”导致全部判失败的案例。
- 返回体把业务码包在 message 或 data 下时,codeName 要写完整路径,不能只写叶子字段。
- xmlJsonField 一般留空;只有 OA 返回 XML 且需要转 JSON 时,才按接口文档填写。
- 保存后先发一条真实请求,在同步日志里看返回体,再回头校准这五行。
0.1.4 函数参数配置
页面底部只有一项「函数参数」;未配置时显示“未设置”,右侧是「重新设置」按钮。这一步用来挂接 APL 函数。
| 页面字段 | 页面显示 | 操作 |
| 函数参数 | 未设置 | 点「重新设置」,关联 oaRequest 和 dataDetail |
- 点击「重新设置」。
- 按页面提示关联请求前处理函数 oaRequest,以及免登函数 dataDetail;函数按各系统章节创建。
- 本文两个函数都没有入参,函数参数可以保持未设置;只有函数本身声明了参数时,才按参数名逐个填写。
- 保存后重新打开连接配置,确认函数参数区域不再显示“未设置”,且两个函数的类型正确。
| 函数类型 | 作用 | 不挂的后果 |
| oaRequest / 请求前处理 | 改写 requestData、url、header;云之家追加 accessToken,致远转换收件人和状态位 | 云之家地址缺 token 或 401;致远报文字段不对 |
| dataDetail / 免登 | 用 ticket 换 OA 用户信息,返回 oaUser、redirectUrl | 点击待办免登失败或跳错页面 |
函数名和类型以页面下拉实际显示为准。各系统需要哪几个函数,见对应章节的“配套函数”小节。
0.2 三类事件与定位键
| 事件 | 含义 | 用哪个模板 | 定位键要求 |
| 1 新增 | 创建待办 / 提醒 | 新增模板 | 新增时写入,后续处理/删除必须能命中同一个键 |
| 2 处理 | 办结、同意、拒绝 | 处理模板 | 与新增完全一致;状态位按系统调整 |
| 3 删除 | 撤销待办 | 删除模板 | 与新增完全一致;部分系统用删除专用端点 |
| 消息提醒 | 只发消息,不落待办 | crmNotify 新增模板 | 没有处理/删除事件 |
最常见的问题不是字段写错,而是新增用一个键、处理或删除换成了另一个键。泛微的 flowid、蓝凌的 modelId/notifyId、致远的 taskId、云之家的 sourceId,都必须三事件一致。
0.3 本文占位符
| 占位符 | 替换为 | 示例 |
| <OA域名> | OA 访问域名(含端口) | oa.example.com:8080 |
| <注册码> | OA 侧注册的集成码 | OA 控制台生成 |
| <syscode> | OA 侧注册的系统标识 | fxiaoke / CRM / FXXK |
| <企业EI> | CRM 企业 ID | 在 CRM 企业信息中查看 |
| <appId> / <appSecret> | OA 应用凭证 | OA 后台应用管理 |
| <tenantKey> | 云之家 / eteams 租户标识 | 云之家后台 |
| <Basic认证串> | 蓝凌 OpenAPI Basic 认证串 | 蓝凌后台生成 |
| <对象API> / <数据ID> | CRM 回调路径中的对象与记录 | AccountObj / 5f... |
一、泛微配置块
泛微有四条落地路线:标准接口(最常用)、预置接口、E10 新开放平台、消息中心。先按 1.2 选定一条路线,再整套复制该路线的新增/处理/删除模板,不要跨路线混用字段。
1.1 连接配置(页面字段)
| 页面字段 | 推荐值 | 说明 |
| OA 类型 | 泛微 | |
| 连接地址 | https://<OA域名> | 带端口时一并填写 |
| Header | Content-Type: application/json | 部分 OA 另加 accept: */*、connection: Keep-Alive |
| 结果解析 | operResult / message / 1 | 如接口以 HTTP 200 判成功,successCode 改为 200 |
| dataName | 留空 | |
| 账号绑定字段 | OA 工号(loginid / workcode) | 与 receiver 占位符口径一致 |
| 不绑定不发送 | 开启 | 避免未绑定人员报错 |
| 函数 | oaRequest + dataDetail | oaRequest 处理请求体;dataDetail 处理免登 |
1.2 路线选择
| 路线 | 端点 | 适用 | 特点 |
| 标准接口 | /rest/ofs/ReceiveRequestInfoByJson | 大多数 E9 | 三事件共用端点;处理改 isremark + viewtype |
| 标准接口删除 | /rest/ofs/deleteUserRequestInfoByJson 或 /rest/ofs/deleteRequestInfoByJson | 对应 OA 版本 | 只传 syscode + flowid(部分版本加 userid) |
| 预置接口 | /rest/ofs/fxiaokeReceiveRequestInfoByJson | 已部署纷享预置包的 OA | 用 type 区分对象;删除加 ifdelete |
| E10 | /papi/openapi/api/open_intunifytodo/server_engine/receiveRequestInfo | 泛微 E10 | 三事件同端点;删除 isremark 4 |
| 消息中心 | /papi/openapi/api/mc/msg/sendMsg | 只要提醒、不落待办 | 仅 crmNotify;没有处理/删除 |
1.3 标准接口模板(最常用)
以下模板来自线上标准接口连接,已脱敏。pcurl / appurl 可以写相对路径(OA 侧已做 SSO 包装),也可以写 CRM 免登回调;两种都用同一套业务字段。
1.3.1 审批待办 ApprovalTaskObj
新增:
{
"syscode": "<syscode>",
"flowid": "#F012",
"requestname": "CRM-#F055-#F053-#F056",
"workflowname": "#F056",
"nodename": "#F026",
"pcurl": "/xv/ui/detail?apiname=#F037&id=#F015",
"appurl": "/hcrm/avah5#/object_detail/object_detail/pages/detail/index?apiName=#F037&dataId=#F015",
"creator": "#F040",
"createdatetime": "#F009",
"receiver": "#F001",
"receivedatetime": "#F009",
"isremark": "0",
"viewtype": "0",
"receivets": "#currentTime#"
}处理:新增模板不变,只改 isremark 和 viewtype。
{
"syscode": "<syscode>",
"flowid": "#F012",
"requestname": "CRM-#F055-#F053-#F056",
"workflowname": "#F056",
"nodename": "#F026",
"pcurl": "/xv/ui/detail?apiname=#F037&id=#F015",
"appurl": "/hcrm/avah5#/object_detail/object_detail/pages/detail/index?apiName=#F037&dataId=#F015",
"creator": "#F040",
"createdatetime": "#F009",
"receiver": "#F001",
"receivedatetime": "#F009",
"isremark": "2",
"viewtype": "1",
"receivets": "#currentTime#"
}删除(独立端点):
{
"syscode": "<syscode>",
"flowid": "#F012"
}定位键:flowid 三事件必须一致。线上有租户用任务 ID #F012,也有租户用单据 ID #F015;选哪个都可以,但新增、处理、删除必须同一个。
1.3.2 新 BPM 待办 BpmTask
新增:
{
"syscode": "<syscode>",
"flowid": "#G005",
"requestname": "【CRM待办】-#G024 -#G051",
"creator": "#G036",
"receiver": "#F001",
"workflowname": "#G021",
"nodename": "#G022",
"pcurl": "/<企业EI>/#G047/#G050/false",
"appurl": "https://<OA域名>/sso/oauth2.0/authorize?client_id=<appId>&response_type=code&redirect_uri=https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#G047/#G050/true",
"viewtype": "0",
"receivedatetime": "#G012",
"isremark": "0",
"createdatetime": "#G012"
}处理:
{
"syscode": "<syscode>",
"flowid": "#G005",
"requestname": "【CRM待办】-#G024",
"creator": "#G036",
"receiver": "#F001",
"workflowname": "#G021",
"nodename": "#G022",
"viewtype": "1",
"receivedatetime": "#G012",
"isremark": "2"
}删除:
{
"userid": "#F001",
"syscode": "<syscode>",
"flowid": "#G005"
}线上 BpmTask 常用 #G005(节点实例 ID)做 flowid;如果同一节点有多个并行任务,改用任务 ID #G023 更稳,但三事件要一起改。接收人统一用 #F001,线上对所有任务对象都这么配。
1.3.3 阶段任务 StageTaskObj
新增:
{
"syscode": "<syscode>",
"flowid": "#ST044",
"requestname": "【CRM阶段任务】-#ST004-#ST041",
"creator": "#ST017",
"receiver": "#F001",
"workflowname": "#ST041",
"nodename": "#ST004",
"pcurl": "/<企业EI>/#ST038/#ST006/false",
"appurl": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#ST038/#ST006/true",
"viewtype": "0",
"receivedatetime": "#ST022",
"isremark": "0",
"createdatetime": "#ST022"
}处理:
{
"syscode": "<syscode>",
"flowid": "#ST044",
"requestname": "【CRM阶段任务】-#ST004-#ST041",
"creator": "#ST017",
"receiver": "#F001",
"workflowname": "#ST041",
"nodename": "#ST004",
"pcurl": "/<企业EI>/#ST038/#ST006/false",
"appurl": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#ST038/#ST006/true",
"viewtype": "1",
"receivedatetime": "#ST022",
"isremark": "2"
}删除:
{
"userid": "#F001",
"syscode": "<syscode>",
"flowid": "#ST044"
}线上有租户删除时用了另一唯一键(如 #ST012),导致处理/删除命中不到新增。建议统一用任务 ID #ST044;如果 OA 侧要求别的键,三事件一起换。
1.3.4 消息提醒 crmNotify
{
"syscode": "<syscode>",
"flowid": "#H005-#H001",
"requestname": "【CRM提醒】-#H002",
"nodename": "#H006",
"receiver": "#H001",
"workflowname": "#H006",
"viewtype": "0",
"creator": "#H001",
"isremark": "0",
"createdatetime": "#currentTime#",
"receivedatetime": "#currentTime#",
"pcurl": "/<企业EI>/#H003/#H005/false",
"appurl": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#H003/#H005/true"
}注意:标准接口这条模板会生成一条待办。如果只要提醒、不进待办列表,改用消息中心路线,或确认 OA 支持 isremark 8。
1.4 预置接口模板
预置接口用 type 区分对象类型,新增/处理/删除共用一个端点。线上 type 的用法是:1 审批任务、2 BPM 任务、3 阶段任务、4 工单。
新增(审批任务 type=1):
{
"syscode": "<syscode>",
"flowid": "#F012",
"requestname": "#F056",
"workflowname": "#F055",
"nodename": "#F026",
"pcurl": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#F037/#F015/false",
"appurl": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#F037/#F015/true",
"approval": "#F012",
"dataid": "#F015",
"objectname": "#F037",
"approvalcode": "#F013",
"creator": "#F049",
"createdatetime": "#F009",
"receiver": "#F001",
"receivedatetime": "dateTime(#currentTime#)",
"isremark": "0",
"viewtype": "0",
"type": "1",
"receivets": "#currentTime#"
}处理:只改 isremark=2,viewtype 保持 0。
{
"syscode": "<syscode>",
"flowid": "#F012",
"requestname": "#F056",
"workflowname": "#F055",
"nodename": "#F026",
"pcurl": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#F037/#F015/false",
"appurl": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#F037/#F015/true",
"approval": "#F012",
"dataid": "#F015",
"objectname": "#F037",
"approvalcode": "#F013",
"creator": "#F049",
"createdatetime": "#F009",
"receiver": "#F001",
"receivedatetime": "dateTime(#currentTime#)",
"isremark": "2",
"viewtype": "0",
"type": "1",
"receivets": "#currentTime#"
}删除:isremark 保持 0,加 ifdelete=1。
{
"syscode": "<syscode>",
"flowid": "#F012",
"requestname": "#F056",
"workflowname": "#F055",
"nodename": "#F026",
"receiver": "#F001",
"isremark": "0",
"viewtype": "0",
"type": "1",
"ifdelete": "1",
"receivets": "#currentTime#"
}| 对象 | type | flowid 建议 | requestname 建议 |
| ApprovalTaskObj 审批任务 | 1 | #F012 | #F056 |
| BpmTask 新 BPM 任务 | 2 | #G023 | #G024 |
| StageTaskObj 阶段任务 | 3 | #ST044 | #ST041 |
| CasesObj 工单 | 4 | #C032 | #C022 |
线上有些预置接口删除模板写 isremark=4,有些写 0 + ifdelete=1。两种都能见到;以 OA 侧预置包版本为准,但新增/处理/删除的 flowid 必须一致。
1.5 E10 开放平台模板
新增:
{
"syscode": "<syscode>",
"flowid": "#F012",
"requestname": "#F055",
"workflowcode": "#F050",
"workflowname": "#F055",
"receivernodename": "#F026",
"nodename": "#F026",
"pcurl": "/<企业EI>/#F037/#F015/false",
"appurl": "/<企业EI>/#F037/#F015/false",
"creator": "#F040",
"createdatetime": "#F009",
"receiver": "#F001",
"receivedatetime": "#currentTime#",
"isremark": "0",
"viewtype": "0",
"receivets": ""
}处理:
{
"syscode": "<syscode>",
"flowid": "#F012",
"requestname": "#F055",
"workflowcode": "#F050",
"workflowname": "#F055",
"receivernodename": "#F026",
"nodename": "#F026",
"creator": "#F040",
"createdatetime": "#F009",
"receiver": "#F001",
"receivedatetime": "#currentTime#",
"isremark": "2",
"viewtype": "1",
"receivets": ""
}删除:
{
"syscode": "<syscode>",
"flowid": "#F012",
"receiver": "#F001",
"isremark": "4",
"viewtype": "0",
"receivets": ""
}线上 E10 删除用的是 isremark 4,不是 -1。只有 OA 版本明确要求 -1 时才改;三事件的 viewtype 建议保持一致,处理时可用 1。
1.6 配套函数
连接里通常挂两个函数:oaRequest 处理请求体(可选,但线上普遍使用),dataDetail 处理免登。泛微的 token 获取放在 dataDetail。
1.6.1 oaRequest:请求体二次处理
// 函数类型:oaRequest
// 作用:只处理 requestData,不修改 URL
Map requestData = Fx.json.parse(syncArg["requestData"] as String)
String workflowname = requestData["workflowname"] as String
String nodename = requestData["nodename"] as String
if ((workflowname != null && workflowname.contains("【作废】"))
|| (nodename != null && nodename.contains("【作废】"))) {
log.info("命中作废关键字,跳过同步")
return null
}
if (requestData["createdatetime"] == "#createdatetime") {
requestData.put("createdatetime", DateTime.now() as String)
}
if (requestData["receivedatetime"] == "#receivedatetime") {
requestData.put("receivedatetime", DateTime.now() as String)
}
syncArg.put("requestData", Fx.json.toJson(requestData))
return syncArg如果 BpmTask 的 pcurl 指向任务本身而不是业务单据,可以在这个函数里先查 BpmTask,再把 pcurl 改写成业务对象的 API 名和数据 ID。线上有租户这么做;没有这个问题就保持上面的精简版。
1.6.2 dataDetail:免登
// 函数类型:dataDetail
Map requestParams = syncArg["params"]["requestParams"] as Map
String apiName = requestParams["apiName"] as String
String dataId = requestParams["dataId"] as String
boolean isApp = requestParams["isApp"] as boolean
String ticketCode = requestParams["ticket"] as String
String PREFIX_URL = "https://<OA域名>/"
String CLIENTID = "<appId>"
String CLIENTSECRET = "<appSecret>"
String redirect_uri = "https://www.fxiaoke.com/oauth/sp/callback"
if (!apiName) {
String home = isApp ? "https://www.fxiaoke.com/hcrm/avah5"
: "https://www.fxiaoke.com/XV/UI/Home#crm/index"
syncArg.put("redirectUrl", home)
return syncArg
}
String tokenUrl = PREFIX_URL + "sso/oauth2.0/accessToken?client_id=" + CLIENTID
+ "&client_secret=" + CLIENTSECRET + "&code=" + ticketCode
+ "&grant_type=authorization_code&redirect_uri=" + redirect_uri
def (Boolean error, HttpResult data, String errorMessage) = Fx.http.post(tokenUrl, [:], [:])
if (error) {
message.throwErrorMessage("获取OA token失败:" + errorMessage)
}
Map result = Fx.json.parse(data.content as String)
String accessToken = result.access_token as String
HttpResult userData = (HttpResult) http.get(
PREFIX_URL + "sso/oauth2.0/profile?access_token=" + accessToken,
["access_token": accessToken]).getData()
String oaLoginId = userData.content["attributes"]["loginid"].toString()
def (Boolean error1, QueryResult dataU, String errorMessage1) = Fx.object.find(
"PersonnelObj",
FQLAttribute.builder()
.columns(["_id", "name", "employee_number", "user_id", "field_<绑定字段>__c"])
.queryTemplate(QueryTemplate.AND([
"field_<绑定字段>__c": QueryOperator.EQ(oaLoginId)]))
.build(),
SelectAttribute.builder().needInvalid(false).build()
)
if (dataU.size > 0) {
Map person = (dataU.dataList as List)[0] as Map
syncArg.put("oaUser", person["field_<绑定字段>__c"].toString())
} else {
message.throwErrorMessage("找不到对应的CRM人员:" + oaLoginId)
}
return syncArg1.7 泛微验收与常见错误
| 现象 | 优先检查 |
| 平台显示成功,OA 没有待办 | flowid 是否为空;receiver 是否绑定;OA 返回码是否被 resultFormat 判成成功 |
| 处理/删除无效 | 三事件 flowid 是否一致;标准接口处理是否把 viewtype 从 0 改成 1 |
| 待办重复 | flowid 是否每条都唯一;重试是否重复消费 |
| 免登打不开 | dataDetail 是否挂上;pcurl/appurl 是否指向正确对象和数据 ID |
| 401 / URL 参数重复 | 函数是否重复追加 token 或地址参数 |
| 找不到用户 | PersonnelObj 绑定字段是否与 OA 返回的 loginid 一致 |
二、蓝凌配置块
蓝凌有两代接口:新版 notifybus(send / done / removeTodo,推荐)和旧版 sysNotifyTodoRestService(sendTodo / setTodoDone / deleteTodo,存量客户常见)。先确认 OA 部署的是哪一代,再整套复制。
2.1 连接配置(页面字段)
| 页面字段 | 新版 notifybus | 旧版 sendTodo |
| OA 类型 | 蓝凌 | 蓝凌 |
| 连接地址 | https://<蓝凌域名> | https://<蓝凌域名> |
| Header | Content-Type: application/json;Authorization: Basic <Basic认证串> | Content-Type: application/json;部分部署无需鉴权 |
| 结果解析 | success / msg / true | returnState / message / 2 |
| dataName | 留空 | 留空 |
| 账号绑定字段 | LoginName | LoginName 或 PersonNo |
| 函数 | dataDetail | dataDetail |
线上新版连接通常没有 oaRequest;如果要在请求体里做工号转换或字段清洗,再额外加一个 oaRequest。
2.2 新版 notifybus 模板(推荐)
新增(审批任务):
{
"appName": "crm",
"content": "#F004",
"creator": {"fdLoginName": "admin", "fdOrgType": 8},
"entityId": "#F012",
"entityKey": "crm",
"entityName": "crm",
"link": "https://<蓝凌域名>/data/sys-oauth/ssoLogin?appId=<appId>&redirection_url=https%3a%2f%2fwww.fxiaoke.com%2ferp%2fsyncdata%2fopen%2foa%2fauthorize%2fcommon%2flogin%3fei%3d<企业EI>%26apiName%3d#F037%26dataId%3d#F015",
"mobileLink": "同 link",
"moduleName": "crm",
"notifyId": "#F012",
"notifyType": "todo",
"subject": "#F004",
"targets": "{\"fdLoginName\":\"#F001\",\"fdOrgType\":8}"
}处理:
{
"appName": "crm",
"entityId": "#F012",
"entityKey": "crm",
"entityName": "crm",
"moduleName": "crm",
"notifyId": "#F012",
"targets": "{\"fdLoginName\":\"#F001\",\"fdOrgType\":8}",
"timestamp": #currentTime#
}删除:
{
"appName": "crm",
"entityId": "#F012",
"entityKey": "crm",
"entityName": "crm",
"moduleName": "crm",
"notifyId": "#F012",
"targets": "{\"fdLoginName\":\"#F001\",\"fdOrgType\":8}",
"timestamp": #currentTime#
}不同对象的替换关系:
| 对象 | entityId / notifyId | content | subject | link 路径 |
| ApprovalTaskObj | #F012 | #F004 | #F004 | #F028/#F037/#F015 |
| BpmTask | #G023 | #G022 | #G021 | #G026/#G047/#G050 |
| StageTaskObj | #ST044 | #ST041 | #ST041 | #ST002/#ST038/#ST006 |
| CasesObj | #C032 | #C022 | #C022 | #C010/#C039/#C032 |
targets 是 JSON 字符串,不是对象;双引号要转义。线上最常见的失败就是 targets 写成对象,OA 侧解析不到接收人。
2.3 旧版 sendTodo 模板(存量)
新增:
{
"appName": "CRM",
"modelName": "#F037",
"modelId": "#F012-#F001",
"subject": "#F055[#F053]#F056",
"link": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#F037/#F015/false",
"mobileLink": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#F037/#F015/true",
"padLink": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#F037/#F015/false",
"type": 1,
"targets": "{\"LoginName\":\"#F001\"}",
"createTime": "dateTime(#currentTime#)"
}处理:
{
"appName": "CRM",
"modelName": "#F037",
"modelId": "#F012-#F001",
"optType": 2,
"targets": "{\"LoginName\":\"#F001\"}"
}删除:
{
"appName": "CRM",
"modelName": "#F037",
"modelId": "#F012-#F001",
"optType": 1,
"type": 1,
"targets": "{\"LoginName\":\"#F001\"}"
}旧版线上常把接收人拼进 modelId(如 #F012-#F001),避免同一任务多人的待办互相覆盖。新版用 entityId/notifyId + targets,不需要拼接收人。
2.4 消息提醒 crmNotify(旧版)
{
"appName": "CRM",
"modelName": "CRM_REMIND",
"modelId": "#H005-#H001",
"subject": "#H002",
"content": "#H006",
"type": 2,
"targets": "{\"LoginName\":\"#H001\"}",
"link": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#H003/#H005/false",
"mobileLink": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#H003/#H005/true",
"padLink": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#H003/#H005/false",
"createTime": "dateTime(#currentTime#)"
}modelName 按 OA 实际模型填写:线上有 CRM_REMIND,也有具体类名(如 KmReviewMain)。targets 可用 LoginName 或 PersonNo,和连接绑定字段保持一致。
2.5 配套函数 dataDetail
// 函数类型:dataDetail
Map requestParams = syncArg["params"]["requestParams"] as Map
String apiName = requestParams["apiName"] as String
String dataId = requestParams["dataId"] as String
boolean isApp = requestParams["isApp"] as boolean
String code = requestParams["mkCode"] as String
if (!code) { code = requestParams["code"] as String }
String oaBaseUrl = "https://<蓝凌域名>"
String appId = "<appId>"
String appSecret = "<appSecret>"
Map<String, String> oaHeaders = [
"Content-Type": "application/json",
"Authorization": "Basic <Basic认证串>"
]
String oaUser = null
if (!apiName) {
String home = isApp ? "https://www.fxiaoke.com/hcrm/avah5"
: "https://www.fxiaoke.com/XV/UI/Home#crm/index"
syncArg.put("redirectUrl", home)
}
if (code) {
String tokenUrl = oaBaseUrl + "/openapi/sys-oauth/openOauth/getToken"
Map<String, Object> tokenArg = ["appId": appId, "appSecret": appSecret, "code": code]
def (Boolean tokenError, HttpResult tokenResult, String tokenMsg) = http.post(tokenUrl, oaHeaders, tokenArg)
String token = null
if (!tokenError && tokenResult != null && tokenResult.statusCode == 200) {
Map tokenContent = tokenResult.content as Map
if (tokenContent["success"] == true && tokenContent["data"] != null) {
token = (tokenContent["data"] as Map)["token"] as String
}
}
if (!token) {
message.throwErrorMessage("蓝凌 getToken 失败:" + tokenMsg)
}
String userInfoUrl = oaBaseUrl + "/openapi/sys-oauth/openOauth/getUserInfo"
Map<String, Object> userArg = ["token": token] as Map<String, Object>
def (Boolean userError, HttpResult userResult, String userMsg) = http.post(userInfoUrl, oaHeaders, userArg)
if (!userError && userResult != null && userResult.statusCode == 200) {
Map userContent = userResult.content as Map
if (userContent["success"] == true && userContent["data"] != null) {
oaUser = (userContent["data"] as Map)["loginName"] as String
}
}
if (!oaUser) {
message.throwErrorMessage("蓝凌 getUserInfo 失败:" + userMsg)
}
}
if (oaUser) {
syncArg.put("oaUser", oaUser)
} else {
message.throwErrorMessage("未获取到 OA code,无法免登")
}
return syncArg2.6 蓝凌验收与常见错误
| 现象 | 优先检查 |
| 新增成功但接收人为空 | targets 是否为 JSON 字符串;LoginName/PersonNo 与绑定字段是否一致 |
| 处理/删除无效 | modelId/notifyId 是否与新增一致;新版是否用了 done/removeTodo 对应端点 |
| 平台成功但 OA 无待办 | resultFormat successCode 是否为 true(新版)或 2(旧版) |
| 免登失败 | getToken/getUserInfo 的 appId、appSecret、Basic 串是否正确;code 参数名是 mkCode 还是 code |
| 重复待办 | 旧版 modelId 是否包含接收人;新版 notifyId 是否唯一 |
三、致远配置块
致远线上把 taskId 后缀、动作映射、状态更新都放在 oaRequest 函数里;模板只写基础字段。复制时一定要把模板和函数一起复制,不能只复制模板。
3.1 连接配置(页面字段)
| 页面字段 | 推荐值 | 说明 |
| OA 类型 | 致远 | |
| 连接地址 | https://<OA域名> | 带端口时一并填写 |
| Header | Content-Type: application/json | 线上也有留空仍可用的部署 |
| 结果解析 | errcode / errmsg / 200 | dataName 可填 data |
| 账号绑定字段 | OA 工号(employee_number) | 函数会把 user_id 转工号 |
| 不绑定不发送 | 开启 | 线上多数连接开启 |
| 函数 | oaRequest + dataDetail | oaRequest 负责发送/更新/消息;dataDetail 负责免登 |
3.2 审批待办 ApprovalTaskObj
新增:
{
"registerCode": "<注册码>",
"taskId": "#F012",
"title": "#F056",
"senderName": "#F040",
"classify": "CRM",
"state": "0",
"thirdReceiverId": "#F001",
"creationDate": "#currentTime#",
"h5url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#F037/#F015/true",
"url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#F037/#F015/false",
"noneBindingSender": "#F040",
"noneBindingReceiver": "#F001",
"objectId": "#F015",
"entityId": "#F037"
}处理:state=1,带 action,flagTax 触发更新。
{
"registerCode": "<注册码>",
"taskId": "#F012",
"title": "#F056",
"senderName": "#F040",
"classify": "CRM",
"state": "1",
"thirdReceiverId": "#F001",
"creationDate": "#currentTime#",
"h5url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#F037/#F015/false",
"url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#F037/#F015/false",
"noneBindingSender": "#F040",
"noneBindingReceiver": "#F001",
"action": "#F061",
"objectId": "#F015",
"flagTax": "U",
"entityId": "#F037"
}删除:state=1,subState=2。
{
"registerCode": "<注册码>",
"taskId": "#F012",
"title": "#F056",
"senderName": "#F040",
"classify": "CRM",
"state": "1",
"thirdReceiverId": "#F001",
"creationDate": "#currentTime#",
"h5url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#F037/#F015/false",
"url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#F037/#F015/false",
"noneBindingSender": "#F040",
"noneBindingReceiver": "#F001",
"subState": "2",
"objectId": "#F015",
"entityId": "#F037"
}3.3 新 BPM 待办 BpmTask
新增:
{
"registerCode": "<注册码>",
"taskId": "#G023",
"title": "#G021",
"senderName": "#G036",
"classify": "CRM",
"state": "0",
"thirdReceiverId": "#F001",
"creationDate": "#currentTime#",
"h5url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#G047/#G050/true",
"url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#G047/#G050/false",
"noneBindingSender": "#G036",
"noneBindingReceiver": "#F001",
"objectId": "#G050",
"entityId": "#G047"
}处理:
{
"registerCode": "<注册码>",
"taskId": "#G023",
"title": "#G021",
"senderName": "#G036",
"classify": "CRM",
"state": "1",
"thirdReceiverId": "#F001",
"creationDate": "#currentTime#",
"h5url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#G047/#G050/true",
"url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#G047/#G050/false",
"noneBindingSender": "#G036",
"noneBindingReceiver": "#F001",
"action": "#G019",
"objectId": "#G050",
"flagTax": "U",
"entityId": "#G047"
}删除:
{
"registerCode": "<注册码>",
"taskId": "#G023",
"title": "#G021",
"senderName": "#G036",
"classify": "CRM",
"state": "1",
"thirdReceiverId": "#F001",
"creationDate": "#currentTime#",
"h5url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#G047/#G050/false",
"url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#G047/#G050/false",
"noneBindingSender": "#G036",
"noneBindingReceiver": "#F001",
"subState": "2",
"objectId": "#G050",
"entityId": "#G047"
}线上有模板把 BpmTask 的 objectId/entityId 写成 #F015/#F037,会导致关联对象不准;按对象换成 #G050/#G047。
3.4 阶段任务 StageTaskObj
新增:
{
"registerCode": "<注册码>",
"taskId": "#ST044",
"title": "#ST041",
"senderName": "#ST017",
"classify": "CRM",
"state": "0",
"thirdReceiverId": "#F001",
"creationDate": "#currentTime#",
"h5url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#ST038/#ST006/true",
"url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#ST038/#ST006/false",
"noneBindingSender": "#ST017",
"noneBindingReceiver": "#F001",
"objectId": "#ST006",
"entityId": "#ST038"
}处理:
{
"registerCode": "<注册码>",
"taskId": "#ST044",
"title": "#ST041",
"senderName": "#ST017",
"classify": "CRM",
"state": "1",
"thirdReceiverId": "#F001",
"creationDate": "#currentTime#",
"h5url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#ST038/#ST006/true",
"url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#ST038/#ST006/false",
"noneBindingSender": "#ST017",
"noneBindingReceiver": "#F001",
"flagTax": "U",
"objectId": "#ST006",
"entityId": "#ST038"
}删除:
{
"registerCode": "<注册码>",
"taskId": "#ST044",
"title": "#ST041",
"senderName": "#ST017",
"classify": "CRM",
"state": "1",
"thirdReceiverId": "#F001",
"creationDate": "#currentTime#",
"h5url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#ST038/#ST006/false",
"url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#ST038/#ST006/false",
"noneBindingSender": "#ST017",
"noneBindingReceiver": "#F001",
"subState": "2",
"objectId": "#ST006",
"entityId": "#ST038"
}3.5 消息提醒 crmNotify
{
"thirdpartyRegisterCode": "<消息注册码>",
"thirdpartyMessageId": "#H004",
"messageContent": "#H006",
"thirdpartySenderId": "#H001",
"thirdpartyReceiverId": "#H001",
"messageH5URL": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#H003/#H005/true",
"messageURL": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#H003/#H005/false",
"noneBindingSender": "#H001",
"noneBindingReceiver": "#H001"
}致远的消息和待办是两个端点:待办走 thirdpartyPending,消息走 thirdpartyMessage/singleMessage。消息模板里的注册码通常与待办注册码分开申请。
3.6 配套函数 oaRequest
这个函数是致远配置的核心:消息分支、工号转换、action 映射、taskId 后缀、状态更新都在这里。模板里的 taskId 只写原始任务 ID,函数会追加“-接收人”,保证一人一条。
// 函数类型:oaRequest
Map requestData = Fx.json.parse(syncArg["requestData"] as String) as Map
Map headerMap = syncArg["header"] as Map
String url = syncArg["url"] as String
// 1. 消息提醒:直接调消息接口
String thirdpartyRegisterCode = requestData["thirdpartyRegisterCode"] as String
if (thirdpartyRegisterCode != null) {
requestData["creation_date"] = DateTime.now() as String
String messageH5URL = requestData["messageH5URL"] as String
if (messageH5URL != null && messageH5URL.contains("<企业EI>//")) {
requestData["messageH5URL"] = messageH5URL.replace("<企业EI>//", "<企业EI>/<消息回调标识>/")
}
String messageURL = requestData["messageURL"] as String
if (messageURL != null && messageURL.contains("<企业EI>//")) {
requestData["messageURL"] = messageURL.replace("<企业EI>//", "<企业EI>/<消息回调标识>/")
}
Map oaResult = OABPMUtilsPro.messageTodo(requestData)
return ["requestData": requestData, "header": headerMap, "url": url, "result": oaResult]
}
// 2. 发送人/接收人转 OA 工号
String senderName = covtStr(requestData["senderName"] as String)
String thirdReceiverId = covtStr(requestData["thirdReceiverId"] as String)
requestData["senderName"] = senderName
requestData["thirdReceiverId"] = thirdReceiverId
requestData["noneBindingSender"] = senderName
requestData["noneBindingReceiver"] = thirdReceiverId
// 3. 审批动作 → 致远 subState
String action = requestData["action"] as String
if (action) {
switch (action) {
case "cancel": break
case "reject": requestData.put("subState", "1"); break
case "agree": requestData.put("subState", "0"); break
default: break
}
requestData.remove("action")
}
// 4. taskId 追加接收人,保证一人一条
String taskId = requestData["taskId"] as String
requestData["taskId"] = taskId + "-" + thirdReceiverId
// 5. 有 flagTax:走状态更新(处理/删除);否则新建
String flagTax = requestData["flagTax"] as String
if (flagTax != null) {
String subState = requestData["subState"] as String
if (subState == null) {
subState = (action == "agree") ? "0" : "1"
}
requestData.put("subState", subState)
Map oaResult = OABPMUtils.updatePendingState(requestData)
return ["requestData": requestData, "header": headerMap, "url": url, "result": oaResult]
}
Map oaResult = OABPMUtils.sendTodo(requestData)
return ["requestData": requestData, "header": headerMap, "url": url, "result": oaResult]
def String covtStr(String str) {
String resultStr = str
if (str != null && !str.contains("V")) {
if (str.contains("[")) {
str = str.replace("[", "").replace("]", "") as String
}
FQLAttribute fql = FQLAttribute.builder()
.columns(["_id", "name", "user_id", "employee_number"])
.queryTemplate(QueryTemplate.AND(["user_id": QueryOperator.EQ(str)]))
.build()
def (Boolean error, Map data, String errorMessage) = Fx.object.findOne(
"PersonnelObj", fql, SelectAttribute.builder().build())
resultStr = (data == null || data.size() == 0) ? str : data["employee_number"] as String
if (data != null && data["employee_number"] == null) {
resultStr = str
}
}
return resultStr
}OABPMUtils / OABPMUtilsPro 是致远对接工具类,按当前环境实际类名替换。删除时如果 OA 要求 subState=2,模板里写上 subState,函数会保留传入值;线上部分旧函数会重算成 1,需要按这个版本修正。
3.7 配套函数 dataDetail
// 函数类型:dataDetail
Map requestParams = syncArg["params"]["requestParams"] as Map
String apiName = requestParams["apiName"] as String
String dataId = requestParams["dataId"] as String
boolean isApp = requestParams["isApp"] as boolean
String ticket = requestParams["v5ticket"] as String
String oaticket = requestParams["oaticket"] as String
if (oaticket) {
syncArg.put("oaUser", oaticket)
return syncArg
}
String redirectUrl
if (!apiName || apiName == "" || apiName.contains("<消息回调标识>") || apiName.contains("H003")) {
redirectUrl = isApp ? "https://www.fxiaoke.com/hcrm/avah5"
: "https://www.fxiaoke.com/XV/UI/Home#crm/index"
syncArg.put("redirectUrl", redirectUrl)
}
String userId = OABPMUtils.ticket(ticket)
syncArg.put("oaUser", userId)
return syncArg3.8 致远验收与常见错误
| 现象 | 优先检查 |
| 新增成功但处理无效 | taskId 是否由同一个函数追加了相同接收人后缀 |
| 多人收到同一条待办 | taskId 是否追加了 thirdReceiverId;函数是否启用 |
| 删除后仍显示待办 | 删除模板是否带 subState=2;flagTax 是否触发 updatePendingState |
| 发送人/接收人为空 | covtStr 是否把 user_id 转成 employee_number;PersonnelObj 是否有工号 |
| 消息发不出 | 是否走了 thirdpartyMessage 端点;消息注册码是否正确 |
| 平台成功但 OA 无数据 | resultFormat errcode/200 是否与 OA 返回一致;函数是否返回了 result |
四、云之家配置块
云之家 token 由 oaRequest 获取:场景 URL 末尾保留 ?accessToken=,由 oaRequest 调用 YZJService.getAppToken() 追加;连接页面不保存 appSecret。
4.1 连接配置(页面字段)
| 页面字段 | 推荐值 | 说明 |
| OA 类型 | 云之家 | |
| 连接地址 | https://<云之家域名> | 标准云之家为 https://www.yunzhijia.com |
| Header | Content-Type: application/json | |
| 结果解析 | errorCode / error / 200 | |
| dataName | 留空 | |
| 账号绑定字段 | 云之家 openId | 与 params[].openId、openids 一致 |
| 不绑定不发送 | 开启 | |
| 函数 | oaRequest + dataDetail | oaRequest 追加 token;dataDetail 换 openId |
新增/处理/删除的 URL 都以 ?accessToken= 结尾,不要手动填 token;函数会在请求前拼上。
4.2 待办模板(审批 / BPM / 阶段任务)
审批待办新增:
{
"content": "#F056 ->单据号:#F053,提交人:#F054",
"title": "#F055",
"itemtitle": "#F056",
"headImg": "https://<云之家域名>/space/c/photo/load?id=<默认头像ID>",
"appId": "<appId>",
"senderId": "#F049",
"todoType": "1",
"params": [{"openId": "#F001"}],
"url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#F037/#F015/true",
"sourceId": "#F012-#F001",
"sync": true
}处理:
{
"sourceitemid": "#F012-#F001",
"sourcetype": "<appId>",
"actiontype": {"deal": 1},
"openids": ["#F001"],
"sync": true
}删除:
{
"sourceitemid": "#F012-#F001",
"sourcetype": "<appId>",
"actiontype": {"delete": 1},
"openids": ["#F001"],
"sync": true
}不同对象的替换关系:
| 对象 | sourceId | title | itemtitle | content | url 路径 |
| ApprovalTaskObj | #F012-#F001 | #F055 | #F056 | #F056 / #F053 / #F054 | #F028/#F037/#F015 |
| BpmTask | #G023-#F001 | #G021 | #G024 | 提交人:#G051 | #G026/#G047/#G050 |
| StageTaskObj | #ST044-#F001 | #ST004 | #ST041 | #ST041 | #ST002/#ST038/#ST006 |
| CasesObj | #C032-#F001 | #C004 | #C022 | #C022 | #C010/#C039/#C032 |
sourceId 必须三事件一致:新增写进 sourceId,处理和删除用 sourceitemid 回指,sourcetype 回指 appId。线上有租户用 #ST041、#ST043 做阶段任务 sourceId,建议统一用任务 ID #ST044,避免同名任务冲突。
4.3 消息提醒模板
方式一:generatetodo 发消息(线上常用):
{
"type": "message",
"msgType": "#H007",
"content": "#H006",
"title": "#H002",
"feedid": #H010,
"headImg": "https://<云之家域名>/space/c/photo/load?id=<默认头像ID>",
"appId": "<appId>",
"todoType": "0",
"params": [{"openId": "#H001"}],
"url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#H003/#H005/true",
"sourceId": "#H005-#H004",
"sync": true
}方式二:云之家 pubacc/pubsend(部分部署):
{
"type": "message",
"content": "#H002-#H006",
"title": "#H002",
"appId": "<appId>",
"todoType": "0",
"noticeType": #H011,
"params": [{"openId": "#H001"}],
"url": "https://www.fxiaoke.com/erp/syncdata/open/oa/authorize/common/<企业EI>/#H003/#H005/true",
"sourceId": "#H005",
"sync": true
}feedid 用结构化占位符 #H010(不加引号),用于 @me / feed 深链;如果模板里写成字符串 "#H010",拿到的是字面量而不是消息 JSON。
4.4 配套函数 oaRequest
// 函数类型:oaRequest
Map requestData = Fx.json.parse(syncArg["requestData"] as String)
if (requestData["url"]) {
String reqUrl = requestData["url"] as String
if (reqUrl.contains("#H003")) {
requestData["url"] = reqUrl.replace("#H003", "H003")
}
}
String msgType = requestData["msgType"] as String
if (msgType && msgType == "text") {
requestData.remove("url")
}
syncArg.put("requestData", Fx.json.toJson(requestData))
String url = syncArg["url"] as String
String accessToken = YZJService.getAppToken()
syncArg.put("url", url + accessToken)
return syncArg线上这个函数直接修改 syncArg["url"],所以场景 URL 必须预留 ?accessToken=,否则 token 没有位置追加。
4.5 配套函数 dataDetail
// 函数类型:dataDetail
String userAgent = syncArg["params"]["headers"]["user-agent"] as String
String apiName = syncArg["params"]["requestParams"]["apiName"] as String
String dataId = syncArg["params"]["requestParams"]["dataId"] as String
Map requestParams = syncArg["params"]["requestParams"] as Map
boolean apiNameIsEmpty = (apiName == null || apiName == "" || apiName == "null")
def mobilePattern = /Mobile|iPad|iPhone|Android|BlackBerry|Windows Phone|webOS/
def isMobile = (userAgent =~ mobilePattern).find()
boolean fromCrmMessage = requestParams.containsKey("crm_msg")
String redirectUrl
if (!fromCrmMessage && !apiNameIsEmpty) {
redirectUrl = isMobile ?
"https://www.fxiaoke.com/hcrm/avah5#/uipaas_custom/object_detail/pages/detail/index?apiName=" + apiName + "&dataId=" + dataId :
"https://www.fxiaoke.com/XV/UI/Home#paasapp/detail/=/appId_CRM/" + apiName + "/" + dataId
} else {
redirectUrl = isMobile ? "https://www.fxiaoke.com/hcrm/avah5"
: "https://www.fxiaoke.com/XV/UI/Home#paasapp/remind/405/=/appId_CRM"
}
syncArg.put("redirectUrl", redirectUrl)
String ticket = requestParams["ticket"] as String
String openid = YZJService.getTicketOpenid(ticket)
syncArg.put("oaUser", openid)
return syncArg4.6 云之家验收与常见错误
| 现象 | 优先检查 |
| 401 / token 无效 | 场景 URL 是否以 ?accessToken= 结尾;oaRequest 是否已挂载;YZJService 是否可用 |
| 处理/删除无效 | sourceitemid 是否等于新增 sourceId;sourcetype 是否等于新增 appId |
| 接收人收不到 | params[].openId / openids 是否为云之家 openId;账号绑定字段是否一致 |
| 文本消息报错 | 函数是否在 msgType=text 时删除 url |
| @me 拿不到 feedId | feedid 是否用结构化 #H010;消息模板是否带 origin |
| 多人待办互相覆盖 | sourceId 是否包含接收人,#F001 |
五、横向速查
5.1 三事件状态位
| 系统 / 路线 | 新增 | 处理 | 删除 | 定位键 |
| 泛微 标准接口 | isremark 0,viewtype 0 | isremark 2,viewtype 1 | deleteUserRequestInfoByJson / deleteRequestInfoByJson | flowid |
| 泛微 预置接口 | isremark 0,type 1-4,viewtype 0 | isremark 2,viewtype 0 | isremark 0 + ifdelete 1 | flowid |
| 泛微 E10 | isremark 0,viewtype 0 | isremark 2,viewtype 1 | isremark 4,viewtype 0 | flowid |
| 蓝凌 新版 | send | done | removeTodo | entityId / notifyId |
| 蓝凌 旧版 | sendTodo | setTodoDone optType 2 | deleteTodo optType 1 | modelId |
| 致远 | state 0 | state 1 + action + flagTax | state 1 + subState 2 | taskId(函数追加 -接收人) |
| 云之家 | generatetodo,todoType 1 | action.json deal 1 | action.json delete 1 | sourceId → sourceitemid |
5.2 令牌表
审批待办 ApprovalTaskObj(#F0xx):
| 令牌 | 字段 API 名 | 含义 |
| #F001 | candidate_ids | 任务待处理人(所有任务对象模板里都用作接收人) |
| #F004 | name | 任务主题 |
| #F009 | create_time | 创建时间 |
| #F012 | _id | 任务 ID(常用定位键) |
| #F013 | activity_id | 节点 ID |
| #F015 | object_data_id | 单据 ID |
| #F022 | personhandler | 处理人(部分 OA 模板使用) |
| #F023 | creator | 创建人(部分 OA 模板使用) |
| #F026 | task_name | 任务名称 |
| #F028 | tenant_id | 企业 ID(回调路径使用) |
| #F037 | object_api_name | 对象 API 名 |
| #F040 | owner | 负责人 |
| #F045 | workflow_instance_id | 流程实例 ID |
| #F049 | submitter | 提交人 |
| #F050 | workflow_code | 流程编码(E10 模板使用) |
| #F053 | object_data_id__r | 单据名称 |
| #F054 | submitter__r | 提交人姓名 |
| #F055 | object_api_name__r | 对象中文名 |
| #F056 | workflow_instance_id__r | 流程标题 |
| #F061 | action | 审批动作(agree / reject / cancel) |
| #F062 | reply_user | 审批人 |
| #F063 / #F064 | approval_time | 审批时间 |
新 BPM 待办 BpmTask(#G0xx):
| 令牌 | 字段 API 名 | 含义 |
| #G004 | activityId | 节点 ID |
| #G005 | activity_instance_id | 节点实例 ID(线上常用作 flowid) |
| #G009 | applicantId | 申请人 |
| #G012 | create_time | 创建时间 |
| #G019 | action_code | 操作码(线上用作 action) |
| #G021 | name | 任务主题 |
| #G022 | taskName | 任务名称 |
| #G023 | _id | 任务 ID(推荐定位键) |
| #G024 | workflowInstanceName | 流程主题 |
| #G026 | tenant_id | 企业 ID |
| #G027 | objectIds | 单据 ID 集合 |
| #G029 | stageName | 阶段名 |
| #G031 | objectApiName | 对象 API 名(部分模板使用) |
| #G036 | owner | 负责人 |
| #G040 | record_type | 记录类型 |
| #G044 | current_candidate_ids | 当前待处理人 |
| #G047 | objectApiName | 对象 API 名 |
| #G050 | objectDataId | 单据数据 ID |
| #G051 | owner__r | 处理人姓名 |
| #G052 | nodeObjectId | 节点流转对象 ID(部分模板使用) |
阶段任务 StageTaskObj(#ST0xx):
| 令牌 | 字段 API 名 | 含义 |
| #ST002 | tenant_id | 企业 ID |
| #ST004 | workflow_name | 流程名 |
| #ST006 | object_data_id | 关联单据 ID |
| #ST017 | created_by_name | 创建人 / 发起人(线上作 senderName) |
| #ST019 | last_modified_time | 最后修改时间 |
| #ST022 | create_time | 创建时间 |
| #ST030 | created_by | 创建人 |
| #ST038 | object_api_name | 对象 API 名 |
| #ST039 | current_candidate_ids | 当前待处理人 |
| #ST041 | name | 任务名称 |
| #ST043 | order_by | 排序值(部分线上模板用作 sourceId,需确认唯一) |
| #ST044 | _id | 任务 ID(推荐定位键) |
消息对象 crmNotify(#H0xx):
| 令牌 | 字段 API 名 | 含义 |
| #H001 | receiverId | 接收人 |
| #H002 | title | 标题 |
| #H003 | objectApiName | 对象 API 名 |
| #H004 | workflowInstanceId | 流程实例 ID |
| #H005 | objectId | 对象数据 ID |
| #H006 | messageContent | 消息正文 |
| #H007 | type | 消息类型(card / text) |
| #H009 | enterpriseId | 企业 ID |
| #H010 | origin | 原始消息 JSON(取 feedId / 深链用) |
| #H011 | noticeType | 通知类型(部分 OA 使用) |
5.3 结果解析与账号绑定
| 系统 / 路线 | codeName | msgName | successCode | 账号绑定字段 |
| 泛微 | operResult | message | 1 | OA 工号 loginid |
| 蓝凌 新版 | success | msg | true | LoginName |
| 蓝凌 旧版 | returnState | message | 2 | LoginName / PersonNo |
| 致远 | errcode | errmsg | 200 | employee_number |
| 云之家 | errorCode | error | 200 | openId |
5.4 上线核对清单
- 连接页面不配置动态 URL / token,secret 没有写在页面或模板里,动态逻辑都在 oaRequest。
- 函数参数配置已关联 oaRequest 和 dataDetail,页面不再显示“未设置”。
- 连接结果解析的 codeName / msgName / successCode 与 OA 实际返回一致,successCode 没有前后空格。
- 账号绑定字段与模板里的接收人占位符口径一致(工号 / LoginName / openId)。
- 新增、处理、删除三事件的定位键完全一致。
- 泛微处理事件已把 viewtype 从 0 改成 1(标准接口)。
- 蓝凌 targets 是 JSON 字符串,不是对象。
- 致远 oaRequest 已启用,taskId 会追加“-接收人”。
- 云之家场景 URL 以 ?accessToken= 结尾,oaRequest 已追加 token。
- 函数日志里能看到每次请求的 requestData 和 OA 返回,且没有敏感信息。
- 用一条真实待办走完新增 → 处理 → 删除,再检查 OA 当前状态。
六、版本与来源说明
本文于 2026-10-23 依据线上正在运行的泛微、蓝凌、致远、云之家 OA 待办连接器整理:连接参数、场景模板和当前生效的 APL 函数均做过脱敏,不包含客户域名、注册码、appId、secret、企业 EI 或人员信息。本文是线上配置的总结,暂未逐项经过真实 OA 系统联调校验。
线上配置会随 OA 版本、租户定制和接口升级变化。复制模板后,请以该 OA 的实际接口文档和一条真实数据的返回为准;如果 OA 侧升级了端点或状态位,优先按 OA 文档调整,不要照搬旧模板。