JAVA 对接钉钉API(人员、部门、官方智能工作流)20210527

article/2025/7/12 12:12:21

前言

应公司要求,公司人事HR系统需要对接钉钉考勤数据,所以需要获取钉钉的打卡记录、出差、外出、请假、调岗的数据,然后转换成HR系统数据。

对接前准备

创建应用

1、首先需要管理员登录钉钉开放平台,创建应用。

说明 只有管理员和子管理员可登录开发者后台。

钉钉开放平台:钉钉开放平台能力中心

2、在应用开发页面,选择企业内部开发,然后单击创建应用

3、填写应用的基本信息,然后单击确定创建

应用创建后,在基础信息页可获取AppKey和AppSecret。

如果是定制服务商创建的应用,应用的key为CustomKey。开发者需要使用应用的CustomKey和CustomSecret获取调用服务端API的授权凭证。

开发管理

在应用详情页,单击开发管理配置应用的基本信息。

配置

是否必选

配置说明

开发模式

H5微应用必须配置

选择开发模式:

  • 开发应用:开发一个新的H5微应用。

  • 快捷链接:添加一个已有的H5微应用。企业可以将内部应用通过快捷链接的方式接入钉钉,方便内部员工在钉钉工作台中使用。

服务器出口IP

输入调用钉钉服务端API时使用的IP即企业服务器的公网IP,多个IP请以英文逗号","隔开,支持带一个*号通配符的IP格式。

应用首页地址

H5微应用必须配置

输入应用首页URL,在移动端工作台点击应用图标会跳转到此页面。

可输入后端服务部署的服务器的IP或域名。例如:http://公网IP:8080

PC端首页地址

输入在PC端钉钉工作台上打开应用的地址。链接地址必须以http或https开头。

说明 

如果未填写,在钉钉PC端工作台点击应用图标时,会提示“电脑版暂不支持显示,请用手机钉钉扫描下方二维码查看”。只能在手机钉钉客户端使用该应用。

管理后台地址

输入管理员在钉钉管理后台访问该应用的地址。

添加依赖

由于jar是在本地项目,不是中央Maven库,所以要如下引用

<!--钉钉工具包-->
<dependency><groupId>com.taobao.top</groupId><artifactId>top-api-sdk-dev</artifactId><version>ding-open-mc-SNAPSHOT</version><scope>system</scope><systemPath>${pom.basedir}/src/main/webapp/WEB-INF/lib/taobao-sdk-java-auto_1479188381469-20210517.jar</systemPath>
</dependency>

接口对接

公司项目使用mybatis,所以部分代码忽略。

process_code:审批流的唯一码。在审批流编辑页面的URL中获取。

DingDingUtil 与钉钉对接工具类

