注意: 宜搭服务二开功能目前只是内测Alpha版阶段,功能不成熟,未在宜搭对外公开的功能列表中。正式上线时间待定。
权限
因为代码能力和权限都比较大,所以每个租户下,需要单独给自己的开发者分配权限。
入口
限制
应用分发
对于应用分发之后的再次维护场景,代码权限需要企业管理员授予,每个企业开放3名具有代码权限的isv维护人员
代码能力
目前主要分为执行和校验两种类型的代码服务二开能力,同时校验是可以中断操作的
表单
表单校验和表单执行增加代码服务二开入口,如下图所示
流程
流程提交前校验,流程节点任务提交前以及任务和节点完成后事件
操作流程如下图所示:
计划作业
应用维度设置全局定时触发机制,在自定义起始时间和截止时间内设置触发间隔和频率
自定义接口
为了支持更多后台场景,提供自定义接口用于实现前后端打通。自定义接口不用绑定在表单或者流程回调事件中,可以直接被前端js代码调用,从而实现灵活的联动模式。自定义接口入参和出参都是JSON对象,可以方便扩展。
| 接口 | https://s-api.alibaba-inc.com/yida_vpc/app/executeCustomApi.json |
接口通过https提供,只支持post方式
| | —- | —- | —- | | 参数 | | | | appType | 应用ID | 必填 | | systemToken | 应用秘钥 | 必填 | | userId | 登录人的员工工号 | 必填 | | language | 语言环境 | 非必填 可选(zh_CN/en_US) | | serviceId | 自定义服务id | 必填 | | data | 参数 | 非必填 (格式:JSONObject) | | | | |
- 返回值:
- result : JSONObject;
- success : 请求是否成功;
- errorMsg : 错误信息;
- errorCode : 错误码;
- result : JSONObject;
DevOps
提供应用一键部署,部署查询,日志查询等功能
DevOps相关操作入口都放在”应用设置”里
部署管理
可以发起应用部署,查看部署记录(操作人、时间、描述、结果、耗时)
发起部署
点击按钮进入发起部署页面,在代码提交记录中显示未发布的代码提交记录,
服务绑定查询
查询服务二开代码类与页面绑定关系以及执行顺序
日志查询
根据时间段以及完整的类路径(非必填)查询日志,并且提供导出到本地功能。日志一次查询最多返回100条,查询最小间隔5秒
代码开发
所有自定义代码逻辑都是通过继承系统提供的抽象基类实现。不同的抽象基类表明了不同的触发点。
| 表单提交前校验 | com.alibaba.tianshu.callback.base.AbstractFormBeforeSubmitValidityCheckBase |
|---|---|
| 表单提交后执行 | com.alibaba.tianshu.callback.base.AbstractFormAfterSubmitBase |
| 流程提交前校验 | com.alibaba.tianshu.callback.base.AbstractFlowBeforeSubmitBase |
| 任务提交前校验 | com.alibaba.tianshu.callback.base.AbstractFlowTaskBeforeSubmitValidityCheckBase |
| 任务完成后执行 | com.alibaba.tianshu.callback.base.AbstractFlowTaskAfterSubmitBase |
| 节点完成后执行 | com.alibaba.tianshu.callback.base.AbstractFlowNodeAfterCompletedBase |
| 定时触发 | com.alibaba.tianshu.callback.base.AbstractCloudCodeTimerJobBase |
| 自定义接口 | com.alibaba.tianshu.callback.base.AbstractCustomApiBase |
除了继承抽象类之外,用户还需要在自定义继承类上添加注解
OnCall(id = “”, name = “”, description = “”)
id: 服务id,需要应用内全局唯一
name: 服务名称,会在触发点服务选择下拉框中展示
description: 服务描述
样例
样例代码如下所示,目前提供6种类型的样例代码供用户参考,进入WebIDE可以看到
package com.alibaba.user;import com.alibaba.tianshu.callback.base.AbstractFormBeforeSubmitValidityCheckBase;import com.alibaba.tianshu.callback.data.FormBeforeSubmitValidityCheckRequest;import com.alibaba.tianshu.callback.data.FormBeforeSubmitValidityCheckResponse;import com.alibaba.tianshu.exception.BusinessErrorException;import com.alibaba.tianshu.exception.BusinessWarnException;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/*** 回调函数样例类 表单提交前校验* @OnCall:直接放到类上,用于标记该类为回调类* 在execute中实现逻辑*//*** 在OnCall注解中 填写服务id 服务名称 服务描述* 服务id需要在应用内唯一*///OnCall(id = "", name = "", description = "")public class ExampleOnFromCheck extends AbstractFormBeforeSubmitValidityCheckBase {/*** 日志对象*/private static final Logger logger = LoggerFactory.getLogger(ExampleOnFromCheck.class);/*** 执行函数* 如果在函数中抛出异常 可以中断宜搭侧表单提交动作* @param formBeforeSubmitValidityCheckRequest* @return*/@Overridepublic FormBeforeSubmitValidityCheckResponse execute(FormBeforeSubmitValidityCheckRequest formBeforeSubmitValidityCheckRequest) {/**if (warn) {throw new BusinessWarnException("警告发生")}if (error) {throw new BusinessErrorException("错误发生")}*/return null;}}
数据结构
UserData
| 完整类路径 | com.alibaba.tianshu.callback.data.model.UserData |
|---|---|
| 说明 | 用户信息对象 |
| 成员 | |
| String userId; | 用户Id |
FormContext
| 完整类路径 | com.alibaba.tianshu.callback.data.model.FormContext |
|---|---|
| 说明 | 表单上下文 |
| 成员 | |
| String formUuid | 当前表单id |
| String formInstId | 当前表单实例id |
| UserData loginUser | 当前登录人 |
FormAction
| 完整类路径 | com.alibaba.tianshu.callback.data.model.FormAction |
|---|---|
| 说明 | 表单动作 |
| 成员 | |
| Integer action | 动作 public static final Integer FORM_ACTION_NEW = 1; // 表单新增 public static final Integer FORM_ACTION_EDIT = 2; // 表单编辑 public static final Integer FORM_ACTION_DELETE = 3; // 表单删除 |
FormData
| 完整类路径 | com.alibaba.tianshu.callback.data.model.FormData |
|---|---|
| 说明 | 表单数据 |
| 成员 | |
| Map |
表单上数据 key:控件id data:控件值 数据格式详细说明:https://www.yuque.com/yida/help/ihxi9r#b3tmyo 附录1: 表单数据格式说明 |
FlowContext
| 完整类路径 | com.alibaba.tianshu.callback.data.model.FlowContext |
|---|---|
| 说明 | 流程上下文 |
| 成员 | |
| String formUuid | 当前表单id |
| String formInstId | 当前表单实例id |
| String nodeId | 节点id |
| Integer nodeType | 节点类型 public static final Integer NODE_TYPE_MANUAL = 1; // 人工节点 public static final Integer NODE_TYPE_AUTOMATIC = 2; // 自动节点 public static final Integer NODE_TYPE_START = 3; // 开始节点 |
| UserData submitUser | 提交人 |
| List |
审批人 |
FlowAction
| 完整类路径 | com.alibaba.tianshu.callback.data.model.FlowAction |
|---|---|
| 说明 | 节点动作 |
| 成员 | |
| Integer action | 动作 public static final Integer FLOW_ACTION_AGREE = 1; // 同意 public static final Integer FLOW_ACTION_REFUSE = 2; // 拒绝 public static final Integer FLOW_ACTION_SAVE = 3; // 保存 public static final Integer FLOW_ACTION_BACK = 4; // 退回 |
说明1
一些入参对象会存在一下两个对象
private FormData formDataBefore;
private FormData formDataAfter;
before表示变更前数据,after表示变更后数据。
表单编辑场景,before是变更前的数据,after是页面上变更后的数据。
表单新增场景,before是Null,after是页面上变更后的数据。
表单删除场景,before是需要删除的页面数据,after是Null
接口提供
平台提供的所有接口统一收敛在com.alibaba.tianshu.supply.ServiceSupply中
ServiceSupply本身是一个bean,在代码中通过下诉方式注入
@Autowiredprivate ServiceSupply serviceSupply;
接口介绍
| FormDataService | ||
|---|---|---|
| 保存表单数据 | ServiceResult |
详见: https://www.yuque.com/yida/help/ihxi9r 中的 saveFormData.json 描述 |
| 更新表单数据 | ServiceResult |
详见: https://www.yuque.com/yida/help/ihxi9r 中的 updateFormData.json 描述 |
| 删除表单数据 | ServiceResult |
详见: https://www.yuque.com/yida/help/ihxi9r 中的 deleteFormData.json 描述 |
| 根据实例id获得表单数据 | ServiceResult |
详见: https://www.yuque.com/yida/help/ihxi9r 中的 getFormDataById.json 描述 |
| 根据表单上控件数据查询实例id列表 | ServiceResult |
详见: https://www.yuque.com/yida/help/ihxi9r 中的 searchFormDataIds.json 描述 |
| 根据表单上的控件数据查询表单实例数据 | ServiceResult |
详见: https://www.yuque.com/yida/help/ihxi9r 中的 searchFormDatas.json 描述 |
| OrgDeptService | ||
| 根据部门id查询部门信息 | ServiceResult |
|
| 根据部门名称查询部门信息 | ServiceResult
|
|
| 查询部门的子部门 | ServiceResult
|
|
| OrgUserService | ||
| 根据用户id查询用户信息 | ServiceResult |
|
| 根据用户名词查询用户信息 | ServiceResult
|
|
| 查询部门内所有成员 | ServiceResult
|
注意事项
- 所有接口参数都继承 BaseAppAuthParam类,在发起调用的时候需要set下面的值
| appType | 所操作的应用code
如果是操作当前应用可以不传 | | —- | —- | | systemToken | 所操作应用对于的授权信息
如果是操作当前应用可以不传 | | userId | 所操作应用中的用户Id
如果没有特殊需求可以传入上下文中的当前操作人id |
- 为了支持webIDE在线debug,宜搭后台有一个调用转发逻辑,如果当前操作者存在未释放的webIDE空间,会将流量转发到空间中,所以使用完webIDE请直接释放

