您现在的位置是:首页 >学无止境 >ABAP 集成钉钉开放平台网站首页学无止境

ABAP 集成钉钉开放平台

Seele_1018 2024-06-17 10:43:02
简介ABAP 集成钉钉开放平台

SAP->钉钉审批集成流程图

在这里插入图片描述

钉钉开放平台下载证书

进入钉钉开放平台,找到钉钉被调用api的域名
钉钉获取应用token官方文档
请求方法处可以找到域名,直接通过域名访问api地址下载证书
域名:api.dingtalk.com
在这里插入图片描述
访问域名获取证书信息
在这里插入图片描述
在这里插入图片描述
有三层证书,每一层都需要单独下载导出到本地
在这里插入图片描述
在这里插入图片描述

SAP STRUST导入证书

进入sap系统输入事务码STRUST
按如下步骤以此导入之前下载的三个证书
在这里插入图片描述

SAP SM59新建sap与外部系统的http连接

事务码sm59
选中到外部服务器的http连接,再选中创建
在这里插入图片描述
参考如下信息创建
在这里插入图片描述
登录&安全性处,需要将安全协议选择【活动】,ssl证书选择刚刚钉钉证书导入的【标准】

如果证书无误,此时点击上方连接测试,即可收到2xx响应成功的反馈
此处如果提示ssl证书错误,重复前两部重新导入证书
在这里插入图片描述

SE37 封装通用请求函数

使用该函数避免相同调用接口代码重复使用,故封装成统一请求函数
通过SM59创建HTTP实例的目的也是为了将域名和请求地址解耦

FUNCTION ZDD_SEND_TO_DING .
*"----------------------------------------------------------------------
*"*"本地接口:
*"  IMPORTING
*"     REFERENCE(IV_PATH) TYPE  STRING OPTIONAL		接口后缀
*"     REFERENCE(IV_KEY) TYPE  STRING OPTIONAL
*"     REFERENCE(IV_BODY) TYPE  STRING OPTIONAL
*"     REFERENCE(IV_METHOD) TYPE  STRING OPTIONAL
*"  EXPORTING
*"     REFERENCE(EV_DATA)
*"     REFERENCE(EV_STATUS)
*"  TABLES
*"      IT_HEADER STRUCTURE  ZDDS001 OPTIONAL
*"      IT_PARAM STRUCTURE  ZDDS001 OPTIONAL
*"----------------------------------------------------------------------


*&---Global data declarations

  DATA: lr_http_client TYPE REF TO if_http_client,
        lv_url         TYPE string,
        lv_xstr        TYPE xstring,
        lv_len         TYPE i,
        lv_result      TYPE string.

  DATA: lv_key               TYPE string,
        lv_value             TYPE string,
        lv_entcode           TYPE string,
        lv_json              TYPE string,
        lr_json_serializer   TYPE REF TO cl_trex_json_serializer,
        lr_json_deserializer TYPE REF TO cl_trex_json_deserializer,
        lv_sign              TYPE string,
        lv_msg               TYPE string,
        lv_name              TYPE string,
        lv_path              TYPE string.

*&--- 根据SM59 创建 HTTP 对象实例
  cl_http_client=>create_by_destination(
    EXPORTING
      destination = 'DINGTALK'
    IMPORTING
      client =  lr_http_client
    EXCEPTIONS
      argument_not_found =  1
      destination_no_authority = 2
      destination_not_found = 3
      internal_error = 4
      plugin_not_active = 5
      OTHERS = 6
      ).

*&---- 设置PARAM
  lv_path = iv_path.
  LOOP AT it_param INTO DATA(ls_param).
    AT FIRST.
      lv_path = iv_path && '?'.
    ENDAT.

    lv_path = lv_path && ls_param-key && '=' && ls_param-value && '&'.
  ENDLOOP.
  lv_path = shift_right( val = iv_path sub = |&| ). " 去除末尾多余&

*&--- 补充后缀
  cl_http_utility=>set_request_uri(
   EXPORTING
     request = lr_http_client->request
     uri     = lv_path
     ).

*&---- 设置调用方法
  CALL METHOD lr_http_client->request->set_header_field
    EXPORTING
      name  = '~request_method'
      value = iv_method.

*&---- 设置调用方法
  CALL METHOD lr_http_client->request->set_header_field
    EXPORTING
      name  = 'Content-Type' ##NO_TEXT
      value = 'application/json'.