import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.*;
import com.dingtalk.api.response.*;
import com.taobao.api.ApiException;
import lombok.extern.slf4j.Slf4j;import java.util.List;/*** 钉钉工具类*/
@Slf4j
public class DingDingUtil {private final static String SYSTEM_ERROR ="SYSTEM_ERROR";private final static String APPKEY ="";private final static String APPSECRET="";private final static Long AGENTID = 0L;public final static String LEAVE_PROCESS_CODE = "PROC-70A9A9C2-BF877C09E3FE";public final static String PUBLIC_PROCESS_CODE = "PROC-C6B6F367-BE545F51D9E0";public final static String TRAVELWORK_PROCESS_CODE = "PROC-8763D6A9-A788E7CEC119";public final static String TRANSFER_PROCESS_CODE = "PROC-E05FD7DE-5A2B9D7CD6AC";//获取tokenpublic static String getToken (){Object object = LocalCacheClient.get("access_token");if(object != null){return object.toString();}DefaultDingTalkClient client = newDefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");OapiGettokenRequest request = new OapiGettokenRequest();request.setAppkey(DingDingUtil.APPKEY);request.setAppsecret(DingDingUtil.APPSECRET);request.setHttpMethod("GET");try {OapiGettokenResponse response = client.execute(request);LocalCacheClient.set("access_token", response.getAccessToken(),7200*1000);if(!response.isSuccess()) {log.error("调用钉钉接口失败:"+response.getMessage()); }return response.getAccessToken();} catch (ApiException e) {log.error(DingDingUtil.SYSTEM_ERROR, e); }return null;}//获取部门列表public static List<OapiV2DepartmentListsubResponse.DeptBaseResponse> getDepartment(){DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/listsub");OapiV2DepartmentListsubRequest request = new OapiV2DepartmentListsubRequest();//获取根部门下所有部门列表  根部门的部门id为1
//        request.setDeptId(1L);request.setHttpMethod("GET");try {OapiV2DepartmentListsubResponse response = client.execute(request, DingDingUtil.getToken());if(!response.isSuccess()) {log.error("调用钉钉接口失败:"+response.getMessage()); }return response.isSuccess() ? response.getResult():null;} catch (ApiException e) {log.error(DingDingUtil.SYSTEM_ERROR, e); }return null;}//获取部门下的所有用户列表public static List<String> getDepartmentUserId(Long departmentId){DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/listid");OapiUserListidRequest req = new OapiUserListidRequest();req.setDeptId(departmentId);try {OapiUserListidResponse response = client.execute(req, DingDingUtil.getToken());if(!response.isSuccess()) {log.error("调用钉钉接口失败:"+response.getMessage()); }return response.isSuccess()?response.getResult().getUseridList():null;} catch (ApiException e) {log.error(DingDingUtil.SYSTEM_ERROR, e); }return null;}//获取部门下的所有用户列表public static OapiV2UserListResponse.PageResult getDepartmentUser(Long departmentId, long cursor, long size){DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/list");OapiV2UserListRequest request = new OapiV2UserListRequest();request.setDeptId(departmentId);request.setCursor(cursor);request.setSize(size);request.setOrderField("modify_desc");request.setHttpMethod("GET");try {OapiV2UserListResponse  response = client.execute(request, DingDingUtil.getToken());if(!response.isSuccess()) {log.error("调用钉钉接口失败:"+response.getMessage()); }return response.isSuccess()?response.getResult():null;} catch (ApiException e) {log.error(DingDingUtil.SYSTEM_ERROR, e); }return null;}//获取钉钉考勤记录public static List<OapiAttendanceListResponse.Recordresult> getAttendanceList(String startWorkDate, String endWorkDate, List<String> userIdList, long offset, long limit) {// 通过调用接口获取考勤打卡结果DingTalkClient clientDingTalkClient = new DefaultDingTalkClient("https://oapi.dingtalk.com/attendance/list");OapiAttendanceListRequest requestAttendanceListRequest = new OapiAttendanceListRequest();// 查询考勤打卡记录的起始工作日requestAttendanceListRequest.setWorkDateFrom(startWorkDate);// 查询考勤打卡记录的结束工作日requestAttendanceListRequest.setWorkDateTo(endWorkDate);// 员工在企业内的userid列表,最多不能超过50个。requestAttendanceListRequest.setUserIdList(userIdList);// 表示获取考勤数据的起始点requestAttendanceListRequest.setOffset(offset);// 表示获取考勤数据的条数,最大不能超过50条。requestAttendanceListRequest.setLimit(limit);OapiAttendanceListResponse response = null;try {response = clientDingTalkClient.execute(requestAttendanceListRequest,DingDingUtil.getToken());if(!response.isSuccess()) {log.error("调用钉钉接口失败:"+response.getMessage()); }return response.isSuccess()?response.getRecordresult():null;} catch (ApiException e) {log.error(DingDingUtil.SYSTEM_ERROR, e); }return null;}//给用户推送消息(文字消息)public static Object pushUser(String userIds,String content){DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2");OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request();request.setUseridList(userIds);request.setAgentId(AGENTID);request.setToAllUser(false);OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();msg.setMsgtype("text");msg.setText(new OapiMessageCorpconversationAsyncsendV2Request.Text());msg.getText().setContent(content);request.setMsg(msg);try {OapiMessageCorpconversationAsyncsendV2Response response = client.execute(request, DingDingUtil.getToken());if(!response.isSuccess()) {log.error("调用钉钉接口失败:"+response.getMessage()); }return response;} catch (ApiException e) {log.error(DingDingUtil.SYSTEM_ERROR, e); }return null;}//获取审批实例ID列表public static OapiProcessinstanceListidsResponse.PageResult getProcessinstanceListid(String processCode, Long startTime, Long endTime, long cursor, long size) {try {DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/processinstance/listids");OapiProcessinstanceListidsRequest request = new OapiProcessinstanceListidsRequest();request.setProcessCode(processCode);request.setStartTime(startTime);if(endTime != null) {request.setEndTime(endTime);}request.setSize(size);request.setCursor(cursor);OapiProcessinstanceListidsResponse response = client.execute(request, DingDingUtil.getToken());if(!response.isSuccess()) {log.error("调用钉钉接口失败:"+response.getMessage()); }return response.isSuccess()?response.getResult():null;} catch (ApiException e) {log.error(DingDingUtil.SYSTEM_ERROR, e); }return null;}//获取审批实例详情public static OapiProcessinstanceGetResponse.ProcessInstanceTopVo getProcessinstanceInfo(String processInstanceId) {try {DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/processinstance/get");OapiProcessinstanceGetRequest request = new OapiProcessinstanceGetRequest();request.setProcessInstanceId(processInstanceId);OapiProcessinstanceGetResponse response = client.execute(request, DingDingUtil.getToken());if(!response.isSuccess()) {log.error("调用钉钉接口失败:"+response.getMessage()); }return response.isSuccess()?response.getProcessInstance():null;} catch (ApiException e) {log.error(DingDingUtil.SYSTEM_ERROR, e); }return null;}}

LocalCacheClient 本地缓存工具

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;/*** 本地缓存工具*/
public class LocalCacheClient {// 缓存mapprivate static Map<String, Object> cacheMap = new HashMap<String, Object>();// 缓存有效期mapprivate static Map<String, Long> expireTimeMap = new HashMap<String, Long>();/*** 获取指定的value,如果key不存在或者已过期,则返回null* @param key* @return*/public static Object get(String key) {if (!cacheMap.containsKey(key)) {return null;}if (expireTimeMap.containsKey(key)) {if (expireTimeMap.get(key) < System.currentTimeMillis()) { // 缓存失效,已过期return null;}}return cacheMap.get(key);}/*** @param key* @param <T>* @return*/public static <T> T getT(String key) {Object obj = get(key);return obj == null ? null : (T) obj;}/*** 设置value(不过期)* @param key* @param value*/public static void set(String key, Object value) {cacheMap.put(key, value);}/*** 设置value* @param key* @param value* @param millSeconds 过期时间(毫秒)*/public static void set(final String key, Object value, int millSeconds) {final long expireTime = System.currentTimeMillis() + millSeconds;cacheMap.put(key, value);expireTimeMap.put(key, expireTime);if (cacheMap.size() > 2) { // 清除过期数据new Thread(new Runnable() {public void run() {// 此处若使用foreach进行循环遍历,删除过期数据,会抛出java.util.ConcurrentModificationException异常Iterator<Map.Entry<String, Object>> iterator = cacheMap.entrySet().iterator();while (iterator.hasNext()) {Map.Entry<String, Object> entry = iterator.next();if (expireTimeMap.containsKey(entry.getKey())) {long expireTime = expireTimeMap.get(key);if (System.currentTimeMillis() > expireTime) {iterator.remove();expireTimeMap.remove(entry.getKey());}}}}}).start();}}/*** key是否存在* @param key* @return*/public static boolean isExist(String key) {return cacheMap.containsKey(key);}}

对接钉钉业务代码

单独获取钉钉人员、部门方法

@Test
public void doBaseTask() {log.info("--------------------------钉钉人员部门基本任务开始-----------------------");Date now = new Date();try {List<OapiV2DepartmentListsubResponse.DeptBaseResponse> departmentList = DingDingUtil.getDepartment();if(departmentList != null && departmentList.size() > 0) {List<String> userIdList = new ArrayList<>();List<HrDingdingDept> dingdingDeptList = new ArrayList<>();List<OapiV2UserListResponse.ListUserResponse> userList = new ArrayList();for(OapiV2DepartmentListsubResponse.DeptBaseResponse department:departmentList) {long cursor = 0L;long size = 50L;OapiV2UserListResponse.PageResult userPageResult = DingDingUtil.getDepartmentUser(department.getDeptId(), cursor, size);while (userPageResult != null && userPageResult.getHasMore()) {userList.addAll(userPageResult.getList());cursor = userPageResult.getNextCursor();userPageResult = DingDingUtil.getDepartmentUser(department.getDeptId(), cursor, size);}if(userPageResult != null && userPageResult.getList() != null && userPageResult.getList().size() > 0) {userList.addAll(userPageResult.getList());}HrDingdingDept hrDingdingDept = new HrDingdingDept();hrDingdingDept.setDeptId(department.getDeptId());hrDingdingDept.setName(department.getName());hrDingdingDept.setParentId(department.getParentId());hrDingdingDept.setUpdateTime(now);dingdingDeptList.add(hrDingdingDept);}hrDingdingService.insertOrUpdateDept(dingdingDeptList);if(userList != null && userList.size() > 0) {List<HrDingdingUser> hrDingdingUserList = new ArrayList<>();for(OapiV2UserListResponse.ListUserResponse user:userList) {userIdList.add(user.getUserid());HrDingdingUser hrDingdingUser = new HrDingdingUser();hrDingdingUser.setUserid(user.getUserid());hrDingdingUser.setUnionid(user.getUnionid());hrDingdingUser.setName(user.getName());hrDingdingUser.setAvatar(user.getAvatar());hrDingdingUser.setStateCode(user.getStateCode());hrDingdingUser.setMobile(user.getMobile());hrDingdingUser.setHideMobile(user.getHideMobile()?"true":"false");hrDingdingUser.setTelephone(user.getTelephone());hrDingdingUser.setJobNumber(user.getJobNumber());hrDingdingUser.setTitle(user.getTitle());hrDingdingUser.setEmail(user.getEmail());hrDingdingUser.setOrgEmail(user.getOrgEmail());hrDingdingUser.setWorkPlace(user.getWorkPlace());hrDingdingUser.setRemark(user.getRemark());hrDingdingUser.setDeptIdList(JSON.toJSONString(user.getDeptIdList()));hrDingdingUser.setExtension(user.getExtension());hrDingdingUser.setAdmin(user.getAdmin()?"true":"false");hrDingdingUser.setBoss(user.getBoss()?"true":"false");hrDingdingUser.setLeader(user.getLeader()?"true":"false");if(user.getHiredDate() != null) {hrDingdingUser.setHiredDate(new Date(Long.valueOf(user.getHiredDate())));}hrDingdingUser.setExclusiveAccountType(user.getExclusiveAccountType());hrDingdingUser.setExclusiveAccount(user.getExclusiveAccount()?"true":"false");hrDingdingUser.setLoginId(user.getLoginId());hrDingdingUser.setUpdateTime(now);hrDingdingUserList.add(hrDingdingUser);}hrDingdingService.insertOrUpdateUser(hrDingdingUserList);}}} catch (Exception e) {log.error(ConstantsUtil.SYSTEM_ERROR, e);}log.info("--------------------------钉钉人员部门基本任务结束-----------------------");
}

单独获取钉钉打卡记录

@Test
public void doAttendTest() {System.out.println(DingDingUtil.getToken());Date now = new Date();String workDate = DateUtils.getDate(DateUtils.addDay(new Date(), -3));String nowDate = DateUtils.getDate(new Date());String startWorkDate = workDate + " 00:00:00";String endWorkDate = nowDate + " 23:59:59";List<OapiAttendanceListResponse.Recordresult> attendanceList = new ArrayList<>();List<OapiV2DepartmentListsubResponse.DeptBaseResponse> departmentList = DingDingUtil.getDepartment();if(departmentList != null && departmentList.size() > 0) {List<String> userIdList = new ArrayList<>();for(OapiV2DepartmentListsubResponse.DeptBaseResponse department:departmentList) {List<String> userIdListTmp = DingDingUtil.getDepartmentUserId(department.getDeptId());if(userIdListTmp != null && userIdListTmp.size() > 0) {userIdList.addAll(userIdListTmp);}}if(userIdList != null && userIdList.size() > 0) {List<List<String>> userIds = Lists.partition(userIdList, 50);for(List<String> users:userIds) {int count = 1;long offset = 0L;long limit = 50L;List<OapiAttendanceListResponse.Recordresult> attendanceListTmp = DingDingUtil.getAttendanceList(startWorkDate, endWorkDate, users, offset, limit);if(attendanceListTmp != null && attendanceListTmp.size() > 0) {attendanceList.addAll(attendanceListTmp);while (attendanceListTmp.size() <= 50) {count++;offset = (count - 1) * limit;attendanceListTmp = DingDingUtil.getAttendanceList(startWorkDate, endWorkDate, users, offset, limit);if(attendanceListTmp == null || attendanceListTmp.size() == 0) {break;}attendanceList.addAll(attendanceListTmp);}}}}if(attendanceList != null && attendanceList.size() > 0) {List<HrDingdingAttendance> hrDingdingAttendanceList = new ArrayList<>();for(OapiAttendanceListResponse.Recordresult attendance: attendanceList) {HrDingdingAttendance hrDingdingAttendance = new HrDingdingAttendance();hrDingdingAttendance.setAttendanceId(attendance.getId());hrDingdingAttendance.setSourceType(attendance.getSourceType());hrDingdingAttendance.setBaseCheckTime(attendance.getBaseCheckTime());hrDingdingAttendance.setUserCheckTime(attendance.getUserCheckTime());hrDingdingAttendance.setProcInstId(attendance.getProcInstId());hrDingdingAttendance.setApproveId(attendance.getApproveId());hrDingdingAttendance.setLocationResult(attendance.getLocationResult());hrDingdingAttendance.setTimeResult(attendance.getTimeResult());hrDingdingAttendance.setCheckType(attendance.getCheckType());hrDingdingAttendance.setUserId(attendance.getUserId());hrDingdingAttendance.setWorkDate(attendance.getWorkDate());hrDingdingAttendance.setRecordId(attendance.getRecordId());hrDingdingAttendance.setPlanId(attendance.getPlanId());hrDingdingAttendance.setGroupId(attendance.getGroupId());hrDingdingAttendance.setUpdateTime(now);hrDingdingAttendanceList.add(hrDingdingAttendance);}hrDingdingService.insertOrUpdateAttendance(hrDingdingAttendanceList);}}
}

获取工作流程数据

@Test
public void doJXProcessinstanceTest() {String type = "";//出差String processCode = DingDingUtil.TRAVELWORK_PROCESS_CODE;if(StringUtils.isNotBlank(processCode)) {if(DingDingUtil.TRANSFER_PROCESS_CODE.equals(processCode)) {type = "transfer";//调岗} else if(DingDingUtil.TRAVELWORK_PROCESS_CODE.equals(processCode)) {type = "travelwork";//出差} else if(DingDingUtil.PUBLIC_PROCESS_CODE.equals(processCode)) {type = "public";//外出} else if(DingDingUtil.LEAVE_PROCESS_CODE.equals(processCode)) {type = "leave";//请假}}try {List<HrDingdingProcessWithBLOBs> processinstanceList = hrDingdingService.getProcessinstanceList(processCode);if(processinstanceList != null && processinstanceList.size() > 0) {hrDingdingService.insertOrUpdateProcessInstance(type, processinstanceList);}} catch (Exception e) {e.printStackTrace();}}public List<HrDingdingProcessWithBLOBs> getProcessinstanceListid(String processCode) {Date now = new Date();//仅仅获取30天以为的工作流程String workDate = DateUtils.getDate(DateUtils.addDay(new Date(), -30));String startDate = workDate + " 00:00:00";long startTime = Long.valueOf(DateUtils.stringToTime(startDate, EnumDateStyle.YYYY_MM_DD_HH_MM_SS));List<HrDingdingProcessWithBLOBs> processinstanceList = new ArrayList<>();List<String> processinstanceListids = new ArrayList<>();long cursor = 0L;long size = 10L;OapiProcessinstanceListidsResponse.PageResult processinstanceListidsResponse = DingDingUtil.getProcessinstanceListid(processCode, startTime, null, cursor, size);while (processinstanceListidsResponse != null && processinstanceListidsResponse.getNextCursor() != null) {processinstanceListids.addAll(processinstanceListidsResponse.getList());cursor = processinstanceListidsResponse.getNextCursor();processinstanceListidsResponse = DingDingUtil.getProcessinstanceListid(processCode, startTime, null, cursor, size);}if(processinstanceListidsResponse != null && processinstanceListidsResponse.getList() != null && processinstanceListidsResponse.getList().size() > 0) {processinstanceListids.addAll(processinstanceListidsResponse.getList());}if(processinstanceListids != null && processinstanceListids.size() > 0) {for(String processInstanceId:processinstanceListids) {OapiProcessinstanceGetResponse.ProcessInstanceTopVo processInstanceTopVo = DingDingUtil.getProcessinstanceInfo(processInstanceId);if(processInstanceTopVo != null) {HrDingdingProcessWithBLOBs hrDingdingProcess = new HrDingdingProcessWithBLOBs();hrDingdingProcess.setProcessInstanceId(processInstanceId);hrDingdingProcess.setProcessCode(processCode);hrDingdingProcess.setTitle(processInstanceTopVo.getTitle());hrDingdingProcess.setCreateTime(processInstanceTopVo.getCreateTime());hrDingdingProcess.setFinishTime(processInstanceTopVo.getFinishTime());if(StringUtils.isNotBlank(processInstanceTopVo.getOriginatorDeptId())) {hrDingdingProcess.setOriginatorDeptId(Long.valueOf(processInstanceTopVo.getOriginatorDeptId()));}hrDingdingProcess.setOriginatorDeptName(processInstanceTopVo.getOriginatorDeptName());hrDingdingProcess.setOriginatorUserid(processInstanceTopVo.getOriginatorUserid());hrDingdingProcess.setStatus(processInstanceTopVo.getStatus());hrDingdingProcess.setResult(processInstanceTopVo.getResult());hrDingdingProcess.setBusinessId(processInstanceTopVo.getBusinessId());hrDingdingProcess.setBizAction(processInstanceTopVo.getBizAction());hrDingdingProcess.setMainProcessInstanceId(processInstanceTopVo.getMainProcessInstanceId());hrDingdingProcess.setFormComponentValues(JSON.toJSONString(processInstanceTopVo.getFormComponentValues()));hrDingdingProcess.setOperationRecords(JSON.toJSONString(processInstanceTopVo.getOperationRecords()));hrDingdingProcess.setUpdateTime(now);processinstanceList.add(hrDingdingProcess);}}}return processinstanceList;
}

HrDingdingServiceImpl 钉钉数据转换人事HR系统业务数据,省略了部分业务代码

@Slf4j
@Service
public class HrDingdingServiceImpl implements HrDingdingService {@Autowiredprivate HrDingdingUserMapper hrDingdingUserMapper;@Autowiredprivate HrDingdingDeptMapper hrDingdingDeptMapper;@Autowiredprivate HrDingdingAttendanceMapper hrDingdingAttendanceMapper;@Autowiredprivate HrDingdingProcessMapper hrDingdingProcessMapper;//以下为系统其它业务类,本例不展示其详细代码......@Overridepublic void insert(HrDingdingAttendance hrDingdingAttendance) {hrDingdingAttendanceMapper.insert(hrDingdingAttendance);}@Overridepublic void insertOrUpdateAttendance(List<HrDingdingAttendance> hrDingdingAttendanceList) {if(hrDingdingAttendanceList != null && hrDingdingAttendanceList.size() > 0) {for(HrDingdingAttendance hrDingdingAttendance:hrDingdingAttendanceList) {HrDingdingAttendanceExample hrDingdingAttendanceExample = new HrDingdingAttendanceExample();hrDingdingAttendanceExample.createCriteria().andAttendanceIdEqualTo(hrDingdingAttendance.getAttendanceId());List<HrDingdingAttendance> list = hrDingdingAttendanceMapper.selectByExample(hrDingdingAttendanceExample);if(list != null && list.size() > 0) {hrDingdingAttendanceMapper.updateByExampleSelective(hrDingdingAttendance, hrDingdingAttendanceExample);}else {hrDingdingAttendanceMapper.insert(hrDingdingAttendance);}if(hrDingdingAttendance.getUserCheckTime() != null) {HrDingdingUser hrDingdingUser = getDingdingUserByUserId(hrDingdingAttendance.getUserId());if(hrDingdingUser != null) {//根据手机号获取档案用户CMUser cmUser = getHrUserByPhone(hrDingdingUser.getMobile());if(cmUser != null) {//保存HR系统打卡记录HrAttendPunchRecord hapr = new HrAttendPunchRecord();hapr.setHrUserId(cmUser.getHrUserId());hapr.setUserNumber(cmUser.getUserName());hapr.setNameCn(cmUser.getRealName());hapr.setAttendanceTime(String.valueOf(hrDingdingAttendance.getUserCheckTime().getTime()/1000));......}else {log.warn("HR系统不存在手机号["+hrDingdingUser.getMobile()+"]的用户");}}}}}}private CMUser getHrUserByPhone(String phone) {//根据手机号获取HR系统人员信息}/***	根据userid获取钉钉人员详细信息*/private HrDingdingUser getDingdingUserByUserId(String userId) {HrDingdingUserExample hrDingdingUserExample = new HrDingdingUserExample();hrDingdingUserExample.createCriteria().andUseridEqualTo(userId);List<HrDingdingUser> hrDingdingUserList = hrDingdingUserMapper.selectByExample(hrDingdingUserExample);if(hrDingdingUserList != null && hrDingdingUserList.size() > 0) {return hrDingdingUserList.get(0);}return null;}/***	根据名称获取钉钉人员详细信息*/private HrDingdingUser getDingdingUserByName(String userName) {HrDingdingUserExample hrDingdingUserExample = new HrDingdingUserExample();hrDingdingUserExample.createCriteria().andNameEqualTo(userName);List<HrDingdingUser> hrDingdingUserList = hrDingdingUserMapper.selectByExample(hrDingdingUserExample);if(hrDingdingUserList != null && hrDingdingUserList.size() > 0) {return hrDingdingUserList.get(0);}return null;}@Overridepublic void insertOrUpdateUser(List<HrDingdingUser> hrDingdingUserList) {if(hrDingdingUserList != null && hrDingdingUserList.size() > 0) {for(HrDingdingUser hrDingdingUser:hrDingdingUserList) {HrDingdingUserExample hrDingdingUserExample = new HrDingdingUserExample();hrDingdingUserExample.createCriteria().andUseridEqualTo(hrDingdingUser.getUserid());List<HrDingdingUser> list = hrDingdingUserMapper.selectByExample(hrDingdingUserExample);if(list != null && list.size() > 0) {hrDingdingUserMapper.updateByExampleSelective(hrDingdingUser, hrDingdingUserExample);}else {hrDingdingUserMapper.insert(hrDingdingUser);}}}}@Overridepublic void insertOrUpdateDept(List<HrDingdingDept> dingdingDeptList) {if(dingdingDeptList != null && dingdingDeptList.size() > 0) {for(HrDingdingDept hrDingdingDept:dingdingDeptList) {HrDingdingDeptExample hrDingdingDeptExample = new HrDingdingDeptExample();hrDingdingDeptExample.createCriteria().andDeptIdEqualTo(hrDingdingDept.getDeptId());List<HrDingdingDept> list = hrDingdingDeptMapper.selectByExample(hrDingdingDeptExample);if(list != null && list.size() > 0) {hrDingdingDeptMapper.updateByExampleSelective(hrDingdingDept, hrDingdingDeptExample);}else {hrDingdingDeptMapper.insert(hrDingdingDept);}}}}@Overridepublic void insertOrUpdateProcessInstance(String type, List<HrDingdingProcessWithBLOBs> processinstanceList) {if(processinstanceList != null && processinstanceList.size() > 0) {for(HrDingdingProcessWithBLOBs hrDingdingProcess:processinstanceList) {HrDingdingProcessExample hrDingdingProcessExample = new HrDingdingProcessExample();hrDingdingProcessExample.createCriteria().andProcessInstanceIdEqualTo(hrDingdingProcess.getProcessInstanceId());List<HrDingdingProcess> list = hrDingdingProcessMapper.selectByExample(hrDingdingProcessExample);if(list != null && list.size() > 0) {hrDingdingProcessMapper.updateByExampleSelective(hrDingdingProcess, hrDingdingProcessExample);}else {hrDingdingProcessMapper.insert(hrDingdingProcess);}try {if("leave".equals(type)) {convertHrLeave(hrDingdingProcess);}else if("public".equals(type)) {convertHrPublic(hrDingdingProcess);}else if("travelwork".equals(type)) {convertHrTravelwork(hrDingdingProcess);}else if("transfer".equals(type)) {convertHrTransfer(hrDingdingProcess);}} catch (Exception e) {log.error(ConstantsUtil.SYSTEM_ERROR, e);}}}}private void convertHrTransfer(HrDingdingProcessWithBLOBs hrDingdingProcess) {if("COMPLETED".equals(hrDingdingProcess.getStatus()) && "agree".equals(hrDingdingProcess.getResult())) {HrDingdingUser hrDingdingUser = getDingdingUserByUserId(hrDingdingProcess.getOriginatorUserid());if(hrDingdingUser != null) {//根据手机号获取档案用户CMUser cmUser = getHrUserByPhone(hrDingdingUser.getMobile());if (cmUser != null) {ExtOaPeopleDataModel oaModel = new ExtOaPeopleDataModel();//保存人员信息......if(StringUtils.isNotBlank(hrDingdingProcess.getFormComponentValues())) {JSONArray jsonArray = JSONArray.parseArray(hrDingdingProcess.getFormComponentValues());if(jsonArray != null && jsonArray.size() > 0) {JSONObject jsonObject = jsonArray.getJSONObject(0);String value = jsonObject.getString("value");JSONArray valueArr = JSONArray.parseArray(value);for(int i=0;i<valueArr.size();i++) {JSONObject obj = valueArr.getJSONObject(i);JSONObject props = obj.getJSONObject("props");String v = obj.getString("value");if(StringUtils.isBlank(v)) {continue;}if("原部门".equals(props.getString("label"))) {oaModel.setSsbm(v);}else if("原职位".equals(props.getString("label"))) {oaModel.setGwa(v);}else if("转入部门".equals(props.getString("label"))) {oaModel.setDdbm(v);}else if("转入职位".equals(props.getString("label"))) {oaModel.setGwa(v);}else if("生效日期".equals(props.getString("label"))) {oaModel.setSjdgrq(String.valueOf(DateUtils.stringToInteger(v, EnumDateStyle.YYYY_MM_DD)));}}}}ServiceResult<HrOaEmployeeTransfer> result = hrUserEditService.updateOaPeopleData(oaModel);if (result != null && !result.getSuccess()) {log.warn("保存转岗信息失败:"+result.getMessage());}}else {log.warn("HR系统不存在手机号["+hrDingdingUser.getMobile()+"]的用户");}}}}private void convertHrTravelwork(HrDingdingProcessWithBLOBs hrDingdingProcess) {if("COMPLETED".equals(hrDingdingProcess.getStatus()) && "agree".equals(hrDingdingProcess.getResult())) {HrDingdingUser hrDingdingUser = getDingdingUserByUserId(hrDingdingProcess.getOriginatorUserid());if(hrDingdingUser != null) {//根据手机号获取档案用户CMUser cmUser = getHrUserByPhone(hrDingdingUser.getMobile());if (cmUser != null) {HrOaAttendTravelwork hrOaAttendTravelwork = new HrOaAttendTravelwork();List<HrOaAttendTravelwork> hrOaAttendTravelworkList = new ArrayList<>();//保存人员信息......hrOaAttendTravelwork.setSqrq(DateUtils.DateToString(hrDingdingProcess.getCreateTime(), EnumDateStyle.YYYY_MM_DD));hrOaAttendTravelwork.setZw(String.valueOf(oaMapTableService.getPositionOaId(cmUser.getHrUserId()).getResult()));hrOaAttendTravelwork.setSfjk("0");if(StringUtils.isNotBlank(hrDingdingProcess.getFormComponentValues())) {JSONArray jsonArray = JSONArray.parseArray(hrDingdingProcess.getFormComponentValues());if(jsonArray != null && jsonArray.size() > 0) {JSONObject formObj = jsonArray.getJSONObject(0);JSONArray propsArr = JSONArray.parseArray(formObj.getString("value"));if(propsArr != null && propsArr.size() > 0) {if(propsArr != null && propsArr.size() > 0) {for(int i=0;i<propsArr.size();i++) {JSONObject jsonObject = propsArr.getJSONObject(i);JSONObject props = jsonObject.getJSONObject("props");String v = jsonObject.getString("value");if(StringUtils.isBlank(v)) {continue;}if("出差事由".equals(props.getString("label"))) {hrOaAttendTravelwork.setCcsy(v);}if("同行人".equals(props.getString("label"))) {//查询钉钉人员HrDingdingUser txDingdingUser = getDingdingUserByName(v);if(txDingdingUser != null) {CMUser txCmUser = getHrUserByPhone(txDingdingUser.getMobile());if(txCmUser != null) {hrOaAttendTravelwork.setSxry(String.valueOf(oaMapTableService.getUserOaIdByUserId(txCmUser.getUserId()).getResult()));}}}if("行程".equals(props.getString("label"))) {JSONArray rowArray = JSONArray.parseArray(v);if(rowArray != null && rowArray.size() > 0) {for(int j=0; j<rowArray.size(); j++) {HrOaAttendTravelwork travelwork = new HrOaAttendTravelwork();JSONObject rowObj = rowArray.getJSONObject(j);JSONArray row = rowObj.getJSONArray("rowValue");for(int z=0; z<row.size(); z++) {JSONObject work = row.getJSONObject(z);if("目的城市".equals(work.getString("label"))) {travelwork.setCcmdd(work.getString("value"));}else if("开始时间".equals(work.getString("label"))) {String date = work.getString("value");date = date.substring(0, 10);travelwork.setCcrq(String.valueOf(DateUtils.stringToLong(date, "yyyy-MM-dd")));}else if("结束时间".equals(work.getString("label"))) {String date = work.getString("value");date = date.substring(0, 10);travelwork.setCcrqa(String.valueOf(DateUtils.stringToLong(date, "yyyy-MM-dd")));}}hrOaAttendTravelworkList.add(travelwork);}}}}}}}}if(hrOaAttendTravelworkList != null && hrOaAttendTravelworkList.size() > 0) {for(HrOaAttendTravelwork travelwork:hrOaAttendTravelworkList) {hrOaAttendTravelwork.setCcmdd(travelwork.getCcmdd());hrOaAttendTravelwork.setCcrq(travelwork.getCcrq());hrOaAttendTravelwork.setCcrqa(travelwork.getCcrqa());ServiceResult<HrOaAttendTravelwork> result = attendTravelworkService.saveTravelworkData(hrOaAttendTravelwork);if (result != null && !result.getSuccess()) {log.warn("保存出差信息失败:"+result.getMessage());}}}}else {log.warn("HR系统不存在手机号["+hrDingdingUser.getMobile()+"]的用户");}}}}private void convertHrPublic(HrDingdingProcessWithBLOBs hrDingdingProcess) {if("COMPLETED".equals(hrDingdingProcess.getStatus()) && "agree".equals(hrDingdingProcess.getResult())) {HrDingdingUser hrDingdingUser = getDingdingUserByUserId(hrDingdingProcess.getOriginatorUserid());if(hrDingdingUser != null) {//根据手机号获取档案用户CMUser cmUser = getHrUserByPhone(hrDingdingUser.getMobile());if (cmUser != null) {//实体类HrOaAttendPublicLeave hrOaAttendPublicLeave = new HrOaAttendPublicLeave();hrOaAttendPublicLeave.setUserName(cmUser.getUserName());hrOaAttendPublicLeave.setHrUserId(cmUser.getHrUserId());//保存人员信息......hrOaAttendPublicLeave.setApplyDate(String.valueOf(hrDingdingProcess.getCreateTime().getTime()/1000));hrOaAttendPublicLeave.setBorrowMoney("0");if(StringUtils.isNotBlank(hrDingdingProcess.getFormComponentValues())) {JSONArray jsonArray = JSONArray.parseArray(hrDingdingProcess.getFormComponentValues());if(jsonArray != null && jsonArray.size() > 0) {for(int i = 0; i<jsonArray.size(); i++) {JSONObject jsonObject = jsonArray.getJSONObject(i);if(jsonObject.get("name") != null && jsonObject.getString("name").contains("事由")) {String value = jsonObject.getString("value");if(StringUtils.isNotBlank(value)) {hrOaAttendPublicLeave.setLeaveReasonDetail(value);hrOaAttendPublicLeave.setDetails(value);}}if(jsonObject.get("name") != null && jsonObject.getString("name").contains("开始时间")) {String value = jsonObject.getString("value");if(StringUtils.isNotBlank(value)) {value = value.replace("[", "");value = value.replace("]", "");value = value.replace("\"", "");System.out.println(value);String[] formArr = value.split(",");Date beginDate = DateUtils.StringToDate(formArr[0]+":00", EnumDateStyle.YYYY_MM_DD_HH_MM_SS);Date endDate = DateUtils.StringToDate(formArr[1]+":00", EnumDateStyle.YYYY_MM_DD_HH_MM_SS);hrOaAttendPublicLeave.setLeaveBeginDate(String.valueOf(beginDate.getTime()/1000));hrOaAttendPublicLeave.setLeaveEndDate(String.valueOf(endDate.getTime()/1000));hrOaAttendPublicLeave.setLeavePlanReturnDate(String.valueOf(endDate.getTime()/1000));long l = endDate.getTime() - beginDate.getTime();long day = l / (24 * 60 * 60 * 1000);long hour = (l / (60 * 60 * 1000) - day * 24);long min = ((l / (60 * 1000)) - day * 24 * 60 - hour * 60);long s = (l / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);hrOaAttendPublicLeave.setZjts(String.valueOf(day));hrOaAttendPublicLeave.setZjxss(String.valueOf(hour));hrOaAttendPublicLeave.setZjfzs(String.valueOf(s));}}}}}hrOaAttendPublicLeave.setDestination("");hrOaAttendPublicLeave.setFollowUserIds(String.valueOf(oaMapTableService.getUserOaIdByUserId(cmUser.getUserId()).getResult()));hrOaAttendPublicLeave.setLeaveOnDutyContact("");hrOaAttendPublicLeave.setIsBeyondCity("");hrOaAttendPublicLeave.setHaveTravalFee("");hrOaAttendPublicLeave.setExpireReturnReason("");hrOaAttendPublicLeave.setIsRelateMoney("");hrOaAttendPublicLeaveMapper.insert(hrOaAttendPublicLeave);}else {log.warn("HR系统不存在手机号["+hrDingdingUser.getMobile()+"]的用户");}}}}private void convertHrLeave(HrDingdingProcessWithBLOBs hrDingdingProcess) {if("COMPLETED".equals(hrDingdingProcess.getStatus()) && "agree".equals(hrDingdingProcess.getResult())) {HrDingdingUser hrDingdingUser = getDingdingUserByUserId(hrDingdingProcess.getOriginatorUserid());if(hrDingdingUser != null) {//根据手机号获取档案用户CMUser cmUser = getHrUserByPhone(hrDingdingUser.getMobile());if (cmUser != null) {ExtOaHrAttendModel model = new ExtOaHrAttendModel();model.setFromHr(false);model.setUserId(oaMapTableService.getUserOaIdByUserId(cmUser.getUserId()).getResult());if(StringUtils.isNotBlank(hrDingdingProcess.getFormComponentValues())) {JSONArray jsonArray = JSONArray.parseArray(hrDingdingProcess.getFormComponentValues());for(int i = 0; i<jsonArray.size(); i++) {JSONObject jsonObject = jsonArray.getJSONObject(i);if (jsonObject.get("name") != null && jsonObject.getString("name").contains("开始时间")) {String value = jsonObject.getString("value");if(StringUtils.isNotBlank(value)) {value = value.replace("[", "");value = value.replace("]", "");value = value.replace("\"", "");System.out.println(value);String[] formArr = value.split(",");Date beginDate = DateUtils.StringToDate(formArr[0]+":00", EnumDateStyle.YYYY_MM_DD_HH_MM_SS);Date endDate = DateUtils.StringToDate(formArr[1]+":00", EnumDateStyle.YYYY_MM_DD_HH_MM_SS);model.setStartTime(String.valueOf(beginDate.getTime()/1000));model.setEndTime(String.valueOf(endDate.getTime()/1000));switch (formArr[4]) {case "年假": model.setVocationType("3");break;case "事假": model.setVocationType("2");break;case "病假": model.setVocationType("1");break;case "调休":break;case "产假": model.setVocationType("5");break;case "陪产假": model.setVocationType("7");break;case "婚假": model.setVocationType("6");break;case "丧假": model.setVocationType("10");break;}}break;}}}if(StringUtils.isBlank(model.getVocationType())) {log.warn("请假类型获取失败");return;}ServiceResult<ExtOaHrAttendResultModel> result = attendLeaveListService.insertLeaveDetail(model);if (result != null && !result.getSuccess()) {log.warn("保存请假信息失败:"+result.getMessage());}}else {log.warn("HR系统不存在手机号["+hrDingdingUser.getMobile()+"]的用户");}}}}}

实体类

import com.lxhr.mybatis.bean.BaseBean;
import java.io.Serializable;
import java.util.Date;//钉钉人员表
public class HrDingdingUser extends BaseBean implements Serializable {private Long id;private String userid;private String unionid;private String name;private String avatar;private String stateCode;private String mobile;private String hideMobile;private String telephone;private String jobNumber;private String title;private String email;private String orgEmail;private String workPlace;private String remark;private String deptIdList;private String extension;private String admin;private String boss;private String leader;private Date hiredDate;private String exclusiveAccount;private String loginId;private String exclusiveAccountType;private Date updateTime;private static final long serialVersionUID = 1L;//get set ...
}
import com.lxhr.mybatis.bean.BaseBean;
import java.io.Serializable;
import java.util.Date;//钉钉部门表
public class HrDingdingDept extends BaseBean implements Serializable {private Long id;private Long deptId;private String name;private Long parentId;private Date updateTime;private static final long serialVersionUID = 1L;//get set ...
}
import com.lxhr.mybatis.bean.BaseBean;
import java.io.Serializable;
import java.util.Date;//打卡记录表
public class HrDingdingAttendance extends BaseBean implements Serializable {private Long id;private Long attendanceId;private String sourceType;private Date baseCheckTime;private Date userCheckTime;private String procInstId;private Long approveId;private String locationResult;private String timeResult;private String checkType;private String userId;private Date workDate;private Long recordId;private Long planId;private Long groupId;private Date updateTime;private static final long serialVersionUID = 1L;//get set ...
}
import com.lxhr.mybatis.bean.BaseBean;
import java.io.Serializable;
import java.util.Date;//流程表
public class HrDingdingProcess extends BaseBean implements Serializable {private Long id;private String processInstanceId;private String processCode;private String title;private Date createTime;private Date finishTime;private String originatorUserid;private Long originatorDeptId;private String originatorDeptName;private String status;private String result;private String businessId;private String bizAction;private String mainProcessInstanceId;private Date updateTime;private static final long serialVersionUID = 1L;//get set ...
}

实体类对应表结构

CREATE TABLE `hr_dingding_attendance` (`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',`attendance_id` bigint(11) NOT NULL COMMENT '打卡ID',`source_type` varchar(20) DEFAULT '' COMMENT '数据来源:ATM:考勤机打卡(指纹/人脸打卡)BEACON:IBeacon DING_ATM:钉钉考勤机(考勤机蓝牙打卡) USER:用户打卡 BOSS:老板改签 APPROVE:审批系统 SYSTEM:考勤系统 AUTO_CHECK:自动打卡',`base_check_time` datetime DEFAULT NULL COMMENT '计算迟到和早退,基准时间。',`user_check_time` datetime DEFAULT NULL COMMENT '实际打卡时间, 用户打卡时间的毫秒数。',`proc_inst_id` varchar(36) DEFAULT NULL COMMENT '关联的审批实例ID,当该字段非空时,表示打卡记录与请假、加班等审批有关。',`approve_id` bigint(11) DEFAULT NULL COMMENT '关联的审批ID,当该字段非空时,表示打卡记录与请假、加班等审批有关。',`location_result` varchar(20) DEFAULT NULL COMMENT '位置结果: Normal:范围内 Outside:范围外 NotSigned:未打卡',`time_result` varchar(20) DEFAULT NULL COMMENT '打卡结果: Normal:正常 Early:早退 Late:迟到 SeriousLate:严重迟到 Absenteeism:旷工迟到 NotSigned:未打卡',`check_type` varchar(20) DEFAULT NULL COMMENT '考勤类型: OnDuty:上班 OffDuty:下班',`user_id` varchar(36) DEFAULT NULL COMMENT '打卡人的UserID。',`work_date` datetime DEFAULT NULL COMMENT '工作日。',`record_id` bigint(11) DEFAULT NULL COMMENT '打卡记录ID。',`plan_id` bigint(11) DEFAULT NULL COMMENT '排班ID。',`group_id` bigint(11) DEFAULT NULL COMMENT '考勤组ID。',`update_time` datetime DEFAULT NULL COMMENT '更新时间',PRIMARY KEY (`id`)
) COMMENT='钉钉打开记录';CREATE TABLE `hr_dingding_user` (`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',`userid` varchar(36) DEFAULT NULL COMMENT '用户的userid。',`unionid` varchar(36) DEFAULT NULL COMMENT '用户在当前开发者企业账号范围内的唯一标识。',`name` varchar(50) DEFAULT NULL COMMENT '用户姓名。',`avatar` varchar(255) DEFAULT NULL COMMENT '头像地址。',`state_code` varchar(10) DEFAULT NULL COMMENT '国际电话区号。',`mobile` varchar(20) DEFAULT NULL COMMENT '手机号码。',`hide_mobile` varchar(10) DEFAULT NULL COMMENT '是否号码隐藏:true:隐藏  false:不隐藏',`telephone` varchar(20) DEFAULT NULL COMMENT '分机号。',`job_number` varchar(10) DEFAULT NULL COMMENT '员工工号。',`title` varchar(20) DEFAULT NULL COMMENT '职位。',`email` varchar(50) DEFAULT NULL COMMENT '员工邮箱。',`org_email` varchar(50) DEFAULT NULL COMMENT '员工的企业邮箱。',`work_place` varchar(255) DEFAULT NULL COMMENT '办公地点。',`remark` varchar(255) DEFAULT NULL COMMENT '备注。',`dept_id_list` varchar(200) DEFAULT NULL COMMENT '所属部门ID列表。',`extension` varchar(255) DEFAULT NULL COMMENT '扩展属性。',`admin` varchar(10) DEFAULT NULL COMMENT '是否为企业的管理员:true:是  false:不是',`boss` varchar(10) DEFAULT NULL COMMENT '是否为企业的老板:true:是  false:不是',`leader` varchar(10) DEFAULT NULL COMMENT '是否是部门的主管:true:是  false:不是',`hired_date` datetime DEFAULT NULL COMMENT '入职时间,单位毫秒。',`exclusive_account` varchar(10) DEFAULT NULL COMMENT '是否专属帐号:true:是 false:不是',`login_id` varchar(50) DEFAULT NULL COMMENT '专属帐号登录名。',`exclusive_account_type` varchar(10) DEFAULT NULL COMMENT '专属帐号类型:sso:企业自建专属帐号  dingtalk:钉钉自建专属帐号',`update_time` datetime DEFAULT NULL COMMENT '更新时间',PRIMARY KEY (`id`)
) COMMENT='钉钉用户信息';CREATE TABLE `hr_dingding_dept` (`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',`dept_id` bigint(11) DEFAULT NULL COMMENT '部门ID。',`name` varchar(50) DEFAULT NULL COMMENT '部门名称。',`parent_id` bigint(11) DEFAULT NULL COMMENT '父部门ID。',`update_time` datetime DEFAULT NULL COMMENT '更新时间',PRIMARY KEY (`id`)
) COMMENT='钉钉部门表';CREATE TABLE `hr_dingding_process` (`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',`process_instance_id` varchar(50) DEFAULT NULL COMMENT '流程实例标识。',`process_code` varchar(100) DEFAULT NULL COMMENT '审批流的唯一码。',`title` varchar(200) DEFAULT NULL COMMENT '审批实例标题。',`create_time` datetime DEFAULT NULL COMMENT '开始时间',`finish_time` datetime DEFAULT NULL COMMENT '结束时间。',`originator_userid` varchar(36) DEFAULT NULL COMMENT '发起人的userid。',`originator_dept_id` bigint(11) DEFAULT NULL COMMENT '发起人的部门。-1表示根部门。',`originator_dept_name` varchar(50) DEFAULT NULL COMMENT '发起部门。',`status` varchar(20) DEFAULT NULL COMMENT '审批状态:NEW:新创建 RUNNING:审批中 TERMINATED:被终止 COMPLETED:完成 CANCELED:取消',`result` varchar(10) DEFAULT NULL COMMENT '审批结果:agree:同意 refuse:拒绝',`business_id` varchar(36) DEFAULT NULL COMMENT '审批实例业务编号。',`biz_action` varchar(10) DEFAULT NULL COMMENT '审批实例业务动作:MODIFY:表示该审批实例是基于原来的实例修改而来  REVOKE:表示该审批实例是由原来的实例撤销后重新发起的  NONE表示正常发起',`main_process_instance_id` varchar(50) DEFAULT NULL COMMENT '主流程实例标识。',`form_component_values` text COMMENT '表单详情列表。',`operation_records` text COMMENT '操作记录列表。',`update_time` datetime DEFAULT NULL COMMENT '更新时间',PRIMARY KEY (`id`)
) COMMENT='钉钉流程表';

最新sdk下载地址:服务端SDK下载 - 钉钉开放平台

我项目使用sdk地址:JAVA版钉钉开发SDK包dingtalk-sdk-java.zip_OapiV2DepartmentListsubResponse-互联网文档类资源-CSDN下载


http://chatgpt.dhexx.cn/article/CFnPEw3e.shtml

相关文章

Element使用级联选择器

Element使用级联选择器 element的级联选择器和select不一样,下拉框我们可以手动定义label和value,只需要将查出来的值循环一下即可 但是级联选择器的视图层是这样的 只有 :options=“options” 这个属性让我们绑定值,没办法绑定他的label和value element官网给的数据结构是这…

研发部的人员素质要求及自我培养

IT行业发展已经走的很远了&#xff0c;纵观世界经济的发展&#xff0c;经济全球化进程明显加快&#xff0c;信息化已成为全球化的迫切需要和必要保证。世界范围的产业结构调整和信息技术进步&#xff0c;必将对中国信息产业的发展产生深刻影响&#xff0c;所以IT行业的前景还是…

2-Springboot集成Flowable之 选择人员的界面自定义开发

目录 效果实现思路1、新建自己的 my-assignment-popup.html2、修改properties-assignment-controller.js 效果 演示地址 代码地址 前端代码地址 实现思路 部门是基于bootstrap-treeview插件实现的&#xff0c;表格是自己用div css写的。其实自己了解angular修改这里应该没有…

用户选择框设计思路

最近新项目要用到选择人员&#xff0c;于是重新在新项目中设计了一版选人框。 效果图如下&#xff1a; 功能部分 一个选人框主要有以下几个展示部分&#xff1a; 人员组织树已选节点信息操作工具栏 这三大部分再细分下各自应有的基础功能&#xff1a; 人员展示部分&…

解读华为的流程与 IT 管理部门

公众号回复&#xff1a;干货&#xff0c;领取价值58元/套IT管理体系文档 公众号回复&#xff1a;ITIL教材&#xff0c;领取最新ITIL4中文教材 更多专业文档请访问 www.itilzj.com 华为&#xff0c;其流程与IT管理部是国内IT部门的发展标杆&#xff0c;负责的是华为各个部门和跨…

人员选择树,搜索自动筛选功能

要实现的功能截图&#xff1a; 要求&#xff1a; 1、点击收件人输入框可以根据拼音自动筛选数据&#xff0c;并且标记已经选择的数据&#xff0c;没有结果的时候提示&#xff0c;相应的更新左边树节点状态 2、勾选树右侧树的节点左侧输入框出现一一对应的节点名称 用到的…

级联选择器el-cascader处理复杂数据(四层、五层数据),回显部门以及部门下的人员

注意&#xff1a;参考第五层的数据处理比较nice 当级联选择器需要绑定的数组不再是简易数据&#xff0c;props涉及的字段不再是一个&#xff0c;而是列表里面套列表 比如&#xff0c;我想要获取部门以及下面的员工&#xff0c;如何显示&#xff1f;如下图1所示&#xff0c;后…

一个简单的联系人及组织架构选择人员的实现

前言&#xff1a;技术实现&#xff1a;Vitevue3tsvant。 本次主要是因为本人说了一个类似的功能&#xff0c;前期遇到了很多坑&#xff0c;导致 进度缓慢。虽然可以实现&#xff0c;但是都基于多个数组操作的情况&#xff0c;当涉及功能修改或优化&#xff0c;就很难实现了。 本…

jQuery仿钉钉组织架构的选择部门功能,移动端完美树形图

在网上找了好久&#xff0c;实在没见到有类似的只能自己写一个一、功能如图所示&#xff0c;点击选择无限下级功能 二、css <style>body{margin:0;padding:0;background:#ffffff}.spaceBetween {display: flex;justify-content: space-between;align-items: center;}.su…

vue的el-tree实现部门人员的tree展示选择,包括根据已有id进行默认选中设置

根据部门和人员&#xff0c;生成部门人员选择树&#xff0c;用的是Vue的el-tree生产树。 java部分------------------------------------------------------------------------------- 1&#xff1a;中间实体dto&#xff08;就是前端要的字段&#xff0c;让从数据库中查询的时…

Java+zTree审批人员选择

实现一个类似于钉钉审批人员选择的功能。 这里使用zTree实现组织架构树。 实体类需要有id,pId实现上下级关系。 company.java getter/setter略。 private int company_id;//公司idprivate String company_name;//公司名称private String remark;//备注信息private String f…

element-tree 实现部门-人员选择(支持ID相同)

使用element-tree实现id相同的选择 相同人员可在不同部门出现, 当勾选其中一个人员时,其它部门的相同人员也要勾选上右侧可进行删除已勾选人员, 并且树状图勾选状态取消若有勾选,进入时候默认选中 效果如下: 例如: 点击勾选总经办的王五,技术部的王五也要勾选上,右侧删除王五…

【Mobile Org】适用于移动端/H5的组织部门/角色/人员选择组件

Mobile Org Introduction 移动端组织架构数据选择方案&#xff0c;包括组织机构、角色以及人员等分类&#xff0c;支持单选、多选、关键字段自定义以及多种事件及插槽等&#xff0c;适用于大部分组织选人场景。 支持懒加载回调&#xff0c;点击获取当前组织下的子组织及人员…

使用VUE自定义组件封装部门选择功能

背景 照惯例&#xff0c;先交待下背景&#xff0c;从真实需求出发&#xff0c;讲述实现效果、设计思路和实现方式。 软件系统中&#xff0c;会有一些常见常用的选择功能&#xff0c;如部门选择、人员选择等&#xff0c;用于填报表单&#xff0c;使用频率很高。直接使用一方面会…

选择部门(多层级)下人员

一、顶部部门选中标题栏&#xff08;部门面包屑&#xff09; 1.使用水平滚动的ListView&#xff08;HorizontalListView&#xff09;&#xff0c;代码搜索一下就能找到&#xff0c; 2.右监听可以使用符号文字>&#xff08;代码&#xff1a;>&#xff09;&#xff0c;也…

阿里云成立13年首次实现年度盈利;iPhone14有望实现息屏显示;Android 13将采用华为研发的只读文件系统 |EA周报...

EA周报 2022年5月27日 每个星期7分钟&#xff0c;元宝带你喝一杯IT人的浓缩咖啡&#xff0c;了解天下事、掌握IT核心技术。 周报看点 1、成立13年首次年度盈利&#xff0c;阿里云2022财年赚11亿 2、博通官宣610亿美元收购VMware 3、Android 13 将默认采用华为开发的只读文件系统…

【ELT.ZIP】OpenHarmony啃论文俱乐部—数据密集型应用内存压缩

本文出自ELT.ZIP团队&#xff0c;ELT<>Elite(精英)&#xff0c;.ZIP为压缩格式&#xff0c;ELT.ZIP即压缩精英。成员&#xff1a; 上海工程技术大学大二在校生合肥师范学院大二在校生清华大学大二在校生成都信息工程大学大一在校生黑龙江大学大一在校生华南理工大学大一在…

Nydus 镜像扫描加速

文&#xff5c;余硕 上海交通大学22届毕业生 阿里云开发工程师 从事云原生底层系统的开发和探索工作。 本文 6369 字 阅读 16 分钟 GitLink 编程夏令营是在 CCF 中国计算机学会指导下&#xff0c;由 CCF 开源发展委员会&#xff08;CCF ODC&#xff09;举办的面向全国高校学生的…

定了,6大领域93个开源任务,阿里开源导师带你参与中科院开源之夏2022

今年&#xff0c;由阿里巴巴开源导师参与的30个核心开源社区再次加入中国科学院软件研究所开源软件供应链点亮计划支持下的系列高校开源活动——开源之夏2022&#xff0c;共开放93个开源任务。通过本活动&#xff0c;同学们可以在顶级开源导师的指导下&#xff0c;通过3个月的时…

龙蜥操作系统 Anolis OS 8.6 - 来自阿里云的 CentOS 8 100% 兼容发行版

请访问原文链接&#xff1a;https://sysin.org/blog/anolis-os-8/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;www.sysin.org Anolis OS 8 是 OpenAnolis 社区推出的完全开源、中立、开放的发行版&#xff0c;它支持多计算架构&#x…