web调用钉钉待办审批

欲先攻其事,必先利其器,我们想要发布钉钉待办通知首先需要把我们公司的用户id和钉钉的userId做一个绑定,这个绑定的途径有很多种,我们可以用钉钉扫码、手机号获取通过部门信息等方式来拿到钉钉的userId。下面我们重点说一下钉钉扫码绑定这个途径。

钉钉扫码这个方式拿到userId相对于其他方式比较安全,不用担心例如手机号方式数据更改的问题,也不会像通过获取部门信息来拿userId有点冗余。

话不多说,我们直接上具体实现。官方文档 https://developers.dingtalk.com/document/app/scan-qr-code-to-login-3rdapp

配置回调域名

1)创建企业内部应用

详情请参考创建应用,应用创建后,在基础信息页面可以查看到应用的AppKey和AppSecret。

img

2)进入应用详情页,然后单击钉钉登录与分享,添加应用回调的URL,以http或https开头

img

权限管理

需要开通以下权限

img

1、扫码绑定钉钉

a、显示二维码页面

我们用了钉钉提供的扫码登录页面,方便简单。

https://oapi.dingtalk.com/connect/qrconnect?appid=AppKey 
&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=REDIRECT_URI
参数是否必填说明
appid企业内部应用的AppKey。
redirect_uri重定向地址。必须与开发者后台设置的回调域名保持一致。如果是第一种方式需要urlencode编码
state用于防止重放攻击,开发者可以根据此信息来判断redirect_uri只能执行一次来避免重放攻击。
response_type固定为code。
scope固定为snsapi_login。
loginTmpCode通过js获取到的loginTmpCode。

在钉钉用户扫码登录并确认后,会302到你指定的redirect_uri,并向url参数中追加临时授权码code及state两个参数。

b、实现流程

1)通过code获取用户的userId

  • 服务端通过临时授权码获取授权用户的个人信息

调用sns/getuserinfo_bycode接口获取授权用户的个人信息,详情请参考根据sns临时授权码获取用户信息

通过临时授权码Code获取用户信息,临时授权码只能使用一次。

  • 根据unionid获取userid

调用user/getbyunionid接口获取userid,详情请参考根据unionid获取用户信息

根据unionid获取userid,需要创建企业内部应用(小程序或微应用),使用内部应用的Appkey和AppSecret调用接口access_token。

c、实现代码

public class SysUserInfosServiceImpl extends ServiceImpl<SysUserInfosMapper, SysUserInfosPO> implements ISysUserInfosService {

    @Value("${ding.talk.app-key}")
    private String appKey;

    @Value("${ding.talk.app-secret}")
    private String appSecret;

    @Override
    public RespVO<Object> sweepCodeBinding(SweepCodeBindByDingTalkDTO dto) throws ApiException {
        // 获取access_token,注意正式代码要有异常流处理
        String accessToken = getAccessToken();

        // 通过临时授权码获取授权用户的个人信息
        DefaultDingTalkClient client2 = new DefaultDingTalkClient("https://oapi.dingtalk.com/sns/getuserinfo_bycode");
        OapiSnsGetuserinfoBycodeRequest reqByCodeRequest = new OapiSnsGetuserinfoBycodeRequest();
        // 通过扫描二维码,跳转指定的redirect_uri后,向url中追加的code临时授权码
        reqByCodeRequest.setTmpAuthCode(dto.getAuthCode());
        OapiSnsGetuserinfoBycodeResponse byCodeResponse = client2.execute(reqByCodeRequest, appKey, appSecret);
        try {
            byCodeResponse.getUserInfo().getUnionid();
        } catch (NullPointerException e) {
            return RespVO.success(JsonUtil.getJsonByString(byCodeResponse.getBody()));
        }
        // 根据unionId获取userId
        String unionId = byCodeResponse.getUserInfo().getUnionid();
        DingTalkClient clientDingTalkClient = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid");
        OapiUserGetbyunionidRequest reqGetByUnionIdRequest = new OapiUserGetbyunionidRequest();
        reqGetByUnionIdRequest.setUnionid(unionId);
        OapiUserGetbyunionidResponse oapiUserGetbyunionidResponse = clientDingTalkClient.execute(reqGetByUnionIdRequest, accessToken);
        return RespVO.success(JsonUtil.getJsonByString(oapiUserGetbyunionidResponse.getBody()));
    }