*&---- 设置HEADER
  LOOP AT it_header INTO DATA(ls_header).
    lv_key   = ls_header-key.
    lv_value = ls_header-value.
    CALL METHOD lr_http_client->request->set_header_field
      EXPORTING
        name  = lv_key ##NO_TEXT
        value = lv_value.
  ENDLOOP.

  lv_json = iv_body.
  CONDENSE lv_json.
  CALL FUNCTION 'SCMS_STRING_TO_XSTRING'
    EXPORTING
      text     = lv_json
      encoding = '4110'
    IMPORTING
      buffer   = lv_xstr
    EXCEPTIONS
      failed   = 1
      OTHERS   = 2.

  lr_http_client->request->set_data( data = lv_xstr ).

*&--- 数据发送
  CALL METHOD lr_http_client->send
    EXCEPTIONS
      http_communication_failure = 1
      http_invalid_state         = 2
      http_processing_failed     = 3
      http_invalid_timeout       = 4.

  IF sy-subrc <> 0.
    lr_http_client->get_last_error( IMPORTING message = ev_data ).
    ev_status = 'E'.
    RETURN.
  ENDIF.

*&--- 返回结果判断
  CALL METHOD lr_http_client->receive
    EXCEPTIONS
      http_communication_failure = 1
      http_invalid_state         = 2
      http_processing_failed     = 3.

  IF sy-subrc <> 0 .	" 连接异常
    lr_http_client->get_last_error( IMPORTING message = ev_data ).
    ev_status = 'E'.
    RETURN.
  ELSE.
    ev_data = lr_http_client->response->get_cdata( ).
    CALL METHOD lr_http_client->close.
    ev_status = 'S'.

  ENDIF.

ENDFUNCTION.

SE37 封装获取钉钉 token函数

获取token的前提是需要有相关权限,参考官方文档配置权限
钉钉获取应用token官方文档
获取token函数几乎在每个接口前均需调用,因为钉钉的鉴权机制要求调用其他接口前需传入token
新建一个钉钉应用接口的配置表,用于存储不同应用的token,因为频繁调用获取token接口将会被拦截,使用中间表可以降低访问频次同时也节省接口调用次数
在这里插入图片描述

FUNCTION zdd_get_token.
*"----------------------------------------------------------------------
*"*"本地接口:
*"  IMPORTING
*"     REFERENCE(IV_APPNAME) TYPE  ZEL_APPNAME
*"  EXPORTING
*"     REFERENCE(EV_TOKEN) TYPE  ZEL_TOKEN
*"----------------------------------------------------------------------

*----------------------------------------------------------------------*
* 结构声明类型/Structure type declaration
*----------------------------------------------------------------------*
  TYPES: BEGIN OF ty_request,
           appkey    TYPE string,
           appsecret TYPE string,
         END OF ty_request.

  TYPES: BEGIN OF ty_result,
           expirein    TYPE i,
           accesstoken TYPE string,
         END OF ty_result.

*----------------------------------------------------------------------*
* 变量定义/Global variable definition
*----------------------------------------------------------------------*
  DATA: ls_request TYPE ty_request,
        ls_result  TYPE ty_result,
        ls_zddt001 TYPE zddt001,
        lv_body    TYPE string,
        lv_path    TYPE string,
        lv_method  TYPE string,
        lv_result  TYPE string,
        lv_status  TYPE c.

  lv_path   = '/v1.0/oauth2/accessToken'. " 获取token
  lv_method = 'POST'.


  " 根据应用获取token信息
  SELECT SINGLE
    appname     , " 应用名称
    appkey      , " appKey
    appsecret   , " appSecret
    token       , " token
    overdate    , " token过期日
    overtime      " token过期时间
  FROM zddt001
  WHERE
    appname = @iv_appname
  INTO CORRESPONDING FIELDS OF @ls_zddt001.

  " 校验过期日
  " 钉钉有限额,且频繁访问token会被拦截
  IF sy-datum <= ls_zddt001-overdate AND sy-uzeit < ls_zddt001-overtime.
    " 使用已保存的token
    ev_token = ls_zddt001-token.
  ELSE.
    " token已过期则重新获取
    " 结构转换json
    MOVE-CORRESPONDING ls_zddt001 TO ls_request.
    lv_body = /ui2/cl_json=>serialize(
      data = ls_request
      compress = abap_true
      pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).

    " 替换请求字符
    REPLACE ALL OCCURRENCES OF 'appkey' IN lv_body WITH 'appKey'.
    REPLACE ALL OCCURRENCES OF 'appsecret' IN lv_body WITH 'appSecret'.

    CALL FUNCTION 'ZDD_SEND_TO_DING'
      EXPORTING
        iv_path   = lv_path
        iv_body   = lv_body
        iv_method = lv_method
      IMPORTING
        ev_data   = lv_result
        ev_status = lv_status.
    IF lv_status = 'E'.
      " 异常处理
    ELSE.
      " 返回成功
      " 解析json
      /ui2/cl_json=>deserialize(
        EXPORTING
          json = lv_result
          CHANGING data = ls_result ).

      IF ls_result IS INITIAL.
        " 异常处理
      ELSE.
        " 更新过期日
        IF sy-uzeit > '220000'. " 跨日
          ls_zddt001-overtime = '000000'.
          ls_zddt001-overdate = sy-datum + 1.
        ELSE.
          ls_zddt001-overtime = sy-uzeit + ( 60 * 90 ). " 设置逾期时间90分钟后(官网是2小时,稍微提前一点)
          ls_zddt001-overdate = sy-datum.
        ENDIF.
        ls_zddt001-token = ls_result-accesstoken.
        MODIFY zddt001 FROM ls_zddt001.
      ENDIF.
    ENDIF.

    ev_token = ls_result-accesstoken.
  ENDIF.

