注意: 宜搭服务二开功能目前只是内测Alpha版阶段,功能不成熟,未在宜搭对外公开的功能列表中。正式上线时间待定。

权限

因为代码能力和权限都比较大,所以每个租户下,需要单独给自己的开发者分配权限。

入口

平台管理 -》 平台权限管理 -》服务端开发人员

限制

目前每个租户最多开放 5名开发者的名额
image.png

应用分发

对于应用分发之后的再次维护场景,代码权限需要企业管理员授予,每个企业开放3名具有代码权限的isv维护人员
image.png

代码能力

目前主要分为执行和校验两种类型的代码服务二开能力,同时校验是可以中断操作的

表单

表单校验和表单执行增加代码服务二开入口,如下图所示
image.png

image.png

image.png

流程

流程提交前校验,流程节点任务提交前以及任务和节点完成后事件
操作流程如下图所示:
image.png

计划作业

应用维度设置全局定时触发机制,在自定义起始时间和截止时间内设置触发间隔和频率
image.png
image.png

自定义接口

为了支持更多后台场景,提供自定义接口用于实现前后端打通。自定义接口不用绑定在表单或者流程回调事件中,可以直接被前端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 : 错误码;

DevOps

提供应用一键部署,部署查询,日志查询等功能
DevOps相关操作入口都放在”应用设置”里
image.png

部署管理

可以发起应用部署,查看部署记录(操作人、时间、描述、结果、耗时)
image.png

发起部署

点击按钮进入发起部署页面,在代码提交记录中显示未发布的代码提交记录,
image.png

服务绑定查询

查询服务二开代码类与页面绑定关系以及执行顺序
image.png

日志查询

根据时间段以及完整的类路径(非必填)查询日志,并且提供导出到本地功能。日志一次查询最多返回100条,查询最小间隔5秒
image.png

代码开发

所有自定义代码逻辑都是通过继承系统提供的抽象基类实现。不同的抽象基类表明了不同的触发点。

表单提交前校验 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可以看到

  1. package com.alibaba.user;
  2. import com.alibaba.tianshu.callback.base.AbstractFormBeforeSubmitValidityCheckBase;
  3. import com.alibaba.tianshu.callback.data.FormBeforeSubmitValidityCheckRequest;
  4. import com.alibaba.tianshu.callback.data.FormBeforeSubmitValidityCheckResponse;
  5. import com.alibaba.tianshu.exception.BusinessErrorException;
  6. import com.alibaba.tianshu.exception.BusinessWarnException;
  7. import org.slf4j.Logger;
  8. import org.slf4j.LoggerFactory;
  9. /**
  10. * 回调函数样例类 表单提交前校验
  11. * @OnCall:直接放到类上,用于标记该类为回调类
  12. * 在execute中实现逻辑
  13. */
  14. /**
  15. * 在OnCall注解中 填写服务id 服务名称 服务描述
  16. * 服务id需要在应用内唯一
  17. */
  18. //OnCall(id = "", name = "", description = "")
  19. public class ExampleOnFromCheck extends AbstractFormBeforeSubmitValidityCheckBase {
  20. /**
  21. * 日志对象
  22. */
  23. private static final Logger logger = LoggerFactory.getLogger(ExampleOnFromCheck.class);
  24. /**
  25. * 执行函数
  26. * 如果在函数中抛出异常 可以中断宜搭侧表单提交动作
  27. * @param formBeforeSubmitValidityCheckRequest
  28. * @return
  29. */
  30. @Override
  31. public FormBeforeSubmitValidityCheckResponse execute(
  32. FormBeforeSubmitValidityCheckRequest formBeforeSubmitValidityCheckRequest) {
  33. /**
  34. if (warn) {
  35. throw new BusinessWarnException("警告发生")
  36. }
  37. if (error) {
  38. throw new BusinessErrorException("错误发生")
  39. }
  40. */
  41. return null;
  42. }
  43. }

数据结构

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 data 表单上数据 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 approvalUserList 审批人

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,在代码中通过下诉方式注入

  1. @Autowired
  2. private ServiceSupply serviceSupply;

接口介绍