- webIDE新建之后,相关jar包以及语法的索引正在建立,webIDE最下面的进度中有显示进度,等该信息完成结束后,编辑器可以正常工作提示内容推荐

- 流程中绑定的服务二开,目前流程节点类型(nodeType)和流程动作(flowAction)这两个字段的值为0(未知)
- 流程中服务二开对应基类的执行顺序:流程开始节点(AbstractFlowBeforeSubmitBase)->流程节点提交前校验(AbstractFlowTaskBeforeSubmitValidityCheckBase)->流程节点提交前(AbstractFlowTaskAfterSubmitBase)->流程节点结束(AbstractFlowNodeAfterCompletedBase)
- 流程中绑定的服务二开,目前暂时不支持从流程上下文(flowContext)中获取审批人(approvalUserList)
样例代码
package com.alibaba.user;import java.util.Date;import java.util.HashMap;import java.util.LinkedList;import java.util.List;import java.util.Map;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONArray;import com.alibaba.fastjson.JSONObject;import com.alibaba.tianshu.annotation.OnCall;import com.alibaba.tianshu.callback.base.AbstractFormBeforeSubmitValidityCheckBase;import com.alibaba.tianshu.callback.data.FormBeforeSubmitValidityCheckRequest;import com.alibaba.tianshu.callback.data.FormBeforeSubmitValidityCheckResponse;import com.alibaba.tianshu.callback.data.model.FormData;import com.alibaba.tianshu.callback.data.model.UserData;import com.alibaba.tianshu.exception.BusinessWarnException;import com.alibaba.tianshu.supply.ServiceSupply;import com.alibaba.work.tianshu.api.common.param.ServiceResult;import com.alibaba.work.tianshu.api.form.param.GetFormDataByIdParam;import com.alibaba.work.tianshu.api.form.param.SaveFormDataParam;import com.alibaba.work.tianshu.api.form.param.SearchFormDataParam;import com.alibaba.work.tianshu.api.form.vo.FormInstanceVo;import com.alibaba.work.tianshu.api.model.dto.PageDto;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.util.CollectionUtils;/*** 数迹计划作业逻辑*/@OnCall(id = "SJ Job Schema", name = "数迹计划作业", description = "数迹计划作业")public class SchemaJobAfterFormSubmitSJ extends AbstractFormBeforeSubmitValidityCheckBase {@Autowiredprivate ServiceSupply serviceSupply;@Overridepublic FormBeforeSubmitValidityCheckResponse execute(FormBeforeSubmitValidityCheckRequest arg0) {// 根据输入的订单id查询对应的记录List<FormInstanceVo> formInstanceVos = findJob(arg0);if (CollectionUtils.isEmpty(formInstanceVos)) {throw new BusinessWarnException("表单数据没有找到");}// 找到信息后 开始执行业务逻辑StringBuilder sb = new StringBuilder();for (FormInstanceVo vo : formInstanceVos) {String jobGroup = vo.getFormData().get("selectField_Group").toString();String jobClass = vo.getFormData().get("textField_Class").toString();String time = vo.getFormData().get("dateField_k3411elp").toString().toString();Date date = new Date(Long.valueOf(time));sb.append("组别: ").append(jobGroup);sb.append(" 班次: ").append(jobClass);sb.append(" 时间: ").append(String.format("%tc%n", date));}// 将结果写入saveJob(arg0, sb.toString());return null;}private void saveJob(FormBeforeSubmitValidityCheckRequest arg0, String jobSchedule) {SaveFormDataParam saveFormData = new SaveFormDataParam();saveFormData.setUserId(arg0.getFormContext().getLoginUser().getUserId());saveFormData.setFormUuid("FORM-HC766RB1XKSAAUQ84ZT2OCZKUB513P5CR143K0");Map<String, Object> formData = new HashMap<>();formData.put("textField_k341rdyh", jobSchedule);formData.put("employeeField_k341rdyg", String.format("[\"%s\"]", arg0.getFormContext().getLoginUser().getUserId()));saveFormData.setFormDataJson(JSON.toJSONString(formData));ServiceResult<String> result = serviceSupply.getFormDataService().saveFormData(saveFormData);if (!result.isSuccess()) {throw new BusinessWarnException(result.getErrorMsg());}}private List<FormInstanceVo> findJob(FormBeforeSubmitValidityCheckRequest arg0) {UserData oper = arg0.getFormContext().getLoginUser();FormData formData = arg0.getFormDataAfter();Object orderId = formData.getData().get("textField_OrderId");if (orderId == null) {throw new BusinessWarnException("订单id不存在");}String orderIdStr = orderId.toString();System.out.println("orderId:" + orderIdStr);String searchField = getSearchText(orderIdStr);System.out.println("searchText:" + searchField);SearchFormDataParam searchFormDataParam = new SearchFormDataParam();searchFormDataParam.setUserId(oper.getUserId());searchFormDataParam.setFormUuid("FORM-HC766RB1FKSAV4O9Z4IPT30IXWBM36RC1143K0");searchFormDataParam.setSearchFieldJson(searchField);ServiceResult<PageDto<String>> result = serviceSupply.getFormDataService().searchFormDataIds(searchFormDataParam);if (!result.isSuccess()) {throw new BusinessWarnException(result.getErrorMsg());}List<String> instanIdList = result.getResult().getData();if (CollectionUtils.isEmpty(instanIdList)) {throw new BusinessWarnException("数据没有准备好 无法找到计划");}List<FormInstanceVo> formDataList = new LinkedList<>();for (String instanId : instanIdList) {GetFormDataByIdParam getFormDataByIdParam = new GetFormDataByIdParam();getFormDataByIdParam.setUserId(oper.getUserId());getFormDataByIdParam.setFormInstId(instanId);ServiceResult<FormInstanceVo> formResult = serviceSupply.getFormDataService().getFormDataById(getFormDataByIdParam);if (!formResult.isSuccess()) {throw new BusinessWarnException(formResult.getErrorMsg());}FormInstanceVo vo = formResult.getResult();System.out.println(JSON.toJSON(vo));JSONArray peopleIdObj = (JSONArray)vo.getFormData().get("employeeField_Oper_id");System.out.println(peopleIdObj);if (oper.getUserId().equals(peopleIdObj.get(0))) {formDataList.add(formResult.getResult());}}return formDataList;}private String getSearchText(String orderIdStr) {Map<String, Object> searchMap = new HashMap<>();searchMap.put("textField_OrderId", orderIdStr);return JSON.toJSONString(searchMap);}}

