注意: 宜搭服务二开功能目前只是内测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
*/
@Override
public 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,在代码中通过下诉方式注入
@Autowired
private 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 {
@Autowired
private ServiceSupply serviceSupply;
@Override
public 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);
}
}