前言

应公司要求,公司人事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下载

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

  1. java对接顺丰(丰桥)官方接口查询物流附小程序物流模板

    java对接顺丰接口查询物流 前提 丰桥是没有客服的,旁边的客服和群都是摆设,"同性交友会"罢了 准备 注册丰桥账号,申请开发者权限,这个的话是秒过,然后去申请接口 申请之后,如果 ...

  2. Java对接某地联通API接口01---选号服务

    首先,需要认证看对接文档,里面会详细的写出对接上的技术要求和规范.如果有问题,一定要找对方的技术何时清楚,注意要找技术,没事别@老总或者一般的经理,会被骂!!! 1. 封装实体类.这个工作算是最简单的 ...

  3. 「开发者说」车辆管理上钉钉,云上管车小程序开发分享

    本篇文章的供稿人为云上管车研发工程师刘奇云,内容主要为钉钉小程序开发相关,概览本文大概需要3分钟,精读本文需要10分钟 "随着经济社会数字化发展水平的提升,协同办公日趋常态化.云上管车通过与 ...

  4. Java中对接钉钉API获取数据流程

    场景 需要做后台管理系统,接入钉钉API获取人员.考勤.审批等相关数据. 实现 钉钉开放平台应用开发文档 https://developers.dingtalk.com/document/app 首先 ...

  5. 钉钉与钉钉对接集成查询表单列表详情(宜搭)连通发起审批实例(官方)(钉钉【项目事项】未审核完成=>钉钉【工作延误记录表】)

    钉钉与钉钉对接集成查询表单列表详情(宜搭)连通发起审批实例(官方)(钉钉[项目事项]未审核完成=>钉钉[工作延误记录表]) 数据源平台:钉钉 钉钉(DingTalk)是阿里巴巴集团打造的企业级智 ...

  6. 开发日志:准备开发ERP对接钉钉,对使用钉钉API简单可行性概述。

    钉钉是一款现在非常流行的业务办公类软件,具有企业内部沟通.考勤审批.外部CRM等功能.除此之外,钉钉还开放了API接口.让我们程序员可以非常方便借助钉钉已有的功能,补充企业管理的短板.刚好最近准备开发 ...

  7. 对接钉钉消息通知_接入钉钉API发送企业消息

    工作中有个需求,是要把录入的销售机会由系统自动分配给销售,然后对接钉钉给销售人员发送企业消息,通知他进行跟单. 参考资料 1. 获取Access_Token Access_Token是企业访问钉钉开放 ...

  8. 金蝶云星空与钉钉对接集成采购订单查询打通发起审批实例(官方)

    来源系统:金蝶云星空 金蝶K/3Cloud(金蝶云星空)是移动互联网时代的新型ERP,是基于WEB2.0与云技术的新时代企业管理服务平台.金蝶K/3Cloud围绕着"生态.人人.体验&quo ...

  9. 企业内部应用接入钉钉获取部门及人员信息

    企业内部应用接入钉钉获取部门及人员信息 开发者后台配置 1.登录开发者后台并配置 2.创建应用 java开发阶段 1.所需钉钉接口文档 2.springboot项目配置类 3.pom.xml 4.编写 ...

最新文章

  1. mfc 饼图绘画_每周推荐|江南百景图放置类佛系游戏,慢慢玩才是乐趣
  2. ICPC-无限路之城
  3. HDU4006(The kth great number)
  4. Cisco三层交换机DHCP中继简单配置
  5. ABAP, UI5和webpack的处理入口
  6. 算法 | 一段C语言和汇编的对应分析,揭示函数调用的本质
  7. Windows Serivce服务实现过程和打包安装
  8. 我的世界服务器怎么在计分板上面显示,我的世界计分板指令教程 计分板指令怎么使用...
  9. JSP 文件上传下载系列之二[Commons fileUpload]
  10. 计算机系统配置有几方面要求,台式电脑安装win10系统配置要求有哪些
  11. matlab求系统根轨迹和系统增益,控制系统的根轨迹分析
  12. 如何使用ELK来监控性能
  13. Asp.net页面生命周期详解任我行(2)-WebForm页面生命周期WEBFORM_ASPNET控件树的生成和作用...
  14. 零基础 Java 学习笔记
  15. PDF迅捷转换器html网址,迅捷pdf转换成HTML转换器
  16. Python3.X 爬虫实战(并发爬取)
  17. 张氏矢量化骨骼化细化算法
  18. mysql按年月排序group by升序_排序-在MySQL中按GROUP BY名称之前的日期和时间排序
  19. 梯度算法之梯度上升和梯度下降
  20. 【UCIe】UCIe 支持的协议及操作模式

热门文章

  1. Python数据结构速成
  2. gh ost mysql_MySQL DDL--gh-ost学习
  3. Facebook 如何存储150亿张、1.5PB的照片
  4. 《觉醒》:头脑编程与全息宇宙 大卫·艾克
  5. rtems总体设计思路
  6. 毕业设计总结(惯性导航)
  7. cass块参照怎么改颜色,CASS符号颜色自定义的诀窍,都在这里了!
  8. elastalert控制警报时间段
  9. 如何自定义Tomcat 404错误页面
  10. bash快捷键Quick bash shortcuts--用Enki学Linux系列(4)