    public String getAccessToken() throws ApiException {
        DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
        OapiGettokenRequest request = new OapiGettokenRequest();
        request.setAppkey(appKey);
        request.setAppsecret(appSecret);
        /*请求方式*/
        request.setHttpMethod("GET");
        OapiGettokenResponse response = client.execute(request);
        return response.getAccessToken();
    }
}

2)内部调用绑定钉钉接口

调用示例

    @Value("${ding_talk.srv-url}")
    private String srvUrl;

    @Value("${ding_talk.fe-url}")
    private String feUrl;

@Override
public RespVO<Object> sweepCodeBinding(SweepCodeBindByDingTalkDTO dto, String jwtToken) {
    //解析出accountId
    String accountId = CookieUtil.getOperator(jwtToken).toString();
    Map<String, Object> mapParam = new HashMap<>();
    mapParam.put("auth_code", dto.getAuthCode());
  	//调用内部接口
    String response = SendHttpsUtil.sendPostByMap(srvUrl + "v1/ding-talk/backlog/sweep-code-binding-by-ding-talk", mapParam);
    JSONObject data;
    try {
        data = JSON.parseObject(response).getJSONObject("data");
    } catch (NullPointerException e) {
        return RespVO.success(RespVO.failure(ApiResultCode.G1102, "绑定地址请求失败"));
    }
    String userId;
    if (data.get("errcode").equals(0)) {
        userId = data.getJSONObject("result").get("userid").toString();
    } else {
        return RespVO.success(RespVO.failure(ApiResultCode.G1100, "绑定失败"));
    }
    //判断用户表是否已经记录该员工信息
    List<SysUserInfoPO> userInfoList = iSysUserInfoService.list(new QueryWrapper<SysUserInfoPO>().eq("account_id", accountId));
    SysUserInfoPO sysUserInfo = new SysUserInfoPO();
    if (userInfoList.size() > 0 && StringUtils.isNotBlank(userId)) {
        //如果已记录,则直接给userId字段传值(修改)
        sysUserInfo.setDtUserId(userId);
        sysUserInfo.setModifier(accountId);
        iSysUserInfoService.getBaseMapper().update(sysUserInfo, new UpdateWrapper<SysUserInfoPO>().eq("account_id", accountId));
    }
    return RespVO.success("绑定成功");
}

工具类

json工具类

public class JsonUtil {

    /**
     * json字符串转map
     *
     * @param str 字符串
     * @return map
     */
    public static LinkedHashMap<String, Object> getJsonByString(String str) {
        return JSON.parseObject(str, new TypeReference<>() {
        }, Feature.OrderedField);
    }
}

RespVO

package com.dingTalk.common.vo;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.suntime.sntp.common.vo.ApiResultCode;
import com.suntime.sntp.common.vo.DefaultApiResultCode;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.server.ServerWebExchange;

@ResponseBody
public class RespVO<T> {

    private int code;

    private String message;

    private Long count;

    @JsonInclude(Include.NON_NULL)
    private T data;

    @JsonIgnore
    private ApiResultCode apiResultCode;

    @JsonProperty("execute_time")
    @JsonInclude(Include.NON_NULL)
    private Long executeTime;

    private RespVO(){}

    private RespVO(ApiResultCode code, T data) {
        this.code = code.getCode();
        this.message = code.getMessage();
        this.data = data;
        this.apiResultCode = code;
        this.count = null;
    }

    private RespVO(ApiResultCode code, T data, Long count) {
        this.code = code.getCode();
        this.message = code.getMessage();
        this.data = data;
        this.apiResultCode = code;
        this.count = count;
    }

    private RespVO(ApiResultCode code, String msg, T data) {
        this.code = code.getCode();
        this.message = msg;
        this.data = data;
        this.apiResultCode = code;
        this.count = null;
    }

    public static <E> RespVO<E> success() {
        return new RespVO(DefaultApiResultCode.SUCCESS, (Object) null);
    }

    public static <E> RespVO<E> success(E data) {
        return new RespVO(DefaultApiResultCode.SUCCESS, data);
    }

    public static <E> RespVO<E> success(E data, Long count) {
        return new RespVO(DefaultApiResultCode.SUCCESS, data, count);
    }

    public static <E> RespVO<E> of(ApiResultCode respCode, String msg, E body) {
        return new RespVO(respCode, msg, body);
    }

