ABAP 集成钉钉开放平台

本文介绍了如何在SAP系统中通过SAPSTRUST导入钉钉API证书,以及如何使用SE37编程封装通用请求函数(如ZDD_SEND_TO_DING和ZDD_GET_TOKEN)以简化钉钉API调用,包括创建钉钉待办任务的示例。
摘要由CSDN通过智能技术生成
SAP->钉钉审批集成流程图
钉钉开放平台下载证书

进入钉钉开放平台,找到钉钉被调用api的域名
钉钉获取应用token官方文档
请求方法处可以找到域名,直接通过域名访问api地址下载证书
域名:api.dingtalk.com

SAP STRUST导入证书

进入sap系统输入事务码STRUST
按如下步骤以此导入之前下载证书

中间有遇到证书有问题,找了SAP的basis后解决该问题,参考SAP note510007

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:钉钉待办事项有新版和旧版,下图为旧版待办事项,如果为新版待办事项可能显示不出来,在其他栏位藏着

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值