使用钉钉开源api发送钉钉工作消息

发布于:2025-07-25 ⋅ 阅读:(13) ⋅ 点赞:(0)

在工作管理系统场景中,上下级和不同部门之间常常有请假,餐补等流程操作,而这些操作通常需要人员手动进行,这里我们引入一个钉钉的api,可以基于钉钉来发送工作消息通知

1、导入钉钉sdk
<dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>alibaba-dingtalk-service-sdk</artifactId>
            <version>2.0.0</version>
        </dependency>
        
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>dingtalk</artifactId>
            <version>2.2.26</version>
        </dependency>
2、登录钉钉开发者后台,选择租户

3、点击右上角创建应用

4、申请开通工作消息API基础权限

5、获取企业AngentId并获取ClientID和ClientSecret,用于后续程序中获取token

6、配置clientId和clientSecret

7、进行api的调用和业务逻辑的开发(这里我封装了一个接口用于发送钉钉消息,方便前端调用)
@Tag(name = "钉钉-发送工作消息")
@RestController
@RequestMapping("/dingtalk")
@Validated
public class DingTalkController {

    @Resource
    private DingTalkService dingTalkService;
    @PostMapping("/sendSubmitMessage")
    @Operation(summary = "钉钉发送消息通知提交餐补证明")
/*    @PreAuthorize("@ss.hasPermission('dingtalk:hr:sendsubmitmsg')")*/
    public CommonResult<Boolean> sendSubmitMessage(@RequestBody List<Long> ids) {
        Boolean result = dingTalkService.sendSubmitMessageToUser(ids);
        return success(result);
    }

    @PostMapping("/sendReSubmitMessage")
    @Operation(summary = "钉钉发送消息通知重新提交餐补证明")
    /*@PreAuthorize("@ss.hasPermission('dingtalk:hr:sendremsg')")*/
    public CommonResult<Boolean> sendReSubmitMessage(@RequestParam Long poofId, @RequestParam String remark) {
        dingTalkService.sendReSubmitMessageToUser(poofId, remark);
        return success(true);
    }
}
这里我们以发送上传餐补证明消息给指定用户接口为例
@Service
@Slf4j
public class DingTalkServiceImpl implements DingTalkService {

    // 注入钉钉应用的 AppKey 和 AppSecret
    @Value("${justauth.type.DINGTALK.client-id}")
    private String clientId;

    @Value("${justauth.type.DINGTALK.client-secret}")
    private String clientSecret;

    private static final String DINGTALK_API_BASE_URL = "https://oapi.dingtalk.com";
    private static final String GET_TOKEN_URL = "/gettoken";

    @Resource
    private AdminUserApi adminUserApi;

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private MealAllowanceProofMapper mealAllowanceProofMapper;

    @Resource
    private MealAllowanceDataMapper mealAllowanceDataMapper;