    public static <E> RespVO<E> of(ApiResultCode apiResultCode, String msg) {
        return of(apiResultCode, msg, null);
    }

    public static <E> RespVO<E> failure(ApiResultCode apiResultCode, String msg) {
        return new RespVO(apiResultCode, msg, (Object) null);
    }

    public static <E> RespVO<E> failure(ApiResultCode apiResultCode) {
        return failure(apiResultCode, apiResultCode.getMessage());
    }

    public static <E> RespVO<E> serverError(String overrideDefaultMsg) {
        return new RespVO(DefaultApiResultCode.G500, overrideDefaultMsg, (Object) null);
    }

    public static <E> RespVO<E> serverError() {
        return new RespVO(DefaultApiResultCode.G500, (Object) null);
    }

    public static RespVO<Object> emptyResp(ServerWebExchange exchange) {
        return exchange.getRequest().getMethod() == HttpMethod.GET ? failure(ApiResultCode.G1001) : success();
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    public Long getCount() {
        return count;
    }

    public T getData() {
        return data;
    }

    @JsonIgnore
    public ApiResultCode getApiResultCode() {
        return apiResultCode;
    }

    public Long getExecuteTime() {
        return executeTime;
    }

    public void setExecuteTime(Long executeTime) {
        this.executeTime = executeTime;
    }

    @JsonIgnore
    public boolean cacheable() {
        return this.getCode() == ApiResultCode.SUCCESS.getCode() || this.getCode() == ApiResultCode.G1001.getCode();
    }
}

SendHttpsUtil

public class SendHttpsUtil {

    /**
     * 发送POST请求,参数是Map, contentType=x-www-form-urlencoded
     *
     * @param url      发送请求的 URL
     * @param mapParam 请求参数
     * @return 所代表远程资源的响应结果
     */
    public static String sendPostByMap(String url, Map<String, Object> mapParam) {
        Map<String, String> headParam = new HashMap<>(128);
        headParam.put("Content-type", "application/json;charset=UTF-8");
        return sendPost(url, mapParam, headParam);
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url   发送请求的 URL
     * @param param 请求参数
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, Map<String, Object> param, Map<String, String> headParam) {
        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性 请求头
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent",
                    "Fiddler");

            if (headParam != null) {
                for (Entry<String, String> entry : headParam.entrySet()) {
                    conn.setRequestProperty(entry.getKey(), entry.getValue());
                }
            }
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(JSON.toJSONString(param));
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //使用finally块来关闭输出流、输入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return result.toString();
    }
}

d、实现效果

img

2、发送待办通知

咱们绑定完钉钉后,就可以回归主题啦,发送审核待办消息。

a、接收参数

参数名称参数说明是否必须数据类型
biz_id业务idfalsestring
dtl待办任务的内容truestring
title待办任务的标题truestring
url待办任务的跳转链接truestring
user_id钉钉用户idtruestring

img

package com.dingTalk.v1.webApi.entity.dto;

import com.baomidou.mybatisplus.annotation.TableField;
import com.dingtalk.api.request.OapiWorkrecordAddRequest;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;

/**
 * @author lixuan
 */
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value = "RemindAuditDTO对象", description = "提醒审核")
public class RemindAuditDTO implements Serializable {

    private static final long serialVersionUID = 1L;

    @NotNull(message = "钉钉用户id不能为空")
    @ApiModelProperty(name = "user_id", value = "钉钉用户id")
    @TableField("user_id")
    @JsonProperty("user_id")
    private String userId;

    @NotNull(message = "待办任务的标题不能为空")
    @ApiModelProperty(name = "title", value = "待办任务的标题")
    @TableField("title")
    @JsonProperty("title")
    private String title;

    @NotNull(message = "跳转URL不能为空")
    @ApiModelProperty(name = "url", value = "待办任务的跳转链接")
    @TableField("url")
    @JsonProperty("url")
    private String url;

    @ApiModelProperty(name = "biz_id", value = "业务id")
    @TableField("biz_id")
    @JsonProperty("biz_id")
    private String bizId;

    @NotNull(message = "待办任务的内容不能为空")
    @ApiModelProperty(name = "dtl", value = "待办任务的内容")
    @TableField("dtl")
    @JsonProperty("dtl")
    private String dtl;
}

b、实现代码