ENDFUNCTION.

创建钉钉待办任务示例

到上一步为止基本的框架就搭建好了,后面的步骤就是封装其他功能接口做对接,以创建钉钉待办为例子
创建钉钉待办任务

FUNCTION zdd_create_task.
*"----------------------------------------------------------------------
*"*"本地接口:
*"  EXPORTING
*"     REFERENCE(EV_ID) TYPE  STRING
*"----------------------------------------------------------------------

*----------------------------------------------------------------------*
* 结构声明类型/Structure type declaration
*----------------------------------------------------------------------*
  " 请求body
  TYPES: BEGIN OF ty_request,
           subject     TYPE string,  " 主题
           description TYPE string,  " 描述
           duetime     TYPE p,       " 逾期时间
         END OF ty_request.

  " 响应
  TYPES: BEGIN OF ty_result,
           id TYPE string,        " 待办id
         END OF ty_result.

*----------------------------------------------------------------------*
* 变量定义/Global variable definition
*----------------------------------------------------------------------*
  DATA: lt_header  TYPE TABLE OF zdds001,
        ls_header  TYPE zdds001,
        ls_request TYPE ty_request,
        ls_result  TYPE ty_result,
        lv_token   TYPE zel_token,
        lv_body    TYPE string,
        lv_path    TYPE string,
        lv_method  TYPE string,
        lv_result  TYPE string,
        lv_status  TYPE c.

  " 调用的路径可以再设计自建表存储,暂时没想好用哪些字段
  lv_path   = '/v1.0/todo/users/PUoiinWIpa2yH2ymhiiGiP6g/tasks?operatorId=PUoiinWIpxxx'.
  lv_method = 'POST'.

  " 请求body
  ls_request-subject      = '待办事项测试'.
  ls_request-description  = '你有一个待办事项'.
  ls_request-duetime      = 1684287389233.

  " 请求token
  CALL FUNCTION 'ZDD_GET_TOKEN'
    EXPORTING
      iv_appname = 'DEMO'
    IMPORTING
      ev_token   = lv_token.

  " token填入header
  ls_header-key   = 'x-acs-dingtalk-access-token'.
  ls_header-value = lv_token.
  APPEND ls_header TO lt_header.

  " 请求body转json
  lv_body = /ui2/cl_json=>serialize(
    data = ls_request
    compress = abap_true
    pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).
  REPLACE ALL OCCURRENCES OF 'duetime' IN lv_body WITH 'dueTime'.

  " 发送钉钉请求待办
  CALL FUNCTION 'ZDD_SEND_TO_DING'
    EXPORTING
      iv_path   = lv_path
      iv_body   = lv_body
      iv_method = lv_method
    IMPORTING
      ev_data   = lv_result
      ev_status = lv_status
    TABLES
      it_header = lt_header.
  IF lv_status = 'E'.
    " SM59连接异常
  ELSE.
    " 返回成功
    " 解析json
    /ui2/cl_json=>deserialize(
      EXPORTING
        json = lv_result
        CHANGING data = ls_result ).
    IF ls_result IS INITIAL.
      " 未正常解析,异常
    ELSE.
      ev_id = ls_result-id.
    ENDIF.

  ENDIF.


ENDFUNCTION.

调用结果

PS:钉钉待办事项有新版和旧版,下图为旧版待办事项,如果为新版待办事项可能显示不出来,在其他栏位藏着
在这里插入图片描述

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。