FormDataService
保存表单数据 ServiceResult saveFormData(SaveFormDataParam var1); 详见:
https://www.yuque.com/yida/help/ihxi9r 中的
saveFormData.json 描述
更新表单数据 ServiceResult updateFormData(UpdateFormDataParam var1); 详见:
https://www.yuque.com/yida/help/ihxi9r 中的
updateFormData.json 描述
删除表单数据 ServiceResult deleteFormData(DeleteFormDataParam var1); 详见:
https://www.yuque.com/yida/help/ihxi9r 中的
deleteFormData.json 描述
根据实例id获得表单数据 ServiceResult getFormDataById(GetFormDataByIdParam var1); 详见:
https://www.yuque.com/yida/help/ihxi9r 中的
getFormDataById.json 描述
根据表单上控件数据查询实例id列表 ServiceResult> searchFormDataIds(SearchFormDataParam var1); 详见:
https://www.yuque.com/yida/help/ihxi9r 中的
searchFormDataIds.json 描述
根据表单上的控件数据查询表单实例数据 ServiceResult> searchFormDatas(SearchFormDataParam var1); 详见:
https://www.yuque.com/yida/help/ihxi9r 中的
searchFormDatas.json 描述
OrgDeptService
根据部门id查询部门信息 ServiceResult queryDeptByDeptNo(QueryDeptByDeptNoParam param)
根据部门名称查询部门信息 ServiceResult> queryDeptByDeptName(QueryDeptByDeptNameParam param);
查询部门的子部门 ServiceResult> querySubDeptByDeptNo(QueryDeptByDeptNoParam param);
OrgUserService
根据用户id查询用户信息 ServiceResult queryUserInfoByUserId(QueryUserInfoByIDParam var1);
根据用户名词查询用户信息 ServiceResult> queryUserInfoByUserName(QueryUserInfoByNameParam var1);
查询部门内所有成员 ServiceResult> queryUserInfoByDeptNo(QueryDeptByDeptNoParam param);

注意事项

  • 所有接口参数都继承 BaseAppAuthParam类,在发起调用的时候需要set下面的值 | appType | 所操作的应用code
    如果是操作当前应用可以不传 | | —- | —- | | systemToken | 所操作应用对于的授权信息
    如果是操作当前应用可以不传 | | userId | 所操作应用中的用户Id
    如果没有特殊需求可以传入上下文中的当前操作人id |
  • 为了支持webIDE在线debug,宜搭后台有一个调用转发逻辑,如果当前操作者存在未释放的webIDE空间,会将流量转发到空间中,所以使用完webIDE请直接释放

image.png

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

image.png

  • 流程中绑定的服务二开,目前流程节点类型(nodeType)和流程动作(flowAction)这两个字段的值为0(未知)
  • 流程中服务二开对应基类的执行顺序:流程开始节点(AbstractFlowBeforeSubmitBase)->流程节点提交前校验(AbstractFlowTaskBeforeSubmitValidityCheckBase)->流程节点提交前(AbstractFlowTaskAfterSubmitBase)->流程节点结束(AbstractFlowNodeAfterCompletedBase)
  • 流程中绑定的服务二开,目前暂时不支持从流程上下文(flowContext)中获取审批人(approvalUserList)