@Override
public RespVO<Object> remindAudit(RemindAuditDTO dto) throws ApiException {
    OapiWorkrecordAddRequest req = new OapiWorkrecordAddRequest();
    //接收人ID
    req.setUserid(dto.getUserId());
    req.setCreateTime(System.currentTimeMillis());
    req.setTitle(sdf.format(System.currentTimeMillis()) + "  " + dto.getTitle());
    //手机端打开页面
    req.setUrl(dto.getUrl());
    //PC端打开页面
    req.setPcUrl(dto.getUrl());
    //发送内容
    List<OapiWorkrecordAddRequest.FormItemVo> list = new ArrayList<>();
    OapiWorkrecordAddRequest.FormItemVo vo = new OapiWorkrecordAddRequest.FormItemVo();
    vo.setTitle(dto.getTitle());
    String content = "您有一个待办审核";
    if (StringUtils.isNotBlank(Html2PlainTextUtil.convert(Markdown2HtmlUtil.convert(dto.getDtl())))) {
        //将markdown格式转换为text格式
        content = Html2PlainTextUtil.convert(Markdown2HtmlUtil.convert(dto.getDtl()));
    }
    vo.setContent(content);
    list.add(vo);
    req.setFormItemList(list);
    //pc端打开方式,2 钉钉内打开,4 浏览器内打开
    req.setPcOpenType(4L);
    //流程业务id,避免多个业务冲突,根据发送人、接收人、业务id和业务来源确定唯一
    String bizId = String.valueOf(System.currentTimeMillis());
    if (StringUtils.isNotBlank(dto.getBizId())) {
        bizId = dto.getBizId();
    }
    req.setBizId(bizId);
    DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/workrecord/add");
    OapiWorkrecordAddResponse rsp = client.execute(req, getAccessToken());
    return RespVO.success(JsonUtil.getJsonByString(rsp.getBody()));
}

c、内部调用

@Override
public RespVO<Object> remindAudits(RemindAuditsDTO dto) {
    //判断有没有绑定钉钉
    String userId = getDingIdByAccountId(dto.getAccountId());
    if (StringUtils.isBlank(getDingIdByAccountId(dto.getAccountId()))) {
        return RespVO.success(RespVO.failure(ApiResultCode.G500, "j产品负责人未绑定钉钉,请联系产品负责人"));
    }
    String pcUrl = feUrl + "middle-office/personal-center";
    //流程业务id,避免多个业务冲突,根据发送人、接收人、业务id和业务来源确定唯一
    String bizId = getBizId(pcUrl, dto.getAccountId());
    //用户名
    String realName = getRealNameByAccountId(dto.getAccountId());
    Map<String, Object> mapParam = new HashMap<>();
    mapParam.put("user_id", userId);
    mapParam.put("title", dto.getTitle());
    mapParam.put("dtl", dto.getDtl());
    mapParam.put("url", pcUrl);
    mapParam.put("biz_id", bizId);
    String response = SendHttpsUtil.sendPostByMap(srvUrl + "v1/ding-talk/backlog/remind-audit-common", mapParam);
    JSONObject data = JSON.parseObject(response).getJSONObject("data");
    if (data.get("errcode").equals(0)) {
        if (StringUtils.isNotBlank(data.get("record_id").toString())) {
            //如果之前该需求已发送过待办了,新发送同一个需求时,将之前的待办任务取消
            GetApprovedDTO getApprovedDTO = new GetApprovedDTO();
            getApprovedDTO.setAccountId(dto.getAccountId());
            getApprovedDTO.setRecordId(dto.getRecordId());
            getAppRoved(getApprovedDTO);

            String recordId = data.get("record_id").toString();
            SysDtTaskRecPO dtTaskRec = new SysDtTaskRecPO(recordId, dto.getAccountId(), userId, realName, pcUrl, dto.getTitle(), dto.getDtl(), 1);
            iSysDtTaskRecService.save(dtTaskRec);
            SysDevDemandPO sysDevDemand = new SysDevDemandPO();
            sysDevDemand.setApvlRecordId(recordId);
            iSysDevDemandService.getBaseMapper().update(sysDevDemand, new UpdateWrapper<SysDevDemandPO>().eq("demand_id", dto.getRecordId()));
            return RespVO.success("发送成功");
        }
    }
    return RespVO.success(RespVO.failure(ApiResultCode.G1100, "发送失败"));
}

d、实现效果

img

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值