update:将飞书对接代码移动到对接模块中

This commit is contained in:
sunzheng
2024-07-25 10:13:10 +08:00
parent 0152bc3bb1
commit bfc1b46557
15 changed files with 34 additions and 34 deletions
@@ -0,0 +1,92 @@
package com.jero.modules.docking.feishu.controller;
import com.alibaba.fastjson.JSONObject;
import com.jero.common.api.vo.Result;
import com.jero.modules.docking.feishu.dto.BotMessageDto;
import com.jero.modules.docking.feishu.dto.InstanceDto;
import com.jero.modules.docking.feishu.service.IFeiShuDockingService;
import com.jero.modules.docking.feishu.vo.definition.ApprovalsDefinitionVo;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* <p>
* 飞书对接
* </p>
*/
@Api(tags = "飞书对接")
@RestController
@RequestMapping("/feishu")
@Slf4j
public class FeiShuDockingController {
@Resource
private IFeiShuDockingService feiShuDockingService;
/**
* 获取token
* @return
*/
@GetMapping("/getAccessToken")
public String getAccessToken() {
return feiShuDockingService.getAccessToken();
}
/**
* 创建三方审批定义
* @return
*/
@PostMapping("/createApprovalDefinition")
public String createApprovalDefinition() {
return feiShuDockingService.createApprovalDefinition();
}
/**
* 获取指定三方审批定义
* @param approvalDefinitionCode
* @return
*/
@GetMapping("/getApprovalDefinition")
public ApprovalsDefinitionVo getApprovalDefinition(String approvalDefinitionCode) {
return feiShuDockingService.getApprovalDefinition(approvalDefinitionCode);
}
/**
* 根据手机号获取用户的user_id
* @param phones
* @return
*/
@PostMapping("/getUserId")
public String getUserId(String phones) {
return feiShuDockingService.getUserId(phones);
}
/**
* 同步三方审批实例
* @return
*/
@PostMapping("/synchronizeApprovalInstance")
public void synchronizeApprovalInstance(@RequestBody InstanceDto instanceDto) {
feiShuDockingService.synchronizeApprovalInstance(instanceDto);
}
/**
* 使用Bot发送富文本消息
* @return
*/
@PostMapping("/sendHrefMessageByBot")
public void sendHrefMessageByBot(@RequestBody BotMessageDto botMessageDto) {
feiShuDockingService.sendHrefMessageByBot(botMessageDto);
}
/**
* 根据预授权码获取用户信息
*/
@PostMapping("/getUserInfoByCode")
public Result<JSONObject> getUserInfoByCode(@RequestBody JSONObject codeObject) {
return feiShuDockingService.getUserInfoByCode(codeObject);
}
}
@@ -0,0 +1,27 @@
package com.jero.modules.docking.feishu.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class BotMessageDto {
@ApiModelProperty("消息接收者的id,可以为多个,逗号拼接")
private String receiveIds;
@ApiModelProperty("超链接的文字")
private String hrefText;
@ApiModelProperty("超链接前面的普通文字")
private String textBeforeHref;
@ApiModelProperty("超链接后面的普通文字")
private String textAfterHref;
@ApiModelProperty("消息的标题")
private String title;
@ApiModelProperty("超链接的跳转地址")
private String hrefUrl;
}
@@ -0,0 +1,68 @@
package com.jero.modules.docking.feishu.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
public class InstanceDto {
@ApiModelProperty("发起人的id")
private String promoterUserId;
@ApiModelProperty("审核人的ids,可能为多个,逗号分割")
private String reviewedIds;
@ApiModelProperty("标题")
private String title;
@ApiModelProperty("审核实例的id")
private String processInstanceId;
@ApiModelProperty("审核任务的ids,有几个审核人就对应几个任务id,逗号分割")
private String taskIds;
/**
* 审批实例状态
* 示例值:"PENDING"
* PENDING 审批中
* APPROVED: 审批流程结束,结果为同意
* REJECTED: 审批流程结束,结果为拒绝
* CANCELED 审批发起人撤回
* DELETED 审批被删除
* HIDDEN: 状态隐藏(不显示状态)
* TERMINATED:审批终止
*/
@ApiModelProperty("审核实例的状态")
private String instanceStatus;
@ApiModelProperty("审核实例的开始时间")
private Date instanceStartTime;
/**
* 任务状态
* 示例值:"PENDING"
* 可选值有:
* PENDING:待审批
* APPROVED:任务同意
* REJECTED:任务拒绝
* TRANSFERRED:任务转交
* DONE:任务通过但审批人未操作;审批人看不到这个任务, 若想要看到, 可以通过抄送该人.
*/
@ApiModelProperty("审核任务的状态,有几个审核人对应几个审核任务的状态,逗号分割")
private String taskStatus;
@ApiModelProperty("发起人的PC跳转地址")
private String promoterPcLink;
@ApiModelProperty("发起人的移动端跳转地址")
private String promoterMobileLink;
@ApiModelProperty("审核人的PC端跳转地址,有几个审核人对应几条链接,逗号分割")
private String reviewPcLink;
@ApiModelProperty("审核人的移动端跳转地址,有几个审核人对应几条链接,逗号分割")
private String reviewMobileLink;
}
@@ -0,0 +1,54 @@
package com.jero.modules.docking.feishu.service;
import com.alibaba.fastjson.JSONObject;
import com.jero.common.api.vo.Result;
import com.jero.modules.docking.feishu.dto.BotMessageDto;
import com.jero.modules.docking.feishu.dto.InstanceDto;
import com.jero.modules.docking.feishu.vo.definition.ApprovalsDefinitionVo;
public interface IFeiShuDockingService {
/**
* 获取飞书应用的token
* @return
*/
String getAccessToken();
/**
* 创建三方审批的定义
* @return
*/
String createApprovalDefinition();
/**
* 查看指定三方审批定义
* @return
*/
ApprovalsDefinitionVo getApprovalDefinition(String approvalDefinitionCode);
/**
* 获取用户id
* @return
*/
String getUserId(String phones);
/**
* 同步三方审批实例
* @return
*/
void synchronizeApprovalInstance(InstanceDto instanceDto);
/**
* 使用机器人发送消息
* @param botMessageDto
* @return
*/
void sendHrefMessageByBot(BotMessageDto botMessageDto);
/**
* 根据预授权码获取用户信息
* @param codeObject
* @return
*/
Result<JSONObject> getUserInfoByCode(JSONObject codeObject);
}
@@ -0,0 +1,604 @@
package com.jero.modules.docking.feishu.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jero.common.api.CommonAPI;
import com.jero.common.api.vo.Result;
import com.jero.common.constant.CommonConstant;
import com.jero.common.exception.JeroBootException;
import com.jero.common.system.util.JwtUtil;
import com.jero.common.system.vo.SysDictItemCore;
import com.jero.common.util.RedisUtil;
import com.jero.common.util.oConvertUtils;
import com.jero.modules.docking.feishu.dto.BotMessageDto;
import com.jero.modules.docking.feishu.dto.InstanceDto;
import com.jero.modules.docking.feishu.service.IFeiShuDockingService;
import com.jero.modules.docking.feishu.vo.bot.BotMessageVo;
import com.jero.modules.docking.feishu.vo.definition.*;
import com.jero.modules.docking.feishu.vo.instance.ApprovalsInstanceVo;
import com.jero.modules.docking.feishu.vo.instance.InstanceLink;
import com.jero.modules.docking.feishu.vo.instance.InstanceTask;
import com.jero.modules.system.entity.*;
import com.jero.modules.system.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
@Service
@Slf4j
public class FeiShuDockingServiceImpl implements IFeiShuDockingService {
@Resource
private RestTemplate restTemplate;
@Resource
private Environment env;
@Resource
private ISysUserService sysUserService;
@Autowired
private RedisUtil redisUtil;
@Autowired
private ISysDepartService sysDepartService;
@Autowired
private ISysTenantService sysTenantService;
@Autowired
private ISysDictService sysDictService;
@Resource
private ISysUserRoleService sysUserRoleService;
@Resource
private ISysRoleService sysRoleService;
@Resource
private CommonAPI commonAPI;
@Value("${feishu.app_id}")
private String appId;
@Value("${feishu.app_secret}")
private String appSecret;
@Value("${feishu.managersIds}")
private String managersIds;
@Value("${feishu.approvalName}")
private String approvalName;
private static final String USER_INFO = "userInfo";
@Override
public String getAccessToken() {
String url = env.getProperty("feishu.get_token");
assert url != null;
JSONObject jsonObject = new JSONObject();
jsonObject.put("app_id", appId);
jsonObject.put("app_secret", appSecret);
String res = restTemplate.postForObject(url, jsonObject, String.class);
JSONObject result = JSON.parseObject(res);
String code = result.getString("code");
if ("0".equals(code)) {
return result.getString("tenant_access_token");
} else {
throw new JeroBootException("获取飞书token失败");
}
}
@Override
public String createApprovalDefinition() {
String url = env.getProperty("feishu.create_approval_definition");
assert url != null;
// 获取token
String accessToken = getAccessToken();
// 构建对象
ApprovalsDefinitionVo approvalsDefinitionVo = buildDefinitionVo();
// 设置请求头
HttpHeaders headers = createHeaders(accessToken);
// 构建包含查询参数的 URL
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url)
.queryParam("department_id_type", "open_department_id")
.queryParam("user_id_type", "user_id");
// 创建 HttpEntity
HttpEntity<ApprovalsDefinitionVo> entity = new HttpEntity<>(approvalsDefinitionVo, headers);
// 发送请求
ResponseEntity<String> response = restTemplate.exchange(builder.toUriString(), HttpMethod.POST, entity, String.class);
// 处理响应
JSONObject jsonResponse = JSON.parseObject(response.getBody());
if ("0".equals((String.valueOf(jsonResponse.get("code"))))) {
JSONObject data = (JSONObject) jsonResponse.get("data");
return data.getString("approval_code");
} else {
log.error(jsonResponse.get("msg").toString());
throw new JeroBootException("创建三方审批定义失败");
}
}
// 构建创建审批定义的入参对象
public ApprovalsDefinitionVo buildDefinitionVo() {
ApprovalsDefinitionVo approvalsDefinitionVo = new ApprovalsDefinitionVo();
// 构建审批定义vo
approvalsDefinitionVo.setApproval_name("@i18n@1");
approvalsDefinitionVo.setApproval_code("permission_test3");
approvalsDefinitionVo.setGroup_code("work_group");
approvalsDefinitionVo.setGroup_name("@i18n@2");
// 构建三方审批相关对象.
DefinitionExternal definitionExternal = new DefinitionExternal();
definitionExternal.setCreate_link_pc("");
definitionExternal.setCreate_link_mobile("");
definitionExternal.setSupport_pc(true);
definitionExternal.setSupport_mobile(true);
definitionExternal.setSupport_batch_read(true);
definitionExternal.setEnable_mark_readed(true);
// 构建可见人列表对象
DefinitionViewers definitionViewers = new DefinitionViewers();
definitionViewers.setViewer_type("NONE");
List<DefinitionViewers> viewers = new ArrayList<>();
viewers.add(definitionViewers);
// 构建语言文案
List<DefinitionTexts> texts = new ArrayList<>();
texts.add(new DefinitionTexts("@i18n@1",approvalName));
texts.add(new DefinitionTexts("@i18n@2",approvalName));
texts.add(new DefinitionTexts("@i18n@3","飞书"));
// 构建国际化文案对象
DefinitionI18nResources definitionI18nResources = new DefinitionI18nResources();
definitionI18nResources.setLocale("zh-CN");
definitionI18nResources.setIs_default(true);
definitionI18nResources.setTexts(texts);
List<DefinitionI18nResources> i18n_resources = new ArrayList<>();
i18n_resources.add(definitionI18nResources);
approvalsDefinitionVo.setExternal(definitionExternal);
approvalsDefinitionVo.setViewers(viewers);
approvalsDefinitionVo.setI18n_resources(i18n_resources);
List<String> mangers = Arrays.asList(managersIds.split(","));
String[] mangersString = mangers.toArray(new String[0]);
approvalsDefinitionVo.setManagers(mangersString);
return approvalsDefinitionVo;
}
@Override
public ApprovalsDefinitionVo getApprovalDefinition(String approvalDefinitionCode) {
String url = env.getProperty("feishu.get_approval_definition");
assert url != null;
// 获取token
String accessToken = getAccessToken();
// 拼接URL
String fullUrl = url + "/" + approvalDefinitionCode;
// 设置请求头
HttpHeaders headers = createHeaders(accessToken);
// 创建 HttpEntity
HttpEntity<String> entity = new HttpEntity<>(headers);
// 发送请求
ResponseEntity<String> response = restTemplate.exchange(fullUrl, HttpMethod.GET, entity, String.class);
JSONObject jsonResponse = JSON.parseObject(response.getBody());
if ("0".equals((String.valueOf(jsonResponse.get("code"))))) {
JSONObject dataJson = jsonResponse.getJSONObject("data");
return JSON.toJavaObject(dataJson, ApprovalsDefinitionVo.class);
} else {
log.error(jsonResponse.get("msg").toString());
throw new JeroBootException("查看三方审批定义失败");
}
}
@Override
public String getUserId(String phones) {
try {
String url = env.getProperty("feishu.get_user_id");
assert url != null;
// 获取token
String accessToken = getAccessToken();
// 构建请求对象
JSONObject jsonObject = new JSONObject();
List<String> phonesList = Arrays.asList(phones.split(","));
String[] phonesString = phonesList.toArray(new String[0]);
jsonObject.put("mobiles", phonesString);
// 设置请求头
HttpHeaders headers = createHeaders(accessToken);
// 构建包含查询参数的 URL
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url)
.queryParam("user_id_type", "user_id");
// 创建 HttpEntity
HttpEntity<JSONObject> entity = new HttpEntity<>(jsonObject, headers);
// 发送请求
ResponseEntity<String> response = restTemplate.exchange(builder.toUriString(), HttpMethod.POST, entity, String.class);
// 处理响应
JSONObject jsonResponse = JSON.parseObject(response.getBody());
if ("0".equals((String.valueOf(jsonResponse.get("code"))))) {
JSONObject data = jsonResponse.getJSONObject("data");
JSONArray userList = data.getJSONArray("user_list");
// 将 JSONArray 转换为 List<JSONObject>
List<JSONObject> userListObjects = userList.toJavaList(JSONObject.class);
return userListObjects.get(0).getString("user_id");
} else {
log.error("获取飞书用户id失败" + jsonResponse.get("msg").toString());
return "";
}
} catch (Exception e) {
log.error(e.getMessage());
}
return "";
}
/**
* 同步三方审批实例
*
* @return
*/
@Override
public void synchronizeApprovalInstance(InstanceDto instanceDto) {
String url = env.getProperty("feishu.synchronize_approval_instance");
assert url != null;
try {
// 获取token
String accessToken = getAccessToken();
// 构建对象
ApprovalsInstanceVo approvalsInstanceVo = buildApprovalsInstanceVo(instanceDto);
// 设置请求头
HttpHeaders headers = createHeaders(accessToken);
// 创建 HttpEntity
HttpEntity<ApprovalsInstanceVo> entity = new HttpEntity<>(approvalsInstanceVo, headers);
restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
}catch (Exception e) {
log.error("同步三方审批实例异常" + e.getMessage());
}
}
private ApprovalsInstanceVo buildApprovalsInstanceVo(InstanceDto instanceDto) {
// 转换userId为飞书Id
// 发起人的id
String promoterUserId = instanceDto.getPromoterUserId();
if (StringUtils.isNoneBlank(promoterUserId)) {
SysUser promoterUser = sysUserService.getById(promoterUserId);
promoterUserId = promoterUser.getFeishuId();
}
// 审核人的id(可能为多个),查出对应的飞书id
String reviewedIds = instanceDto.getReviewedIds();
List<String> newReviewedIds = new ArrayList<>();
if (StringUtils.isNotBlank(reviewedIds)) {
List<String> reviewedIdList = Arrays.asList(reviewedIds.split(","));
for (String reviewedId : reviewedIdList) {
SysUser reviewUser = sysUserService.getById(reviewedId);
if (!Objects.isNull(reviewUser) && StringUtils.isNotBlank(reviewUser.getFeishuId())) {
newReviewedIds.add(reviewUser.getFeishuId());
}
}
}
// 任务id
String taskIds = instanceDto.getTaskIds();
List<String> taskIdList = new ArrayList<>();
if (StringUtils.isNotBlank(taskIds)) {
taskIdList = Arrays.asList(instanceDto.getTaskIds().split(","));
}
// 任务状态
String taskStatus = instanceDto.getTaskStatus();
List<String> taskStatusList = new ArrayList<>();
if (StringUtils.isNotBlank(taskIds)) {
taskStatusList = Arrays.asList(taskStatus.split(","));
}
// 替换跳转的地址为单点登录的地址
String url = env.getProperty("feishu.sign_login_in");
assert url != null;
// 审核人对应的 pc 跳转地址
String reviewPcLink = instanceDto.getReviewPcLink();
List<String> reviewPcLinkList = new ArrayList<>();
if (StringUtils.isNotBlank(reviewPcLink)) {
reviewPcLinkList = Arrays.asList(reviewPcLink.split(","));
reviewPcLinkList = reviewPcLinkList.stream()
.map(link -> url.replace("{REDIRECT_URI}", link))
.collect(Collectors.toList());
}
// 审核人对应的 mobile 跳转地址
String reviewMobileLink = instanceDto.getReviewMobileLink();
List<String> reviewMobileLinkList = new ArrayList<>();
if (StringUtils.isNotBlank(reviewMobileLink)) {
reviewMobileLinkList = Arrays.asList(reviewMobileLink.split(","));
reviewMobileLinkList = reviewMobileLinkList.stream()
.map(link -> url.replace("{REDIRECT_URI}", link))
.collect(Collectors.toList());
}
// 发起人对应的pc的跳转地址
String promoterPcLink = instanceDto.getPromoterPcLink();
if (StringUtils.isNotBlank(promoterPcLink)) {
promoterPcLink = url.replace("{REDIRECT_URI}", promoterPcLink);
}
// 发起人移动端的跳转地址
String promoterMobileLink = instanceDto.getPromoterMobileLink();
if (StringUtils.isNotBlank(promoterMobileLink)) {
promoterMobileLink = url.replace("{REDIRECT_URI}", promoterMobileLink);
}
// 审批定义的code
String approvalDefinition = createApprovalDefinition();
// 发起人相关的信息
ApprovalsInstanceVo approvalsInstanceVo = new ApprovalsInstanceVo();
approvalsInstanceVo.setApproval_code(approvalDefinition);
approvalsInstanceVo.setStatus(instanceDto.getInstanceStatus());
approvalsInstanceVo.setInstance_id(instanceDto.getProcessInstanceId());
InstanceLink instanceLink = new InstanceLink();
instanceLink.setPc_link(promoterPcLink);
instanceLink.setMobile_link(promoterMobileLink);
approvalsInstanceVo.setLinks(instanceLink);
approvalsInstanceVo.setTitle("@i18n@1");
approvalsInstanceVo.setUser_id(promoterUserId); // 审批发起人的user_id
approvalsInstanceVo.setStart_time(String.valueOf(instanceDto.getInstanceStartTime().getTime()));
approvalsInstanceVo.setEnd_time("0");
approvalsInstanceVo.setUpdate_time(String.valueOf(System.currentTimeMillis()));
approvalsInstanceVo.setUpdate_mode("UPDATE");
approvalsInstanceVo.setDisplay_method("BROWSER");
List<InstanceTask> instanceTasks = new ArrayList<>();
for (int i = 0; i < newReviewedIds.size(); i++) {
// 审核人相关的信息
InstanceTask instanceTask = new InstanceTask();
instanceTask.setUser_id(newReviewedIds.get(i)); // 审批人的id
if (!taskIdList.isEmpty()) {
instanceTask.setTask_id(taskIdList.get(i));
}
InstanceLink taskLink = new InstanceLink();
if (!reviewPcLinkList.isEmpty()) {
taskLink.setPc_link(reviewPcLinkList.get(i));
}
if (!reviewMobileLinkList.isEmpty()) {
taskLink.setMobile_link(reviewMobileLinkList.get(i));
}
instanceTask.setLinks(taskLink);
if (!taskStatusList.isEmpty()) {
instanceTask.setStatus(taskStatusList.get(i));
}
instanceTask.setTitle("@i18n@1");
instanceTask.setCreate_time(String.valueOf(System.currentTimeMillis()));
instanceTask.setEnd_time(String.valueOf(System.currentTimeMillis()));
instanceTask.setUpdate_time(String.valueOf(System.currentTimeMillis()));
instanceTask.setDisplay_method("BROWSER");
instanceTasks.add(instanceTask);
}
approvalsInstanceVo.setTask_list(instanceTasks);
// 国际化文案
DefinitionI18nResources definitionI18nResources = new DefinitionI18nResources();
definitionI18nResources.setLocale("zh-CN");
definitionI18nResources.setIs_default(true);
List<DefinitionTexts> texts = new ArrayList<>();
texts.add(new DefinitionTexts("@i18n@1",instanceDto.getTitle()));
definitionI18nResources.setTexts(texts);
List<DefinitionI18nResources> i18n_resources = new ArrayList<>();
i18n_resources.add(definitionI18nResources);
approvalsInstanceVo.setI18n_resources(i18n_resources);
return approvalsInstanceVo;
}
private HttpHeaders createHeaders(String accessToken) {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + accessToken);
headers.set("Content-Type", "application/json");
return headers;
}
@Override
public void sendHrefMessageByBot(BotMessageDto botMessageDto) {
String url = env.getProperty("feishu.bot_send_message");
String signLoginUrl = env.getProperty("feishu.sign_login_in");
assert url != null;
assert signLoginUrl != null;
try {
// 获取token
String accessToken = getAccessToken();
// 构建参数
List<String> newPushRange = new ArrayList<>();
if (StringUtils.isNotBlank(botMessageDto.getReceiveIds())) {
List<String> pushRangeList = Arrays.asList(botMessageDto.getReceiveIds().split(","));
for (String id : pushRangeList) {
SysUser reviewUser = sysUserService.getById(id);
if (!Objects.isNull(reviewUser) && StringUtils.isNotBlank(reviewUser.getFeishuId())) {
newPushRange.add(reviewUser.getFeishuId());
}
}
}
if (newPushRange.isEmpty()) {
log.error("接收消息的飞书用户均不存在,消息发送终止");
return;
}
// 将url替换为单点登录的地址
String hrefUrl = botMessageDto.getHrefUrl();
if (StringUtils.isNotBlank(hrefUrl)) {
hrefUrl = signLoginUrl.replace("{REDIRECT_URI}", hrefUrl);
}
// 构建消息内容部分
JSONArray contentArray = new JSONArray();
// 构建普通文本前缀
if (StringUtils.isNotBlank(botMessageDto.getTextBeforeHref())) {
JSONObject textBeforeHrefObject = new JSONObject();
textBeforeHrefObject.put("tag", "text");
textBeforeHrefObject.put("text", botMessageDto.getTextBeforeHref());
contentArray.add(textBeforeHrefObject);
}
// 构建超链接文本
if (StringUtils.isNotBlank(botMessageDto.getHrefText()) && StringUtils.isNotBlank(hrefUrl)) {
JSONObject hrefContentObject = new JSONObject();
hrefContentObject.put("tag", "a");
hrefContentObject.put("href", hrefUrl);
hrefContentObject.put("text", botMessageDto.getHrefText());
contentArray.add(hrefContentObject);
}
// 构建普通文本后缀
if (StringUtils.isNotBlank(botMessageDto.getTextAfterHref())) {
JSONObject textAfterHrefObject = new JSONObject();
textAfterHrefObject.put("tag", "text");
textAfterHrefObject.put("text", botMessageDto.getTextAfterHref());
contentArray.add(textAfterHrefObject);
}
List<JSONArray> contentList = new ArrayList<>();
contentList.add(contentArray);
JSONObject zhCnObject = new JSONObject();
zhCnObject.put("title", botMessageDto.getTitle());
zhCnObject.put("content", contentList);
JSONObject finalObject = new JSONObject();
finalObject.put("zh_cn", zhCnObject);
String content = finalObject.toJSONString();
BotMessageVo botMessageVo = new BotMessageVo();
botMessageVo.setMsg_type("post");
botMessageVo.setContent(content);
// 设置请求头
HttpHeaders headers = createHeaders(accessToken);
// 构建包含查询参数的 URL
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url).queryParam("receive_id_type", "user_id");
// 创建 HttpEntity
for (String pushId : newPushRange) {
String uuid = UUID.randomUUID().toString().replace("-", "");
botMessageVo.setReceive_id(pushId);
botMessageVo.setUuid(uuid);
HttpEntity<BotMessageVo> entity = new HttpEntity<>(botMessageVo, headers);
restTemplate.exchange(builder.toUriString(), HttpMethod.POST, entity, String.class);
}
} catch (Exception e) {
log.error("飞书发送消息异常" + e.getMessage());
}
}
/**
* 根据预授权码获取用户信息
*
* @param codeObject
* @return
*/
@Override
public Result<JSONObject> getUserInfoByCode(JSONObject codeObject) {
String url = env.getProperty("feishu.get_userinfo_by_code");
assert url != null;
String code = codeObject.getString("code");
// 获取token
String accessToken = getAccessToken();
// 设置请求头
HttpHeaders headers = createHeaders(accessToken);
// 创建 HttpEntity
JSONObject jsonObject = new JSONObject();
jsonObject.put("grant_type","authorization_code");
jsonObject.put("code",code);
HttpEntity<JSONObject> entity = new HttpEntity<>(jsonObject, headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
// 处理响应
Result<JSONObject> result = new Result<>();
JSONObject jsonResponse = JSON.parseObject(response.getBody());
if ("0".equals((String.valueOf(jsonResponse.get("code"))))) {
JSONObject dataJson = jsonResponse.getJSONObject("data");
String feishuId = dataJson.getString("user_id");
// 从sys_user表查询对应的用户信息
LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(SysUser::getFeishuId,feishuId);
SysUser suer = sysUserService.getOne(lambdaQueryWrapper);
//用户登录信息
userInfo(suer, result);
} else {
log.error("获取用户信息失败");
}
return result;
}
private Result<JSONObject> userInfo(SysUser sysUser, Result<JSONObject> result) {
String multiDepart ="multi_depart";
String syspassword = sysUser.getPassword();
String username = sysUser.getUsername();
//获取用户角色信息
String userId = sysUser.getId();
if (StringUtils.isNotBlank(userId)){
LambdaQueryWrapper<SysUserRole> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUserRole::getUserId, userId);
List<SysUserRole> sysUserRoles = sysUserRoleService.list(wrapper);
if (!sysUserRoles.isEmpty()){
List<String> collect = sysUserRoles.stream().map(SysUserRole::getRoleId).distinct().collect(Collectors.toList());
List<SysRole> roles = sysRoleService.listByIds(collect);
String code = roles.stream().map(SysRole::getRoleCode).collect(Collectors.joining(","));
sysUser.setRoleCode(code);
}
}
// 获取用户部门信息
JSONObject obj = new JSONObject();
List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
obj.put("departs", departs);
if (departs == null || departs.isEmpty()) {
obj.put(multiDepart, 0);
} else if (departs.size() == 1) {
sysUserService.updateUserDepart(username, departs.get(0).getOrgCode());
obj.put(multiDepart, 1);
} else {
//查询当前是否有登录部门
SysUser sysUserById = sysUserService.getById(sysUser.getId());
if(oConvertUtils.isEmpty(sysUserById.getOrgCode())){
sysUserService.updateUserDepart(username, departs.get(0).getOrgCode());
}
obj.put(multiDepart, 2);
}
if (CollectionUtils.isNotEmpty(departs)) {
List<String> departIdList = departs.stream().map(SysDepart::getId).collect(Collectors.toList());
sysUser.setDepartIds(StringUtils.join(departIdList, ","));
}
String tenantIds = sysUser.getRelTenantIds();
if (oConvertUtils.isNotEmpty(tenantIds)) {
List<String> tenantIdList = Arrays.asList(tenantIds.split(","));
// 该方法仅查询有效的租户,如果返回0个就说明所有的租户均无效。
List<SysTenant> tenantList = sysTenantService.queryEffectiveTenant(tenantIdList);
if (tenantList.isEmpty()) {
return Result.error("与该用户关联的租户均已被冻结,无法登录!");
} else {
obj.put("tenantList", tenantList);
}
}
// 生成token
String token = JwtUtil.sign(username, syspassword);
// 设置token缓存有效时间
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000);
String departIds = sysUser.getDepartIds();
if (StringUtils.isNotBlank(departIds)) {
String departIds_dicText = translateDictValue("id", "depart_name", "sys_depart", departIds, null);
sysUser.setDepartIds_dicText(departIds_dicText);
}
obj.put("token", token);
obj.put(USER_INFO, sysUser);
obj.put("sysAllDictItems", sysDictService.queryAllDictItems());
result.setResult(obj);
result.setMessage("登录成功");
return result;
}
private String translateDictValue(String code, String text, String table, String key, List<SysDictItemCore> listDict) {
if (oConvertUtils.isEmpty(key)) {
return null;
}
StringBuilder textValue = new StringBuilder();
String[] keys = key.split(",");
for (String k : keys) {
String tmpValue = null;
if (k.trim().length() == 0) {
continue; //跳过循环
}
if (!org.springframework.util.StringUtils.isEmpty(table)) {
tmpValue = commonAPI.queryTableDictTextByKey(table, text, code, k.trim());
} else {
List<SysDictItemCore> listNew = listDict.stream()
.filter(o -> Objects.equals(o.getDictCode(), code) && Objects.equals(o.getItemValue(), k.trim())).collect(Collectors.toList());
if (!org.springframework.util.CollectionUtils.isEmpty(listNew)) {
tmpValue = listNew.get(0).getItemText();
}
}
if (tmpValue != null) {
if (!"".contentEquals(textValue)) {
textValue.append(",");
}
textValue.append(tmpValue);
}
}
return textValue.toString();
}
}
@@ -0,0 +1,35 @@
package com.jero.modules.docking.feishu.vo.bot;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class BotMessageVo {
/**
* 消息接收者的id
*/
@ApiModelProperty("消息接收者的ID")
private String receive_id;
/**
* 消息类型 包括:text、post、image、file、audio、media、
* sticker、interactive、share_chat、share_user等
*/
@ApiModelProperty("消息类型")
private String msg_type;
/**
* 消息内容,JSON结构序列化后的字符串
*/
@ApiModelProperty("消息内容")
private String content;
/**
* 由开发者生成的唯一字符串序列,用于发送消息请求去重;
* 持有相同uuid的请求1小时内至多成功发送一条消息
*/
@ApiModelProperty("UUID")
private String uuid;
}
@@ -0,0 +1,80 @@
package com.jero.modules.docking.feishu.vo.definition;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
public class ApprovalsDefinitionVo {
/**
*
* 审批名称的国际化文案 Key,以 @i18n@ 开头,长度不得少于 9 个字符
* 示例值: @i18n@1
*/
@ApiModelProperty("审批名称的国际化文案")
private String approval_name;
/**
*
* 审批定义 code,用户自定义,定义的唯一标识,如果不存在该 code,则创建,否则更新
* 示例值: permission_test
*/
@ApiModelProperty("审批定义code")
private String approval_code;
/**
*
* 审批定义所属审批分组,用户自定义; 如果group_code当前不存在,则会新建审批分组;
* 如果group_code已经存在,则会使用group_name更新审批分组名称
* 示例值: work_group
*/
@ApiModelProperty("审批定义所属审批分组")
private String group_code;
/**
* 分组名称,值的格式是 i18n key,文案放在 i18n_resource
* 如果是 group_code 当前不存在,则该 group_name 必填,否则,
* 如果填写了则会更新分组名称,不填则不更新分组名称;
* 审批发起页 审批定义的分组名称来自该字段
* 示例值: @i18n@2
*/
@ApiModelProperty("审批定义所属审批分组")
private String group_name;
/**
* 审批定义的说明,值的格式是 i18n key,文案放在 i18n_resource
* 审批发起页 审批定义的说明内容来自该字段
* 示例值: @i18n@2
*/
@ApiModelProperty("审批定义的说明")
private String description;
/**
* 三方审批相关
*/
@ApiModelProperty("三方审批相关")
private DefinitionExternal external;
/**
* 可见人列表(最大支持长度200),可通知配置多个可见人,
* 只有在配置的范围内用户可以在审批发起页看到该审批,
* 默认不传,则是任何人不可见
*/
@ApiModelProperty("可见人列表")
private List<DefinitionViewers> viewers;
/**
* 国际化文案
*/
@ApiModelProperty("国际化文案")
private List<DefinitionI18nResources> i18n_resources;
/**
* 根据user_id_type填写流程管理员id列表(最大支持长度200)
* 示例值:["19a294c2"]
*/
@ApiModelProperty("流程管理员id列表")
private String[] managers;
}
@@ -0,0 +1,108 @@
package com.jero.modules.docking.feishu.vo.definition;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 三方审批相关
*/
@Data
public class DefinitionExternal {
/**
*
* 列表中用于提示审批来自哪里,i18n key,
* 注意不需要“来自”前缀,审批中心会拼上前缀
* 示例值:"@i18n@3"
*/
@ApiModelProperty("列表中用于提示审批来自哪里")
private String biz_name;
/**
* 审批定义业务类别
* 示例值:"permission"
*/
@ApiModelProperty("审批定义业务类别")
private String biz_type;
/**
* 移动端发起链接,如果设置了该链接,则会在移动端审批发起页展示该审批,
* 用户点击后会跳转到该链接进行发起;
* 如果不填,则在mobile端不显示该审批
*/
@ApiModelProperty("移动端发起链接")
private String create_link_mobile;
/**
* PC端发起链接,如果设置了该链接,则会在PC端审批发起页展示该审批,
* 用户点击后会跳转到该链接进行发起;
* 如果不填,则在PC端不显示该审批;
*/
@ApiModelProperty("PC端发起链接")
private String create_link_pc;
/**
* 审批实例、审批任务、审批抄送是否要在PC端展示,
* 如果为 true,则PC端列表会展示该定义下的实例信息,否则,不展示
*/
@ApiModelProperty("审批实例、审批任务、审批抄送是否要在PC端展示")
private boolean support_pc;
/**
* 审批实例、审批任务、审批抄送是否要在移动端展示,
* 如果为 true,则PC端列表会展示该定义下的实例信息,否则,不展示
*/
@ApiModelProperty("审批实例、审批任务、审批抄送是否要在移动端展示")
private boolean support_mobile;
/**
* 是否支持批量已读
*/
@ApiModelProperty("是否支持批量已读")
private boolean support_batch_read;
/**
* 是否支持批量已读(该字段无效)
*/
@ApiModelProperty("是否支持标注可读(该字段无效)")
private boolean enable_mark_readed;
/**
* 是否支持快速操作
*/
@ApiModelProperty("是否支持快速操作")
private boolean enable_quick_operate;
/**
* 三方系统的操作回调 url,【待审批】列表的任务审批人点同意或拒绝操作后,审批中心调用该地址通知三方系统
*/
@ApiModelProperty("三方系统的操作回调 url")
private String action_callback_url;
/**
* 回调时带的 token, 用于业务系统验证请求来自审批,
*/
@ApiModelProperty("回调时带的token")
private String action_callback_token;
/**
* 请求参数加密密钥,如果配置了该参数,则会对请求参数进行加密,业务需要对请求进行解密
*/
@ApiModelProperty("请求参数加密密钥")
private String action_callback_key;
/**
* 是否支持批量审批
*/
@ApiModelProperty("是否支持批量审批")
private boolean allow_batch_operate;
/**
* 审批流程数据是否不纳入效率统计
*/
@ApiModelProperty("审批流程数据是否不纳入效率统计")
private boolean exclude_efficiency_statistics;
}
@@ -0,0 +1,61 @@
package com.jero.modules.docking.feishu.vo.definition;
import com.alibaba.fastjson.annotation.JSONField;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* 国际化文案
*/
@Data
public class DefinitionI18nResources {
/**
* 语言可选值有: zh-CN:中文 en-US:英文 ja-JP:日文
* 示例值:"zh-CN"
* zh-CN:中文
* en-US:英文
* ja-JP:日文
*/
@ApiModelProperty("语言可选值有: zh-CN:中文 en-US:英文 ja-JP:日文")
private String locale;
/**
* 是否默认语言,默认语言需要包含所有key,非默认语言如果key不存在会使用默认语言代替
*/
@ApiModelProperty("是否默认语言,默认语言需要包含所有key,非默认语言如果key不存在会使用默认语言代替")
@JSONField(name = "is_default")
private boolean is_default;
/**
* 示例值: "{ @i18n@1: 权限申请 }"
*/
@ApiModelProperty("{ @i18n@1: 权限申请 }")
private List<DefinitionTexts> texts;
public String getLocale() {
return locale;
}
public void setLocale(String locale) {
this.locale = locale;
}
public boolean isIs_default() {
return is_default;
}
public void setIs_default(boolean is_default) {
this.is_default = is_default;
}
public List<DefinitionTexts> getTexts() {
return texts;
}
public void setTexts(List<DefinitionTexts> texts) {
this.texts = texts;
}
}
@@ -0,0 +1,30 @@
package com.jero.modules.docking.feishu.vo.definition;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class DefinitionTexts {
public DefinitionTexts(String key, String value) {
this.key = key;
this.value = value;
}
public DefinitionTexts() {
}
/**
文案key
示例值:"@i18n@1"
*/
@ApiModelProperty("文案key")
private String key;
/**
文案
示例值:"people"
*/
@ApiModelProperty("文案")
private String value;
}
@@ -0,0 +1,36 @@
package com.jero.modules.docking.feishu.vo.definition;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 可见人列表
*/
@Data
public class DefinitionViewers {
/**
可见人类型 示例值:"USER"
* 可选值有:
TENANT:租户内可见
DEPARTMENT:指定部门
USER:指定用户
NONE:任何人都不可见
*/
@ApiModelProperty("可见人类型 TENANT:租户内可见 DEPARTMENT:指定部门 USER:指定用户 NONE:任何人都不可见")
private String viewer_type;
/**
* 当 viewer_type 是 USER,根据user_id_type填写用户id
* 示例值:"19a294c2"
*/
@ApiModelProperty("当 viewer_type 是 USER,根据user_id_type填写用户id")
private String viewer_user_id;
/**
* 当 viewer_type 为DEPARTMENT,根据department_id_type填写部门id
* 示例值:"od-ac9d697abfa990b715dcc33d58a62a9d"
*/
@ApiModelProperty("当 viewer_type 为DEPARTMENT,根据department_id_type填写部门id")
private String viewer_department_id;
}
@@ -0,0 +1,185 @@
package com.jero.modules.docking.feishu.vo.instance;
import com.jero.modules.docking.feishu.vo.definition.DefinitionI18nResources;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
public class ApprovalsInstanceVo {
/**
* 审批定义 code, 创建审批定义返回的值,表示该实例属于哪个流程;
* 该字段会影响到列表中该实例的标题,标题取自对应定义的 name 字段
* 示例值:"81D31358-93AF-92D6-7425-01A5D67C4E71"
*/
@ApiModelProperty("审批定义 code")
private String approval_code;
/**
* 审批实例状态
* 示例值:"PENDING"
* PENDING 审批中
* APPROVED: 审批流程结束,结果为同意
* REJECTED: 审批流程结束,结果为拒绝
* CANCELED 审批发起人撤回
* DELETED 审批被删除
* HIDDEN: 状态隐藏(不显示状态)
* TERMINATED:审批终止
*/
@ApiModelProperty("审批实例状态")
private String status;
/**
* 审批实例扩展 JSON。单据编号通过传business_key字段来实现。
* 以下示例值未转义,使用时请注意转义。
* 你可查看请求体示例中转义后的 extra 示例值
* 示例值:"{"xxx":"xxx","business_key":"xxx"}"
*/
@ApiModelProperty("审批实例扩展JSON")
private String extra;
/**
*
* 审批实例唯一标识,用户自定义,需确保证租户和应用下都唯一
* 示例值:"24492654"
*/
@ApiModelProperty("审批实例唯一标识")
private String instance_id;
/**
* 审批实例链接集合
* 用于【已发起】列表的跳转,跳转回三方系统
*/
@ApiModelProperty("审批实例链接集合")
private InstanceLink links;
/**
* 审批展示名称,如果填写了该字段,则审批列表中的审批名称使用该字段,
* 如果不填该字段,则审批名称使用审批定义的名称
* 示例值:"@i18n@1"
*/
@ApiModelProperty("审批展示名称")
private String title;
/**
* 用户提交审批时填写的表单数据,用于所有审批列表中展示。可传多个值,
* 但审批中心pc展示前2个,移动端展示前3个,长度不超过2048字符
* 示例值:[{ "name": "@i18n@2", "value": "@i18n@3" }]
*/
@ApiModelProperty("用户提交审批时填写的表单数据")
private List<InstanceForm> form;
/**
*
* 审批发起人 user_id,发起人可在【已发起】列表中看到所有已发起的审批;
* 在【待审批】,【已审批】【抄送我】列表中,该字段展示审批是谁发起的。
* 示例值:"a987sf9s"
*/
@ApiModelProperty("审批发起人 user_id")
private String user_id;
/**
* 审批发起人 用户名,如果发起人不是真实的用户(例如是某个部门),
* 没有 user_id,则可以使用该字段传名称
* 示例值:"@i18n@9"
*/
@ApiModelProperty("审批发起人 用户名")
private String user_name;
/**
* 审批发起人 open id
* 示例值:"ou_be73cbc0ee35eb6ca54e9e7cc14998c1"
*/
@ApiModelProperty("审批发起人 open id")
private String open_id;
/**
* 发起人部门,用于列表中展示发起人所属部门。不传则不展示。
* 如果用户没加入任何部门,传 "",将展示租户名称传 department_name 展示部门名称
* 示例值:"od-8ec33278bc2"
*/
@ApiModelProperty("发起人部门")
private String department_id;
/**
* 审批发起人 部门,如果发起人不是真实的用户(例如是某个部门),
* 没有 department_id,则可以使用该字段传名称
* 示例值:"@i18n@10"
*/
@ApiModelProperty("审批发起人 部门")
private String department_name;
/**
* 审批发起时间,Unix毫秒时间戳
* 示例值:"1556468012678"
*/
@ApiModelProperty("审批发起时间 Unix毫秒时间戳")
private String start_time;
/**
* 审批实例结束时间,Unix毫秒时间戳
* 示例值:"1556468012678"
*/
@ApiModelProperty("审批实例结束时间 Unix毫秒时间戳")
private String end_time;
/**
* 审批实例最近更新时间;用于推送数据版本控制如果 update_mode 值为 UPDATE
* 则只有传过来的 update_time 有变化时(变大),才会更新审批中心中的审批实例信息。
* 使用该字段主要用来避免并发时老的数据更新了新的数据
* 示例值:"1556468012678"
*/
@ApiModelProperty("审批实例最近更新时间 Unix毫秒时间戳")
private String update_time;
/**
* 列表页打开审批实例的方式
* 示例值:"BROWSER"
* BROWSER:跳转系统默认浏览器打开
* SIDEBAR:飞书中侧边抽屉打开
* NORMAL:飞书内嵌页面打开
* TRUSTEESHIP:以托管打开
*/
@ApiModelProperty("列表页打开审批实例的方式")
private String display_method;
/**
* 更新方式, 当 update_mode=REPLACE时,每次都以当前推送的数据为最终数据,会删掉审批中心中多余的任务、抄送数据(不在这次推送的数据中);
* 当 update_mode=UPDATE时,则不会删除审批中心的数据,而只是进行新增和更新实例、任务数据
* 示例值:"UPDATE"
* 可选值有:
* REPLACE:全量替换,默认值
* UPDATE:增量更新
*/
@ApiModelProperty("列表页打开审批实例的方式")
private String update_mode;
/**
* 抄送列表
*/
@ApiModelProperty("任务列表")
private List<InstanceTask> task_list;
/**
* 国际化文案
*/
@ApiModelProperty("国际化文案")
private List<DefinitionI18nResources> i18n_resources;
/**
* 单据托管认证token,托管回调会附带此token,帮助业务方认证
* 示例值:"788981c886b1c28ac29d1e68efd60683d6d90dfce80938ee9453e2a5f3e9e306"
*/
@ApiModelProperty("单据托管认证token")
private String trusteeship_url_token;
/**
* 用户的类型,会影响请求参数用户标识域的选择,包括加签操作回传的目标用户, 目前仅支持 "user_id"
* 示例值:"user_id"
*/
@ApiModelProperty("用户的类型")
private String trusteeship_user_id_type;
}
@@ -0,0 +1,22 @@
package com.jero.modules.docking.feishu.vo.instance;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class InstanceForm {
/**
* 表单字段名称
* 示例值:"@i18n@2"
*/
@ApiModelProperty("表单字段名称")
private String name;
/**
* 表单值
* 示例值:"@i18n@3"
*/
@ApiModelProperty("表单值")
private String value;
}
@@ -0,0 +1,23 @@
package com.jero.modules.docking.feishu.vo.instance;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class InstanceLink {
/**
* pc端的跳转链接,当用户使用飞书 pc 端时,
* 使用该字段进行跳转,托管的链接保持不变。
*/
@ApiModelProperty("pc端的跳转链接")
private String pc_link;
/**
* 移动端的跳转链接,当用户使用飞书 pc 端时,
* 使用该字段进行跳转,托管的链接保持不变。
*/
@ApiModelProperty("移动端的跳转链接")
private String mobile_link;
}
@@ -0,0 +1,151 @@
package com.jero.modules.docking.feishu.vo.instance;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 任务列表
*/
@Data
public class InstanceTask {
/**
* 审批实例内的唯一标识,用于更新审批任务时定位数据
*/
@ApiModelProperty("审批实例内的唯一标识")
private String task_id;
/**
* 审批人 user_id,和 open_id 二者至少填一个。
* 该任务会出现在审批人的【待审批】或【已审批】列表中
*/
@ApiModelProperty("审批人 user_id")
private String user_id;
/**
* 审批人 open_id,和 user_id 二者至少填一个
* 示例值:"ou_be73cbc0ee35eb6ca54e9e7cc14998c1"
*/
@ApiModelProperty("审批人 open_id")
private String open_id;
/**
* 审批任务名称
* 示例值:"i18n1"
*/
@ApiModelProperty("审批任务名称")
private String title;
/**
* 【待审批】或【已审批】中使用的跳转链接,
* 用于跳转回三方系统pc_link 和 mobile_link 必须填一个,
* 填写的是哪一端的链接,即会跳转到该链接,不受平台影响
*/
@ApiModelProperty("跳转链接")
private InstanceLink links;
/**
* 任务状态
* 示例值:"PENDING"
* 可选值有:
* PENDING:待审批
* APPROVED:任务同意
* REJECTED:任务拒绝
* TRANSFERRED:任务转交
* DONE:任务通过但审批人未操作;审批人看不到这个任务, 若想要看到, 可以通过抄送该人.
*/
@ApiModelProperty("任务状态")
private String status;
/**
* 扩展 json,任务结束原因需传complete_reason字段。枚举值与对应说明:
* approved:同意
* rejected:拒绝
* node_auto_reject:(因逻辑判断产生的)自动拒绝
* specific_rollback:退回(包括退回到发起人、退回到中间任一审批人)
* add:并加签(添加新审批人,和我一起审批)
* add_pre:前加签(添加新审批人,在我之前审批)
* add_post:后加签(添加新审批人,在我之后审批)
* delete_assignee:减签
* forward: 手动转交
* forward_resign:离职自动转交
* recall:撤销(撤回单据,单据失效)
* delete :删除审批单
* admin_forward:管理员在后台操作转交
* system_forward:系统自动转交
* auto_skip:自动通过
* manual_skip:手动跳过
* submit_again:重新提交任务
* restart:重新启动流程
* others:其他(作为兜底)
* 示例值:"{"xxx":"xxx","complete_reason":"approved"}"
*/
@ApiModelProperty("扩展 json")
private String extra;
/**
* 任务创建时间,Unix 毫秒时间戳
* 示例值:"1556468012678"
*/
@ApiModelProperty("任务创建时间,Unix 毫秒时间戳")
private String create_time;
/**
* 任务完成时间:未结束的审批为 0,Unix 毫秒时间戳
* 示例值:"1556468012678"
*/
@ApiModelProperty("任务完成时间:未结束的审批为 0,Unix 毫秒时间戳")
private String end_time;
/**
* task最近更新时间,用于推送数据版本控制; 更新策略同 instance 中的 update_time
* 示例值:"1556468012678"
*/
@ApiModelProperty("任务完成时间:未结束的审批为 0,Unix 毫秒时间戳")
private String update_time;
/**
* 操作上下文,当用户操作时,
* 回调请求中带上该参数,用于传递该任务的上下文数据
* 示例值:"123456"
*/
@ApiModelProperty("操作上下文")
private String action_context;
/**
* 列表页打开审批任务的方式
* 示例值:"BROWSER"
* 可选值有:
* BROWSER:跳转系统默认浏览器打开
* SIDEBAR:飞书中侧边抽屉打开
* NORMAL:飞书内嵌页面打开
* TRUSTEESHIP:以托管模式打开
*/
@ApiModelProperty("列表页打开审批任务的方式")
private String display_method;
/**
* 三方任务支持不纳入效率统计。false:纳入效率统计。true:不纳入效率统计
* 示例值:false
* 默认值:false
*/
@ApiModelProperty("三方任务支持不纳入效率统计")
private boolean exclude_statistics;
/**
* 节点id:必须同时满足
* 一个流程内,每个节点id唯一。如一个流程下「直属上级」、「隔级上级」等每个节点的Node_id均不一样
* 同一个流程定义内,不同审批实例中的相同节点,Node_id要保持不变。例如张三和李四分别发起了请假申请,这2个审批实例中的「直属上级」节点的node_id应该保持一致
* 示例值:"node"
*/
@ApiModelProperty("节点id")
private String node_id;
/**
* 节点名称,如「财务审批」「法务审批」,支持中英日三种语言。示例:i18n@name。需要在i18n_resources中传该名称对应的国际化文案
* 示例值:"i18n@name"
*/
@ApiModelProperty("节点名称")
private String node_name;
}