样例代码

  1. package com.alibaba.user;
  2. import java.util.Date;
  3. import java.util.HashMap;
  4. import java.util.LinkedList;
  5. import java.util.List;
  6. import java.util.Map;
  7. import com.alibaba.fastjson.JSON;
  8. import com.alibaba.fastjson.JSONArray;
  9. import com.alibaba.fastjson.JSONObject;
  10. import com.alibaba.tianshu.annotation.OnCall;
  11. import com.alibaba.tianshu.callback.base.AbstractFormBeforeSubmitValidityCheckBase;
  12. import com.alibaba.tianshu.callback.data.FormBeforeSubmitValidityCheckRequest;
  13. import com.alibaba.tianshu.callback.data.FormBeforeSubmitValidityCheckResponse;
  14. import com.alibaba.tianshu.callback.data.model.FormData;
  15. import com.alibaba.tianshu.callback.data.model.UserData;
  16. import com.alibaba.tianshu.exception.BusinessWarnException;
  17. import com.alibaba.tianshu.supply.ServiceSupply;
  18. import com.alibaba.work.tianshu.api.common.param.ServiceResult;
  19. import com.alibaba.work.tianshu.api.form.param.GetFormDataByIdParam;
  20. import com.alibaba.work.tianshu.api.form.param.SaveFormDataParam;
  21. import com.alibaba.work.tianshu.api.form.param.SearchFormDataParam;
  22. import com.alibaba.work.tianshu.api.form.vo.FormInstanceVo;
  23. import com.alibaba.work.tianshu.api.model.dto.PageDto;
  24. import org.springframework.beans.factory.annotation.Autowired;
  25. import org.springframework.util.CollectionUtils;
  26. /**
  27. * 数迹计划作业逻辑
  28. */
  29. @OnCall(id = "SJ Job Schema", name = "数迹计划作业", description = "数迹计划作业")
  30. public class SchemaJobAfterFormSubmitSJ extends AbstractFormBeforeSubmitValidityCheckBase {
  31. @Autowired
  32. private ServiceSupply serviceSupply;
  33. @Override
  34. public FormBeforeSubmitValidityCheckResponse execute(FormBeforeSubmitValidityCheckRequest arg0) {
  35. // 根据输入的订单id查询对应的记录
  36. List<FormInstanceVo> formInstanceVos = findJob(arg0);
  37. if (CollectionUtils.isEmpty(formInstanceVos)) {
  38. throw new BusinessWarnException("表单数据没有找到");
  39. }
  40. // 找到信息后 开始执行业务逻辑
  41. StringBuilder sb = new StringBuilder();
  42. for (FormInstanceVo vo : formInstanceVos) {
  43. String jobGroup = vo.getFormData().get("selectField_Group").toString();
  44. String jobClass = vo.getFormData().get("textField_Class").toString();
  45. String time = vo.getFormData().get("dateField_k3411elp").toString().toString();
  46. Date date = new Date(Long.valueOf(time));
  47. sb.append("组别: ").append(jobGroup);
  48. sb.append(" 班次: ").append(jobClass);
  49. sb.append(" 时间: ").append(String.format("%tc%n", date));
  50. }
  51. // 将结果写入
  52. saveJob(arg0, sb.toString());
  53. return null;
  54. }
  55. private void saveJob(FormBeforeSubmitValidityCheckRequest arg0, String jobSchedule) {
  56. SaveFormDataParam saveFormData = new SaveFormDataParam();
  57. saveFormData.setUserId(arg0.getFormContext().getLoginUser().getUserId());
  58. saveFormData.setFormUuid("FORM-HC766RB1XKSAAUQ84ZT2OCZKUB513P5CR143K0");
  59. Map<String, Object> formData = new HashMap<>();
  60. formData.put("textField_k341rdyh", jobSchedule);
  61. formData.put("employeeField_k341rdyg", String.format("[\"%s\"]", arg0.getFormContext().getLoginUser().getUserId()));
  62. saveFormData.setFormDataJson(JSON.toJSONString(formData));
  63. ServiceResult<String> result = serviceSupply.getFormDataService().saveFormData(saveFormData);
  64. if (!result.isSuccess()) {
  65. throw new BusinessWarnException(result.getErrorMsg());
  66. }
  67. }
  68. private List<FormInstanceVo> findJob(FormBeforeSubmitValidityCheckRequest arg0) {
  69. UserData oper = arg0.getFormContext().getLoginUser();
  70. FormData formData = arg0.getFormDataAfter();
  71. Object orderId = formData.getData().get("textField_OrderId");
  72. if (orderId == null) {
  73. throw new BusinessWarnException("订单id不存在");
  74. }
  75. String orderIdStr = orderId.toString();
  76. System.out.println("orderId:" + orderIdStr);
  77. String searchField = getSearchText(orderIdStr);
  78. System.out.println("searchText:" + searchField);
  79. SearchFormDataParam searchFormDataParam = new SearchFormDataParam();
  80. searchFormDataParam.setUserId(oper.getUserId());
  81. searchFormDataParam.setFormUuid("FORM-HC766RB1FKSAV4O9Z4IPT30IXWBM36RC1143K0");
  82. searchFormDataParam.setSearchFieldJson(searchField);
  83. ServiceResult<PageDto<String>> result = serviceSupply.getFormDataService().searchFormDataIds(searchFormDataParam);
  84. if (!result.isSuccess()) {
  85. throw new BusinessWarnException(result.getErrorMsg());
  86. }
  87. List<String> instanIdList = result.getResult().getData();
  88. if (CollectionUtils.isEmpty(instanIdList)) {
  89. throw new BusinessWarnException("数据没有准备好 无法找到计划");
  90. }
  91. List<FormInstanceVo> formDataList = new LinkedList<>();
  92. for (String instanId : instanIdList) {
  93. GetFormDataByIdParam getFormDataByIdParam = new GetFormDataByIdParam();
  94. getFormDataByIdParam.setUserId(oper.getUserId());
  95. getFormDataByIdParam.setFormInstId(instanId);
  96. ServiceResult<FormInstanceVo> formResult = serviceSupply.getFormDataService().getFormDataById(getFormDataByIdParam);
  97. if (!formResult.isSuccess()) {
  98. throw new BusinessWarnException(formResult.getErrorMsg());
  99. }
  100. FormInstanceVo vo = formResult.getResult();
  101. System.out.println(JSON.toJSON(vo));
  102. JSONArray peopleIdObj = (JSONArray)vo.getFormData().get("employeeField_Oper_id");
  103. System.out.println(peopleIdObj);
  104. if (oper.getUserId().equals(peopleIdObj.get(0))) {
  105. formDataList.add(formResult.getResult());
  106. }
  107. }
  108. return formDataList;
  109. }
  110. private String getSearchText(String orderIdStr) {
  111. Map<String, Object> searchMap = new HashMap<>();
  112. searchMap.put("textField_OrderId", orderIdStr);
  113. return JSON.toJSONString(searchMap);
  114. }
  115. }