    @Override
    public Boolean sendSubmitMessageToUser(List<Long> userIds) {
        /**
         * 发送钉钉消息
         * @param
         */
        //遍历用户id
        Boolean result = true;
        for (Long userId : userIds) {
            //通过用户id得到对应用户的钉钉id
            String userRemark = adminUserApi.getUserRemark(userId).replaceAll("\\D+", "");
            //通过用户id得到对应用户的餐补总金额
            BigDecimal totalAmount = mealAllowanceDataMapper.selectByUserId(userId).getTotalAmount();
            // 调用下方方法发送
            result = sendSingleUserNotice(userRemark, totalAmount);
            if (!result) {
                throw exception(DING_TALK_SEND_MESSAGE_ERROR);
            }
        }
        return result;
    }
发送消息体封装逻辑和消息外观构造:
@SneakyThrows
    private Boolean sendSingleUserNotice(String dingUserId, BigDecimal amount){
        //获取AccessToken
        String accessToken = getAccessToken();
        //"https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2"为钉钉发送消息的接口请求地址
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2");
        //构建消息请求体
        OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request();
        request.setAgentId(#自己企业的AngentId#);

        //设置发送给对应用户的钉钉id
        request.setUseridList(dingUserId);
        //不发给全体成员
        request.setToAllUser(false);

        //构建PC端和移动端的url跳转路径地址,点击路径可以跳转到提交餐补信息的平台
        String PcUrl = "http://effi.fzxs.com.cn:8089/overtime/meal-allowance";
        String mobileUrl = "dingtalk://dingtalkclient/page/link?url=" + URLEncoder.encode(PcUrl, "UTF-8") + "&pc_slide=false";
        
        // 使用ActionCard消息类型创建带按钮的消息
        OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
        msg.setMsgtype("action_card");
        
        // 创建ActionCard消息
        OapiMessageCorpconversationAsyncsendV2Request.ActionCard actionCard = new OapiMessageCorpconversationAsyncsendV2Request.ActionCard();
        
        // 设置消息标题
        actionCard.setTitle("💰 加班餐补通知");
        
        // 构建精美的Markdown内容
        StringBuilder content = new StringBuilder();
        content.append("## 你有新的加班餐补啦 \n\n");
        content.append("### **💰餐补金额: ¥").append(amount.toString()).append("**\n\n");
        content.append("**请及时上传:** 餐饮消费凭证截图,不要让hr小姐姐久等啦 \n\n");
        content.append("⚠️ **截止时间:** 2个工作日内,过期作废");
        
        actionCard.setMarkdown(content.toString());
        
        // 设置按钮布局为竖直排列
        actionCard.setBtnOrientation("0");
        
        // 创建按钮列表
        List<OapiMessageCorpconversationAsyncsendV2Request.BtnJsonList> btnList = new ArrayList<>();
        
        // 添加上传证明按钮
        OapiMessageCorpconversationAsyncsendV2Request.BtnJsonList uploadBtn = new OapiMessageCorpconversationAsyncsendV2Request.BtnJsonList();
        uploadBtn.setTitle("立即上传餐补证明");
        uploadBtn.setActionUrl(mobileUrl);
        btnList.add(uploadBtn);
        
        actionCard.setBtnJsonList(btnList);
        msg.setActionCard(actionCard);
        request.setMsg(msg);
        
        try {
            OapiMessageCorpconversationAsyncsendV2Response response = client.execute(request, accessToken);
            if (response.getErrcode() == 0) {
                log.info("[sendSingleUserNotice][发送餐补通知成功] userId={}, amount={}, taskId={}", 
                        dingUserId, amount, response.getTaskId());
                return true;
            } else {
                log.error("[sendSingleUserNotice][发送餐补通知失败] userId={}, errcode={}, errmsg={}", 
                         dingUserId, response.getErrcode(), response.getErrmsg());
                return false;
            }
        } catch (ApiException e) {
            log.error("[sendSingleUserNotice][发送餐补通知异常] userId={}, error={}", dingUserId, e.getMessage(), e);
            throw ServiceExceptionUtil.exception(DING_TALK_SEND_MESSAGE_ERROR);
        }
    }
    
    /**
     * 创建美观的钉钉卡片消息
     * 
     * @param title 卡片标题
     * @param content 卡片内容
     * @param pcUrl PC端链接
     * @param mobileUrl 移动端链接
     * @return ActionCard对象
     */
    private OapiMessageCorpconversationAsyncsendV2Request.ActionCard createBeautifulActionCard(
            String title, String content, String pcUrl, String mobileUrl) {
        
        OapiMessageCorpconversationAsyncsendV2Request.ActionCard actionCard = new OapiMessageCorpconversationAsyncsendV2Request.ActionCard();
        actionCard.setTitle(title);
        actionCard.setMarkdown(content);
        actionCard.setBtnOrientation("1");
        
        // 添加操作按钮
        List<OapiMessageCorpconversationAsyncsendV2Request.BtnJsonList> btnList = new ArrayList<>();
        
        // 移动端按钮
        OapiMessageCorpconversationAsyncsendV2Request.BtnJsonList mobileBtn = new OapiMessageCorpconversationAsyncsendV2Request.BtnJsonList();
        mobileBtn.setTitle("📱 移动端处理");
        mobileBtn.setActionUrl(mobileUrl);
        btnList.add(mobileBtn);
        
        // PC端按钮
        OapiMessageCorpconversationAsyncsendV2Request.BtnJsonList pcBtn = new OapiMessageCorpconversationAsyncsendV2Request.BtnJsonList();
        pcBtn.setTitle("💻 电脑端处理");
        pcBtn.setActionUrl(pcUrl);
        btnList.add(pcBtn);
        
        actionCard.setBtnJsonList(btnList);
        return actionCard;
    }
获取accessToken方法封装:
/**
     * 获取钉钉访问令牌
     * @return
     */
    public String getAccessToken() {
        try {
            String url = UriComponentsBuilder.fromHttpUrl(DINGTALK_API_BASE_URL + GET_TOKEN_URL)
                    .queryParam("appkey", clientId)
                    .queryParam("appsecret", clientSecret)
                    .toUriString();

            ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
            JSONObject result = JSON.parseObject(response.getBody());

            if (result.getInteger("errcode") != 0) {
                log.error("获取钉钉访问令牌失败:{}", result.getString("errmsg"));
                throw exception(AUTH_ACCESS_TOKEN_ERROR);
            }

            return result.getString("access_token");
        } catch (Exception e) {
            log.error("获取钉钉访问令牌异常",e);
            throw exception(AUTH_ACCESS_TOKEN_ERROR);
        }
    }


网站公告

今日签到

点亮在社区的每一天
去签到