Skip to content

日志查询程序

该程序用于查询接口日志,并已加入PO日志查询(仅限系统内存储的日志数据),如有自定义日志数据想要展示,需要继承本地接口LIF_LOG,并实施接口方法。

报表执行时会检索实例类,因此无需手工创建新的日志实例(CREATE OBJECT,NEW #()等)。

示例代码
*&---------------------------------------------------------------------*
*& REPORT ZLOGREPORT
*&---------------------------------------------------------------------*
*& V2 - XIAOFENG - 2022-09-26 14:11:17
*& 上个项目的那版写得太复杂,无法扩展,这次乘机简化下代码
*& 使用步骤:
*&  1 代码复制粘贴
*&  2 创建9000屏幕,添加一个容器(属性改自适应),命名CON_9000
*&  3 创建状态栏STATUS
*&   3.1 添加三个退出按钮(&F03\&F15\&F12)
*&   3.2 添加REDO按钮,用于错误数据重处理,没有的话忽略即可
*&  4 新建实施类,继承接口LIF_LOG,把接口方法实施即可
*&   4.1 GET_LOG_KIND,为每种日志设定唯一名称
*&   4.2 GET_DATA,查询日志数据,写入到GT_LOG和GT_LOG_RAWDATA中(弹窗用)
*&   4.3 REDO,错误重处理
*&---------------------------------------------------------------------*
REPORT ZLOGREPORT

*&---------------------------------------------------------------------*
*& 本地类声明
*&---------------------------------------------------------------------*
CLASS lcl_progress DEFINITION DEFERRED. " 进度条
" 数据处理相关
INTERFACE lif_unit_parser DEFERRED.
CLASS lcl_data_unit_parser DEFINITION DEFERRED.
CLASS lcl_json_unit_parser DEFINITION DEFERRED.
CLASS lcl_xml_unit_parser DEFINITION DEFERRED.
CLASS lcl_unit_helper DEFINITION DEFERRED.
" ALV相关
INTERFACE lif_alv DEFERRED.
CLASS lcl_alv_logh DEFINITION DEFERRED. " 日志抬头数据展示
CLASS lcl_alv_unit DEFINITION DEFERRED. " 单元数据展示
CLASS lcl_alv_flat DEFINITION DEFERRED. " 扁平结构展示
" 日志处理相关
INTERFACE lif_log DEFERRED.
CLASS lcl_log_po DEFINITION DEFERRED. " PO日志
CLASS lcl_log_custom DEFINITION DEFERRED. " 自定义日志

*&---------------------------------------------------------------------*
*& 类型定义
*&---------------------------------------------------------------------*
" 数据单元,简单来说,一个值就是一个单元
TYPES:
  BEGIN OF ty_unit,
    path  TYPE string, " 路径
    key   TYPE string, " 键
    value TYPE string, " 值
  END OF ty_unit.
TYPES tt_unit TYPE STANDARD TABLE OF ty_unit WITH EMPTY KEY.
" 扁平结构信息
TYPES:
  BEGIN OF ty_flatdt,
    struct   TYPE string, " 结构
    field    TYPE string, " 字段
    position TYPE i, " 位置
  END OF ty_flatdt.
TYPES tt_flatdt TYPE STANDARD TABLE OF ty_flatdt WITH EMPTY KEY.
" 数据单元扩展
TYPES:
  BEGIN OF ty_unitex, " 创建扁平结构用到
    struct TYPE string, " 结构
    path   TYPE string, " 路径
    key    TYPE string, " 键
    value  TYPE string, " 值
  END OF ty_unitex.
TYPES tt_unitex TYPE STANDARD TABLE OF ty_unitex WITH EMPTY KEY.
" 日志抬头信息
TYPES:
  BEGIN OF ty_log_head,
    uuid      TYPE sysuuid_c32, " 日志ID
    zitem     TYPE i, " 序号
    log_kind  TYPE c LENGTH 20, " 日志种类,如PI日志,日志等多种
    intf_id   TYPE c LENGTH 30, " 接口标识
    func_name TYPE rs38l_fnam, " 函数名
    func_text TYPE c LENGTH 40, " 函数描述
    ztext     TYPE c LENGTH 255, " 附加信息,有些接口会在记录的时候附加一些描述说明
    io_flag   TYPE c LENGTH 10, " 入参或出参
    zconut    TYPE i, " 条目统计
    zsender   TYPE c LENGTH 20, " 发送方
    zreceiver TYPE c LENGTH 20, " 接受方
    zraw_icon TYPE icon_d, " 双击展示原始数据
    zdate     TYPE datum, " 日期
    ztime     TYPE uzeit, " 时间
    zuser     TYPE uname, " 用户
    mtype     TYPE bapi_mtype, " 状态
    msg       TYPE bapi_msg, " 消息文本
    rcol      TYPE c LENGTH 4, " 行颜色
    t_scol    TYPE lvc_t_scol, " 单元格颜色
    t_styl    TYPE lvc_t_styl, " 单元格样式
  END OF ty_log_head.
" 日志数据
TYPES:
  BEGIN OF ty_log_data,
*    r_data     TYPE REF TO data, " 日志数据
    t_unit     TYPE tt_unit, " 数据单元
    r_flatdata TYPE REF TO data, " 扁平结构数据
    t_flatdt   TYPE tt_flatdt, " 扁平结构
  END OF ty_log_data.
TYPES:
  BEGIN OF ty_log.
    INCLUDE TYPE ty_log_head.
    INCLUDE TYPE ty_log_data.
TYPES:
  END OF ty_log.
TYPES tt_log TYPE STANDARD TABLE OF ty_log .
" 日志原始数据
TYPES:
  BEGIN OF ty_log_rawdata,
    uuid    TYPE sysuuid_c32, " 日志ID
    zitem   TYPE i, " 序号
    zformat TYPE c LENGTH 10, " 原始数据格式
    zraw    TYPE string, " 原始数据
    zrawx   TYPE xstring, " 原始数据
  END OF ty_log_rawdata.
TYPES tt_log_rawdata TYPE STANDARD TABLE OF ty_log_rawdata .

TYPES tt_uuid TYPE STANDARD TABLE OF ty_log_head-uuid.
TYPES tt_uuid_opt TYPE RANGE OF ty_log_head-uuid.

*&---------------------------------------------------------------------*
*& 数据声明
*&---------------------------------------------------------------------*
DATA gt_log TYPE tt_log. " 日志数据
DATA gt_log_rawdata TYPE tt_log_rawdata. " 日志数据
DATA gt_log_impl TYPE STANDARD TABLE OF REF TO lif_log.

DATA go_alv TYPE REF TO lif_alv. " ALV对象
DATA go_container TYPE REF TO cl_gui_container.
DATA go_grid TYPE REF TO cl_gui_alv_grid.

DATA:
  BEGIN OF gs_fieldname,
    fdlk  TYPE string VALUE 'FDLK', " FLAT_DATA_LINE_KIND
    path  TYPE string VALUE 'PATH',
    field TYPE string VALUE '__FIELD',
  END OF gs_fieldname.

DATA:
  BEGIN OF gs_fdlk,
    header TYPE string VALUE 'HEADER',
    data   TYPE string VALUE 'DATA',
  END OF gs_fdlk.

DATA:
  BEGIN OF gs_color,
    blue        TYPE lvc_s_colo,
    yellow      TYPE lvc_s_colo,
    green       TYPE lvc_s_colo,
    red         TYPE lvc_s_colo,
    dept_blue   TYPE lvc_s_colo,
    dept_yellow TYPE lvc_s_colo,
    dept_green  TYPE lvc_s_colo,
    dept_red    TYPE lvc_s_colo,
  END OF gs_color.

*&---------------------------------------------------------------------*
*& 选择屏幕
*&---------------------------------------------------------------------*
TABLES sxmspmast. " PO相关
TABLES sxmspemas. " PO相关
" 选择条件
DATA:
  BEGIN OF gs_selection.
    INCLUDE TYPE ty_log_head.
DATA:
    intfid TYPE char30, " 自定义日志接口标识
*    INCLUDE TYPE ty_unit.
    field TYPE char30, " TY_UNIT-FIELD,STRING类型的,不能使用
    value TYPE char30. " TY_UNIT-VALUE,STRING类型的,不能使用
DATA:
  END OF gs_selection.
" 自定义选择条件
SELECTION-SCREEN BEGIN OF BLOCK b900 WITH FRAME TITLE b900.
  PARAMETERS p_custom TYPE xflag AS CHECKBOX DEFAULT 'X'.
  SELECT-OPTIONS s_intfid FOR gs_selection-intfid.
SELECTION-SCREEN END OF BLOCK b900.
" 通用选择条件
SELECTION-SCREEN BEGIN OF BLOCK b100 WITH FRAME TITLE b100.
  SELECT-OPTIONS s_uuid FOR gs_selection-uuid.
  SELECT-OPTIONS s_func FOR gs_selection-func_name MATCHCODE OBJECT sfunc_modules.
  SELECT-OPTIONS s_snd FOR gs_selection-zsender.
  SELECT-OPTIONS s_rcv FOR gs_selection-zreceiver.
  SELECT-OPTIONS s_user FOR gs_selection-zuser MATCHCODE OBJECT user_comp.
  SELECT-OPTIONS s_date FOR gs_selection-zdate NO-EXTENSION OBLIGATORY DEFAULT sy-datum.
  SELECT-OPTIONS s_time FOR gs_selection-ztime NO-EXTENSION.
  SELECT-OPTIONS s_mtype FOR gs_selection-mtype.
  " PO选择条件
  SELECTION-SCREEN BEGIN OF BLOCK b102 WITH FRAME TITLE b102.
    PARAMETERS p_po TYPE xflag AS CHECKBOX DEFAULT 'X'.
    SELECT-OPTIONS s_siname FOR sxmspemas-ob_name.
  SELECTION-SCREEN END OF BLOCK b102.
  " 扩展选择条件
  SELECTION-SCREEN BEGIN OF BLOCK b101 WITH FRAME TITLE b101.
    SELECT-OPTIONS s_field FOR gs_selection-field.
    SELECT-OPTIONS s_value FOR gs_selection-value.
  SELECTION-SCREEN END OF BLOCK b101.
SELECTION-SCREEN END OF BLOCK b100.
" 展示方式
SELECTION-SCREEN BEGIN OF BLOCK b200 WITH FRAME TITLE b200.
  PARAMETERS p_logh RADIOBUTTON GROUP rg1 TYPE xflag DEFAULT 'X'.
  PARAMETERS p_flat RADIOBUTTON GROUP rg1 TYPE xflag.
  PARAMETERS p_unit RADIOBUTTON GROUP rg1 TYPE xflag.
  SELECTION-SCREEN BEGIN OF BLOCK b201 WITH FRAME TITLE b201.
    PARAMETERS p_ctl1 TYPE xflag AS CHECKBOX. " 紧凑表示
    PARAMETERS p_ctl2 TYPE xflag AS CHECKBOX DEFAULT 'X'.
    PARAMETERS p_ctl3 TYPE xflag AS CHECKBOX DEFAULT 'X'.
    PARAMETERS p_ctl4 TYPE xflag AS CHECKBOX DEFAULT 'X'.
    PARAMETERS p_ctl5 TYPE i DEFAULT 1.
  SELECTION-SCREEN END OF BLOCK b201.
SELECTION-SCREEN END OF BLOCK b200.

*&---------------------------------------------------------------------*
*& 本地类定义
*&---------------------------------------------------------------------*
*&---------------------------------------------------------------------*
*& CLASS LCL_PROGRESS
*&---------------------------------------------------------------------*
CLASS lcl_progress DEFINITION FINAL.
  PUBLIC SECTION.
    DATA m_total TYPE i.
    DATA m_current TYPE i.
    DATA m_time_start TYPE timestampl.
    DATA m_time TYPE timestampl.
    DATA m_interval TYPE i.
    DATA m_name TYPE string .
    METHODS start " 初始化
      IMPORTING total         TYPE i OPTIONAL
      RETURNING VALUE(result) TYPE REF TO lcl_progress.
    METHODS next " 增加一点进度
      IMPORTING delta         TYPE i DEFAULT 1
      RETURNING VALUE(result) TYPE REF TO lcl_progress.
    METHODS finish " 结束
      RETURNING VALUE(result) TYPE REF TO lcl_progress.
    METHODS show " 左下角消息
      IMPORTING text TYPE string OPTIONAL.
    METHODS percentage " 当前进度
      RETURNING VALUE(result) TYPE f.
    METHODS duration  " 运行时间
      RETURNING VALUE(result) TYPE f.
    CLASS-METHODS step " 展示单个条目
      IMPORTING text TYPE string OPTIONAL.
ENDCLASS.
*&---------------------------------------------------------------------*
*& CLASS (IMPLEMENTATION) LCL_PROGRESS
*&---------------------------------------------------------------------*
CLASS lcl_progress IMPLEMENTATION.
*&---------------------------------------------------------------------*
*& START
*&---------------------------------------------------------------------*
  METHOD start.
    result = me.
    m_total = total.
    IF m_interval = 0.
      m_interval = 1. " 不加限制反而影响性能
    ENDIF.
    m_current = 0.
    GET TIME STAMP FIELD m_time_start.
    m_time = m_time_start.
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& NEXT
*&---------------------------------------------------------------------*
  METHOD next.
    result = me.
    m_current = m_current + delta.

    DATA l_time TYPE timestampl.
    GET TIME STAMP FIELD l_time.
    IF m_current = m_total.
      m_time = l_time.
    ENDIF.

    IF m_time <= l_time.
      show( ).
      IF m_interval > 0.
        m_time = cl_abap_tstmp=>add( tstmp = l_time
                                     secs  = m_interval ).
      ELSE.
        m_time = l_time.
      ENDIF.
    ENDIF.
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& FINISH
*&---------------------------------------------------------------------*
  METHOD finish.
    result = me.
    GET TIME STAMP FIELD m_time.
    show( ).
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& SHOW
*&---------------------------------------------------------------------*
  METHOD show.
    DATA(l_text) = text.
    IF l_text IS INITIAL.
      l_text = |{ percentage( ) DECIMALS = 0 }% [ {
          m_current NUMBER = USER } / { m_total NUMBER = USER } ] {
          duration( ) DECIMALS = 0 }S|.
      IF m_name IS NOT INITIAL.
        l_text = |{ m_name } : { l_text }|.
      ENDIF.
    ENDIF.
    IF sy-batch = ''.
      CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
        EXPORTING
          text = l_text.
    ELSE.
      MESSAGE l_text TYPE 'S'.
    ENDIF.
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& PERCENTAGE
*&---------------------------------------------------------------------*
  METHOD percentage.
    IF m_total <> 0.
      result = m_current / m_total * 100.
    ENDIF.
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& DURATION
*&---------------------------------------------------------------------*
  METHOD duration.
    result = cl_abap_tstmp=>subtract( tstmp1 = m_time tstmp2 = m_time_start ).
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& STEP
*&---------------------------------------------------------------------*
  METHOD step.
    CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
      EXPORTING
        text = text.
  ENDMETHOD.

ENDCLASS.
*&---------------------------------------------------------------------*
*& LIF_UNIT_PARSER DEFINITION
*&---------------------------------------------------------------------*
INTERFACE lif_unit_parser.
  METHODS parse RETURNING VALUE(rt_result) TYPE tt_unit.
ENDINTERFACE.
*&---------------------------------------------------------------------*
*& LCL_DATA_UNIT_PARSER DEFINITION
*&---------------------------------------------------------------------*
CLASS lcl_data_unit_parser DEFINITION.
  PUBLIC SECTION.
    INTERFACES lif_unit_parser.
    METHODS constructor IMPORTING ir_data TYPE REF TO data.
  PRIVATE SECTION.
    DATA mr_data TYPE REF TO data.
    DATA mt_unit TYPE tt_unit.
    METHODS _parse
      IMPORTING ir_data      TYPE REF TO data
                i_path       TYPE string DEFAULT '$'
                i_name       TYPE string OPTIONAL
                io_typedescr TYPE REF TO cl_abap_typedescr OPTIONAL.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_JSON_UNIT_PARSER DEFINITION
*&---------------------------------------------------------------------*
CLASS lcl_json_unit_parser DEFINITION.
  PUBLIC SECTION.
    INTERFACES lif_unit_parser.
    METHODS constructor IMPORTING i_json TYPE string.
  PRIVATE SECTION.
    DATA m_json TYPE string.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_XML_UNIT_PARSER DEFINITION
*&---------------------------------------------------------------------*
CLASS lcl_xml_unit_parser DEFINITION.
  PUBLIC SECTION.
    INTERFACES lif_unit_parser.
    METHODS constructor IMPORTING i_xml TYPE xstring.
  PRIVATE SECTION.
    DATA m_xml TYPE xstring.
    DATA mt_unit TYPE tt_unit.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_UNIT_HELPER DEFINITION
*&---------------------------------------------------------------------*
CLASS lcl_unit_helper DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS parse_data
      IMPORTING ir_data          TYPE REF TO data
      RETURNING VALUE(rt_result) TYPE tt_unit.
    CLASS-METHODS parse_json
      IMPORTING i_json           TYPE string
      RETURNING VALUE(rt_result) TYPE tt_unit.
    CLASS-METHODS parse_xml
      IMPORTING i_xml            TYPE xstring
      RETURNING VALUE(rt_result) TYPE tt_unit.
    CLASS-METHODS generate_flatdata
      IMPORTING it_unit        TYPE tt_unit
                i_compact      TYPE xfeld OPTIONAL
      EXPORTING et_flatdt      TYPE tt_flatdt
      RETURNING VALUE(rr_data) TYPE REF TO data.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_DATA_UNIT_PARSER IMPLEMENTATION
*&---------------------------------------------------------------------*
CLASS lcl_data_unit_parser IMPLEMENTATION.
*&---------------------------------------------------------------------*
*& METHOD CONSTRUCTOR
*&---------------------------------------------------------------------*
  METHOD constructor.

    mr_data = ir_data.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& METHOD LIF_UNIT_PARSER~PARSE
*&---------------------------------------------------------------------*
  METHOD lif_unit_parser~parse.

    _parse( mr_data ).
    rt_result = mt_unit.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& METHOD _PARSE
*&---------------------------------------------------------------------*
  METHOD _parse.

    " 简单来说,就是获取数据的每个字段值,并记录对应路径

    " 数据解析相关参数
    DATA lo_typedescr TYPE REF TO cl_abap_typedescr.
    DATA lo_tabledescr TYPE REF TO cl_abap_tabledescr.
    DATA lo_structdescr TYPE REF TO cl_abap_structdescr.
    DATA lt_component TYPE cl_abap_structdescr=>component_table.
    DATA lr_component TYPE REF TO cl_abap_structdescr=>component.

    " 动态参数
    FIELD-SYMBOLS <fs_table> TYPE STANDARD TABLE.
    FIELD-SYMBOLS <fs_structure> TYPE any.
    FIELD-SYMBOLS <fs_field> TYPE any.
    " 动态引用参数
    FIELD-SYMBOLS <fs_ref> TYPE REF TO data.

    " 数据校验
    CHECK ir_data IS BOUND.
    IF io_typedescr IS BOUND.
      lo_typedescr = io_typedescr.
    ELSE.
      lo_typedescr = cl_abap_typedescr=>describe_by_data_ref( ir_data ).
    ENDIF.

    " 路径处理
    DATA l_pathname TYPE string.
    IF i_name IS INITIAL.
      l_pathname = i_path.
    ELSE.
      l_pathname = |{ i_path }.{ i_name }|.
    ENDIF.

    " 主要处理表、结构、数据元素、引用四种类型
    " 类和接口无需处理
    CASE lo_typedescr->kind.
      WHEN cl_abap_typedescr=>kind_table. " 表格类型
        ASSIGN ir_data->* TO <fs_table>.
        lo_tabledescr ?= lo_typedescr.
        lo_typedescr = lo_tabledescr->get_table_line_type( ). " 表行类型
        DATA l_tabix TYPE sy-tabix. " 所在行
        IF lo_typedescr->kind = cl_abap_typedescr=>kind_ref.
          LOOP AT <fs_table> ASSIGNING <fs_ref>.
            l_tabix = sy-tabix.
            _parse( " 引用类型直接迭代
                ir_data = <fs_ref>
                i_path = |{ l_pathname }[{ l_tabix }]| ).
          ENDLOOP.
        ELSE. " 非引用类型继续分析表行类型
          lo_structdescr ?= lo_typedescr.
          lt_component = lo_structdescr->get_components( ).
          LOOP AT <fs_table> ASSIGNING <fs_structure>.
            l_tabix = sy-tabix.
            LOOP AT lt_component REFERENCE INTO lr_component.
              ASSIGN COMPONENT lr_component->name OF STRUCTURE <fs_structure> TO <fs_field>.
              IF lr_component->type IS BOUND.
                _parse( " 如果行结构字段也是引用类型,继续迭代
                    ir_data = REF #( <fs_field> )
                    i_path = |{ l_pathname }[{ l_tabix }]|
                    i_name = lr_component->name
                    io_typedescr = lr_component->type ).
              ELSEIF <fs_field> IS ASSIGNED.
                INSERT VALUE #(
                  path = |{ l_pathname }[{ l_tabix }]|
                  key = lr_component->name
                  value = <fs_field>
                ) INTO TABLE mt_unit.
              ENDIF.
            ENDLOOP.
          ENDLOOP.
        ENDIF.

      WHEN cl_abap_typedescr=>kind_struct. " 结构类型
        ASSIGN ir_data->* TO <fs_structure>.
        lo_structdescr ?= lo_typedescr.
        lt_component = lo_structdescr->get_components( ).
        LOOP AT lt_component REFERENCE INTO lr_component.
          IF lr_component->type IS BOUND. " 非数据元素类型,继续迭代
            IF lr_component->type->kind = cl_abap_typedescr=>kind_ref.
              " 引用类型处理
              ASSIGN COMPONENT lr_component->name OF STRUCTURE <fs_structure> TO <fs_ref>.
              _parse(
                  ir_data = <fs_ref>
                  i_path = l_pathname
                  i_name = lr_component->name ).
            ELSE.
              " 非引用类型数据
              ASSIGN COMPONENT lr_component->name OF STRUCTURE <fs_structure> TO <fs_field>.
              _parse(
                  ir_data = REF #( <fs_field> )
                  i_path = l_pathname
                  i_name = lr_component->name ).
            ENDIF.
          ELSE.
            ASSIGN COMPONENT lr_component->name OF STRUCTURE <fs_structure> TO <fs_field>.
            INSERT VALUE #(
              path = l_pathname
              key = lr_component->name
              value = <fs_field>
            ) INTO TABLE mt_unit.
          ENDIF.
        ENDLOOP.

      WHEN cl_abap_typedescr=>kind_elem. " 数据元素类型
        ASSIGN ir_data->* TO <fs_field>.
        INSERT VALUE #(
          path = i_path
          key = i_name
          value = <fs_field>
        ) INTO TABLE mt_unit.

      WHEN cl_abap_typedescr=>kind_ref. " 引用类型,继续迭代
        ASSIGN ir_data->* TO <fs_ref>.
        _parse(
            ir_data = <fs_ref>
            i_path = i_path
            i_name = i_name ).
    ENDCASE.

  ENDMETHOD.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_JSON_UNIT_PARSER IMPLEMENTATION
*&---------------------------------------------------------------------*
CLASS lcl_json_unit_parser IMPLEMENTATION.
*&---------------------------------------------------------------------*
*& METHOD CONSTRUCTOR
*&---------------------------------------------------------------------*
  METHOD constructor.

    m_json = i_json.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& METHOD LIF_UNIT_PARSER~PARSE
*&---------------------------------------------------------------------*
  METHOD lif_unit_parser~parse.

    DATA lo_parser TYPE REF TO lif_unit_parser.
    lo_parser ?= NEW lcl_data_unit_parser( /ui2/cl_json=>generate( m_json ) ).
    rt_result = lo_parser->parse( ).

  ENDMETHOD.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_XML_UNIT_PARSER IMPLEMENTATION
*&---------------------------------------------------------------------*
CLASS lcl_xml_unit_parser IMPLEMENTATION.
*&---------------------------------------------------------------------*
*& METHOD CONSTRUCTOR
*&---------------------------------------------------------------------*
  METHOD constructor.

    m_xml = i_xml.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& METHOD LIF_UNIT_PARSER~PARSE
*&---------------------------------------------------------------------*
  METHOD lif_unit_parser~parse.

    DATA lt_path TYPE STANDARD TABLE OF string.
    DATA l_name TYPE string.

    DATA(lo_reader) = cl_sxml_string_reader=>create( m_xml ).
    DATA(lo_node) = lo_reader->read_next_node( ).
    WHILE lo_reader->node_type <> if_sxml_node=>co_nt_final.
      CASE lo_reader->node_type.
        WHEN if_sxml_node=>co_nt_element_open.
          IF l_name IS NOT INITIAL.
            INSERT l_name INTO TABLE lt_path.
          ENDIF.
          l_name = CAST if_sxml_open_element( lo_node )->qname-name.
        WHEN if_sxml_node=>co_nt_element_close.
          CLEAR l_name.
          IF lt_path IS NOT INITIAL.
            DATA(l_tabix) = lines( lt_path ).
            l_name = lt_path[ l_tabix ].
            DELETE lt_path INDEX l_tabix.
          ENDIF.
        WHEN if_sxml_node=>co_nt_value.
          DATA ls_unit TYPE ty_unit.
          CLEAR ls_unit.
          CONCATENATE LINES OF lt_path INTO ls_unit-path SEPARATED BY '.'.
          ls_unit-key = l_name.
          ls_unit-value = CAST if_sxml_value( lo_node )->get_value( ).
          INSERT ls_unit INTO TABLE mt_unit.
      ENDCASE.
      lo_node = lo_reader->read_next_node( ).
    ENDWHILE.

    rt_result = mt_unit.

  ENDMETHOD.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_UNIT_HELPER IMPLEMENTATION
*&---------------------------------------------------------------------*
CLASS lcl_unit_helper IMPLEMENTATION.
*&---------------------------------------------------------------------*
*& METHOD PARSE_DATA
*&---------------------------------------------------------------------*
  METHOD parse_data.

    DATA lo_parser TYPE REF TO lif_unit_parser.
    lo_parser ?= NEW lcl_data_unit_parser( ir_data ).
    rt_result = lo_parser->parse( ).

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& METHOD PARSE_JSON
*&---------------------------------------------------------------------*
  METHOD parse_json.

    DATA lo_parser TYPE REF TO lif_unit_parser.
    lo_parser ?= NEW lcl_json_unit_parser( i_json ).
    rt_result = lo_parser->parse( ).

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& METHOD PARSE_XML
*&---------------------------------------------------------------------*
  METHOD parse_xml.

    DATA lo_parser TYPE REF TO lif_unit_parser.
    lo_parser ?= NEW lcl_xml_unit_parser( i_xml ).
    rt_result = lo_parser->parse( ).

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& METHOD GENERATE_FLATDATA
*& 将层级数据分析并生成平面数据
*&---------------------------------------------------------------------*
  METHOD generate_flatdata.

    CHECK it_unit IS NOT INITIAL.

    " 提取结构
    DATA lt_flatdt TYPE tt_flatdt.
    DATA ls_flatdt TYPE ty_flatdt.
    DATA lt_unitex TYPE tt_unitex.
    DATA ls_unitex TYPE ty_unitex.
    LOOP AT it_unit REFERENCE INTO DATA(lr_unit).
      CLEAR ls_flatdt.
      ls_flatdt-struct = lr_unit->path.
      ls_flatdt-field = lr_unit->key.
      REPLACE ALL OCCURRENCES OF REGEX '\[[0-9]*\]' IN ls_flatdt-struct WITH ''. " 去除表格索引
      INSERT ls_flatdt INTO TABLE lt_flatdt.
      CLEAR ls_unitex.
      ls_unitex-struct = ls_flatdt-struct.
      ls_unitex-path = lr_unit->path.
      ls_unitex-key = lr_unit->key.
      ls_unitex-value = lr_unit->value.
      INSERT ls_unitex INTO TABLE lt_unitex.
    ENDLOOP.
    SORT lt_flatdt BY struct field.
    DELETE ADJACENT DUPLICATES FROM lt_flatdt COMPARING struct field.

    " 计算每组结构最大字段数,并设定字段所在位置
    DATA l_count TYPE i.
    DATA l_count_max TYPE i.
    LOOP AT lt_flatdt REFERENCE INTO DATA(lr_flatdt).
      l_count = l_count + 1.
      lr_flatdt->position = l_count.
      AT END OF struct.
        IF l_count_max < l_count.
          l_count_max = l_count.
        ENDIF.
        CLEAR l_count.
      ENDAT.
    ENDLOOP.

    " 动态数据参数
    DATA lo_tabledescr TYPE REF TO cl_abap_tabledescr.
    DATA lo_structdescr TYPE REF TO cl_abap_structdescr.
    DATA lt_component TYPE cl_abap_structdescr=>component_table.
    DATA ls_component TYPE cl_abap_structdescr=>component.
    FIELD-SYMBOLS <fs_table> TYPE STANDARD TABLE.
    FIELD-SYMBOLS <fs_structure> TYPE any.
    FIELD-SYMBOLS <fs_field> TYPE any.
    FIELD-SYMBOLS <fs_fldk> TYPE any. " 用于染色

    CLEAR ls_component.
    ls_component-name = gs_fieldname-fdlk.
    ls_component-type = cl_abap_elemdescr=>get_string( ).
    INSERT ls_component INTO TABLE lt_component.
    CLEAR ls_component.
    ls_component-name = gs_fieldname-path.
    ls_component-type = cl_abap_elemdescr=>get_string( ).
    INSERT ls_component INTO TABLE lt_component.
    DATA l_index TYPE i.
    DO l_count_max TIMES.
      l_index = l_index + 1.
      CLEAR ls_component.
      ls_component-name = |{ gs_fieldname-field }{ l_index }|.
      ls_component-type = cl_abap_elemdescr=>get_string( ). " 都创建为STRING即可
      INSERT ls_component INTO TABLE lt_component.
    ENDDO.

    lo_structdescr = cl_abap_structdescr=>create( lt_component ).
    lo_tabledescr = cl_abap_tabledescr=>create( lo_structdescr ).
    CREATE DATA rr_data TYPE HANDLE lo_tabledescr.
    ASSIGN rr_data->* TO <fs_table>.

    " 紧凑表示与否
    IF i_compact = abap_true.
      " 紧凑表示按结构进行展示
      SORT lt_unitex BY struct path key.
    ELSE.
      " 非紧凑表示按数据顺序进行展示
      SORT lt_unitex BY path key.
    ENDIF.

    LOOP AT lt_unitex REFERENCE INTO DATA(lr_unitex).
      AT NEW struct. " 新的结构
        INSERT INITIAL LINE INTO TABLE <fs_table> ASSIGNING <fs_structure>.
        UNASSIGN <fs_fldk>.
        ASSIGN COMPONENT gs_fieldname-fdlk OF STRUCTURE <fs_structure> TO <fs_fldk>.
        IF <fs_fldk> IS ASSIGNED.
          <fs_fldk> = gs_fdlk-header.
        ENDIF.
        UNASSIGN <fs_field>.
        ASSIGN COMPONENT gs_fieldname-path OF STRUCTURE <fs_structure> TO <fs_field>.
        IF <fs_field> IS ASSIGNED.
          <fs_field> = lr_unitex->struct.
        ENDIF.
        READ TABLE lt_flatdt TRANSPORTING NO FIELDS WITH KEY struct = lr_unitex->struct BINARY SEARCH.
        IF sy-subrc = 0.
          LOOP AT lt_flatdt REFERENCE INTO lr_flatdt FROM sy-tabix.
            IF lr_flatdt->struct <> lr_unitex->struct.
              EXIT.
            ENDIF.
            UNASSIGN <fs_field>.
            ASSIGN COMPONENT |{ gs_fieldname-field }{ lr_flatdt->position }| OF STRUCTURE <fs_structure> TO <fs_field>.
            IF <fs_field> IS ASSIGNED.
              <fs_field> = lr_flatdt->field.
            ENDIF.
          ENDLOOP.
        ENDIF.
      ENDAT.

      AT NEW path. " 新的数据行
        INSERT INITIAL LINE INTO TABLE <fs_table> ASSIGNING <fs_structure>.
        UNASSIGN <fs_fldk>.
        ASSIGN COMPONENT gs_fieldname-fdlk OF STRUCTURE <fs_structure> TO <fs_fldk>.
        IF <fs_fldk> IS ASSIGNED.
          <fs_fldk> = gs_fdlk-data.
        ENDIF.
        ASSIGN COMPONENT gs_fieldname-path OF STRUCTURE <fs_structure> TO <fs_field>.
        IF <fs_field> IS ASSIGNED.
          <fs_field> = lr_unitex->path.
        ENDIF.
        CLEAR l_index.
      ENDAT.

      " 填充数据
      READ TABLE lt_flatdt REFERENCE INTO lr_flatdt WITH KEY
      struct = lr_unitex->struct
      field = lr_unitex->key
      BINARY SEARCH.
      IF sy-subrc = 0.
        UNASSIGN <fs_field>.
        ASSIGN COMPONENT |{ gs_fieldname-field }{ lr_flatdt->position }| OF STRUCTURE <fs_structure> TO <fs_field>.
        IF <fs_field> IS ASSIGNED.
          <fs_field> = lr_unitex->value.
        ENDIF.
      ENDIF.
    ENDLOOP.

    IF et_flatdt IS SUPPLIED.
      et_flatdt = lt_flatdt.
    ENDIF.

  ENDMETHOD.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LIF_ALV
*&---------------------------------------------------------------------*
INTERFACE lif_alv.
  METHODS pai.
  METHODS pbo.
  METHODS get_selected_uuid
    EXPORTING et_uuid     TYPE tt_uuid
              et_uuid_opt TYPE tt_uuid_opt. " 获取选择行的日志ID
ENDINTERFACE.
*&---------------------------------------------------------------------*
*& LCL_ALV_LOGH
*&---------------------------------------------------------------------*
CLASS lcl_alv_logh DEFINITION .
  PUBLIC SECTION.
    INTERFACES lif_alv.
    DATA ms_layout TYPE lvc_s_layo.
    DATA mt_fieldcat TYPE lvc_t_fcat.
    DATA mt_sort TYPE lvc_t_sort.
    METHODS set_layout.
    CLASS-METHODS set_fieldcat CHANGING ct_fieldcat TYPE lvc_t_fcat.
    METHODS set_sort.
    METHODS set_event.
    METHODS set_color.
    METHODS on_double_click FOR EVENT double_click OF cl_gui_alv_grid
      IMPORTING
        e_row
        e_column
        es_row_no.
    CLASS-METHODS display_rawdata
      IMPORTING i_uuid  TYPE ty_log_rawdata-uuid
                i_zitem TYPE ty_log_rawdata-zitem.
    CLASS-METHODS nav_se37
      IMPORTING i_funcname TYPE ty_log_head-func_name.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_ALV_UNIT
*&---------------------------------------------------------------------*
CLASS lcl_alv_logh IMPLEMENTATION.
*&---------------------------------------------------------------------*
*& LIF_ALV~PBO
*&---------------------------------------------------------------------*
  METHOD lif_alv~pbo.

    IF go_container IS NOT BOUND.
      go_container = NEW cl_gui_custom_container( 'CON_9000' ).
    ENDIF.

    IF go_grid IS NOT BOUND.
      go_grid = NEW cl_gui_alv_grid( go_container ).
      set_layout( ).
      set_fieldcat( CHANGING ct_fieldcat = mt_fieldcat ).
      set_sort( ).
      set_event( ).
      set_color( ).
      DATA ls_variant TYPE disvariant.
      ls_variant = VALUE #( report = sy-repid handle = 1 ).
      CALL METHOD go_grid->set_table_for_first_display
        EXPORTING
          is_layout                     = ms_layout
          is_variant                    = ls_variant
          i_save                        = 'X'
        CHANGING
          it_outtab                     = gt_log
          it_fieldcatalog               = mt_fieldcat
          it_sort                       = mt_sort
        EXCEPTIONS
          invalid_parameter_combination = 1
          program_error                 = 2
          too_many_lines                = 3
          OTHERS                        = 4.
    ELSE.
      CALL METHOD go_grid->refresh_table_display
        EXPORTING
          is_stable      = CONV #( 'XX' )
          i_soft_refresh = abap_true
        EXCEPTIONS
          finished       = 1
          OTHERS         = 2.
    ENDIF.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& LIF_ALV~PAI
*&---------------------------------------------------------------------*
  METHOD lif_alv~pai.
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& SET_LAYOUT
*&---------------------------------------------------------------------*
  METHOD set_layout.

    CLEAR ms_layout.
    ms_layout-zebra = abap_true. " 斑马线
    ms_layout-cwidth_opt = abap_true. " 自动调整ALVL列宽
    ms_layout-sel_mode = 'A'. " 选择模式
    ms_layout-ctab_fname = 'T_SCOL'. " 单元格颜色设置
*    MS_LAYOUT-STYLEFNAME = 'T_STYL'. " 单元格控制

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& SET_FIELDCAT
*&---------------------------------------------------------------------*
  METHOD set_fieldcat.

    DATA ls_fieldcat TYPE lvc_s_fcat.

    DEFINE _init_fieldcat.
      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname = &1.
      ls_fieldcat-tooltip =
      ls_fieldcat-coltext =
      ls_fieldcat-seltext =
      ls_fieldcat-scrtext_l =
      ls_fieldcat-scrtext_m =
      ls_fieldcat-scrtext_s = &2.
      ls_fieldcat-ref_table = &3.
      ls_fieldcat-ref_field = &4.
      INSERT ls_fieldcat INTO TABLE ct_fieldcat.
    END-OF-DEFINITION.

    _init_fieldcat 'UUID     ' '日志ID' '' ''.
*    _INIT_FIELDCAT 'ZITEM    ' '序号' '' ''.
    _init_fieldcat 'LOG_KIND ' '日志分类' '' ''.
    _init_fieldcat 'INTF_ID  ' '接口标识' '' ''.
    _init_fieldcat 'FUNC_NAME' '函数名' '' ''.
    _init_fieldcat 'FUNC_TEXT' '函数描述' '' ''.
*    _INIT_FIELDCAT 'ZTEXT    ' '附加信息' '' ''. " 该项目没有
    _init_fieldcat 'IO_FLAG  ' '接口方向' '' ''.
    _init_fieldcat 'ZSENDER  ' '发送方' '' ''.
    _init_fieldcat 'ZRECEIVER' '接收方' '' ''.
    _init_fieldcat 'ZDATE    ' '日期' '' ''.
    _init_fieldcat 'ZTIME    ' '时间' '' ''.
    _init_fieldcat 'ZUSER    ' '用户' '' ''.
    _init_fieldcat 'MTYPE    ' '状态' '' ''.
*    _INIT_FIELDCAT 'MSG      ' '消息文本' '' ''. " 该项目没有
    _init_fieldcat 'ZCONUT   ' '条目统计' '' ''.
    _init_fieldcat 'ZRAW_ICON' '原始数据' '' ''.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& SET_SORT
*&---------------------------------------------------------------------*
  METHOD set_sort.

    DATA ls_sort TYPE lvc_s_sort.
    DATA l_spos TYPE lvc_s_sort-spos.

    CLEAR ls_sort.
    l_spos = l_spos + 1.
    ls_sort-spos = l_spos.
    ls_sort-fieldname = 'ZDATE'.
    ls_sort-down = abap_true.
    INSERT ls_sort INTO TABLE mt_sort.

    CLEAR ls_sort.
    l_spos = l_spos + 1.
    ls_sort-spos = l_spos.
    ls_sort-fieldname = 'ZTIME'.
    ls_sort-down = abap_true.
    INSERT ls_sort INTO TABLE mt_sort.

    CLEAR ls_sort.
    l_spos = l_spos + 1.
    ls_sort-spos = l_spos.
    ls_sort-fieldname = 'UUID'.
    ls_sort-up = abap_true.
    INSERT ls_sort INTO TABLE mt_sort.

    CLEAR ls_sort.
    l_spos = l_spos + 1.
    ls_sort-spos = l_spos.
    ls_sort-fieldname = 'IO_FLAG'.
    ls_sort-up = abap_true.
    INSERT ls_sort INTO TABLE mt_sort.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& SET_EVENT
*&---------------------------------------------------------------------*
  METHOD set_event.

    SET HANDLER on_double_click FOR go_grid.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& SET_COLOR
*&---------------------------------------------------------------------*
  METHOD set_color.
    DATA ls_scol TYPE lvc_s_scol.

    LOOP AT gt_log REFERENCE INTO DATA(lr_log).
      CLEAR lr_log->t_scol.
      IF lr_log->mtype = 'S'.
        lr_log->t_scol = VALUE #( BASE lr_log->t_scol
          color = gs_color-green
          ( fname = 'MTYPE'  )
          ( fname = 'MSG' ) ).
      ELSE.
        lr_log->t_scol = VALUE #( BASE lr_log->t_scol
          color = gs_color-red
          ( fname = 'MTYPE'  )
          ( fname = 'MSG' ) ).
      ENDIF.
    ENDLOOP.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& ON_DOUBLE_CLICK
*&---------------------------------------------------------------------*
  METHOD on_double_click.

    READ TABLE gt_log REFERENCE INTO DATA(lr_log) INDEX e_row-index.
    CHECK sy-subrc = 0.

    CASE e_column.
      WHEN 'UUID'.
        DATA lt_uuid_opt LIKE RANGE OF gs_selection-uuid.
        lt_uuid_opt = VALUE #( sign = 'I' option = 'EQ' ( low = lr_log->uuid ) ).
        SUBMIT (sy-repid)
        WITH s_uuid IN lt_uuid_opt " 接口ID
        WITH s_date IN s_date " 日期会默认当天,手工传值
        WITH s_time IN s_time " 日期会默认当天,手工传值
        WITH p_logh = ''
        WITH p_unit = ''
        WITH p_flat = 'X'
        AND RETURN.
      WHEN 'ZRAW_ICON'.
        display_rawdata( i_uuid = lr_log->uuid
                         i_zitem = lr_log->zitem ).
      WHEN 'FUNC_NAME'.
        nav_se37( lr_log->func_name ).
    ENDCASE.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& DISPLAY_RAWDATA
*&---------------------------------------------------------------------*
  METHOD display_rawdata.

    DATA l_html TYPE xstring.

    READ TABLE gt_log_rawdata REFERENCE INTO DATA(lr_log_rawdata) WITH KEY
    uuid = i_uuid
    zitem = i_zitem
    BINARY SEARCH.
    IF sy-subrc = 0.
      CASE lr_log_rawdata->zformat.
        WHEN 'JSON'.
          " cl_demo_output=>display_json( lr_log_rawdata->zraw ).
          CALL TRANSFORMATION sjson2html SOURCE XML lr_log_rawdata->zraw RESULT XML l_html.
          cl_abap_browser=>show_html( html_string = cl_abap_codepage=>convert_from( l_html ) ).
        WHEN 'XML'.
*          cl_demo_output=>display_xml( lr_log_rawdata->zrawx ).
          cl_abap_browser=>show_xml( xml_xstring = lr_log_rawdata->zrawx ).
        WHEN OTHERS.
          cl_demo_output=>display( lr_log_rawdata->zraw ).
      ENDCASE.
    ENDIF.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& NAV_SE37
*&---------------------------------------------------------------------*
  METHOD nav_se37.

    DATA ls_tfdir TYPE tfdir.
    " 获取函数对应程序
    SELECT SINGLE pname include
      INTO CORRESPONDING FIELDS OF ls_tfdir
      FROM tfdir
      WHERE funcname = i_funcname.
    " 设置函数全局信息
    DATA ls_rs38l TYPE rs38l.
    CALL FUNCTION 'FUNCTION_INCLUDE_SPLIT'
      EXPORTING
        program                      = ls_tfdir-pname
      IMPORTING
        group                        = ls_rs38l-area
        namespace                    = ls_rs38l-namespace
      EXCEPTIONS
        include_not_exists           = 1
        group_not_exists             = 2
        no_selections                = 3
        no_function_include          = 4
        no_function_pool             = 5
        delimiter_wrong_position     = 6
        no_customer_function_group   = 7
        no_customer_function_include = 8
        reserved_name_customer       = 9
        namespace_too_long           = 10
        area_length_error            = 11
        OTHERS                       = 12.
    CONCATENATE 'L' ls_rs38l-area 'U' ls_tfdir-include INTO ls_tfdir-pname.
    CALL FUNCTION 'EDITOR_PROGRAM'
      EXPORTING
        display = 'X'
        program = ls_tfdir-pname
      EXCEPTIONS
        OTHERS  = 1.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& LIF_ALV~GET_SELECTED_UUID
*&---------------------------------------------------------------------*
  METHOD lif_alv~get_selected_uuid.

    " 获取ALV选取行
    DATA lt_rows TYPE lvc_t_row.
    CALL METHOD go_grid->get_selected_rows
      IMPORTING
        et_index_rows = lt_rows.

    LOOP AT lt_rows INTO DATA(ls_row).
      READ TABLE gt_log REFERENCE INTO DATA(lr_log) INDEX ls_row-index.
      CHECK sy-subrc = 0.
      IF et_uuid IS SUPPLIED.
        APPEND lr_log->uuid TO et_uuid.
      ENDIF.
      IF et_uuid_opt IS SUPPLIED.
        INSERT VALUE #( sign = 'I' option = 'EQ' low = lr_log->uuid ) INTO TABLE et_uuid_opt.
      ENDIF.
    ENDLOOP.

    SORT et_uuid BY table_line.
    DELETE ADJACENT DUPLICATES FROM et_uuid COMPARING table_line.
    DELETE et_uuid WHERE table_line IS INITIAL.

    SORT et_uuid_opt BY low.
    DELETE ADJACENT DUPLICATES FROM et_uuid_opt COMPARING low.
    DELETE et_uuid_opt WHERE low IS INITIAL.

  ENDMETHOD.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_ALV_UNIT
*&---------------------------------------------------------------------*
CLASS lcl_alv_unit DEFINITION .
  PUBLIC SECTION.
    INTERFACES lif_alv.
    TYPES:
      BEGIN OF ty_data.
        INCLUDE TYPE ty_log_head.
        INCLUDE TYPE ty_unit.
    TYPES:
      END OF ty_data.
    DATA mt_data TYPE STANDARD TABLE OF ty_data WITH EMPTY KEY.
    DATA ms_layout TYPE lvc_s_layo.
    DATA mt_fieldcat TYPE STANDARD TABLE OF lvc_s_fcat.
    METHODS constructor.
    METHODS process_data.
    METHODS set_layout.
    METHODS set_fieldcat.
    METHODS set_event.
    METHODS set_color.
    METHODS on_double_click FOR EVENT double_click OF cl_gui_alv_grid
      IMPORTING
        e_row
        e_column
        es_row_no.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_ALV_UNIT
*&---------------------------------------------------------------------*
CLASS lcl_alv_unit IMPLEMENTATION.
*&---------------------------------------------------------------------*
*& CONSTRUCTOR
*&---------------------------------------------------------------------*
  METHOD constructor.
    process_data( ).
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& PROCESS_DATA
*&---------------------------------------------------------------------*
  METHOD process_data.

    DATA ls_data TYPE ty_data.
    DATA(lo_progress) = NEW lcl_progress( ).
    lo_progress->m_name = '转化单元数据'.
    lo_progress->start( lines( gt_log ) ).
    LOOP AT gt_log REFERENCE INTO DATA(lr_log).
      lo_progress->next( ).
      CLEAR ls_data.
      ls_data = CORRESPONDING #( lr_log->* ).
      LOOP AT lr_log->t_unit REFERENCE INTO DATA(lr_unit).
        DATA(ls_data_tmp) = ls_data.
        ls_data_tmp-path = lr_unit->path.
        ls_data_tmp-key = lr_unit->key.
        ls_data_tmp-value = lr_unit->value.
        INSERT ls_data_tmp INTO TABLE mt_data.
      ENDLOOP.
    ENDLOOP.
    lo_progress->finish( ).

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& LIF_ALV~PBO
*&---------------------------------------------------------------------*
  METHOD lif_alv~pbo.

    IF go_container IS NOT BOUND.
      go_container = NEW cl_gui_custom_container( 'CON_9000' ).
    ENDIF.

    IF go_grid IS NOT BOUND.
      go_grid = NEW cl_gui_alv_grid( go_container ).
      set_layout( ).
      set_fieldcat( ).
      set_event( ).
      set_color( ).
      DATA ls_variant TYPE disvariant.
      ls_variant = VALUE #( report = sy-repid handle = 2 ).
      CALL METHOD go_grid->set_table_for_first_display
        EXPORTING
          is_layout                     = ms_layout
          is_variant                    = ls_variant
          i_save                        = 'X'
        CHANGING
          it_outtab                     = mt_data
          it_fieldcatalog               = mt_fieldcat
        EXCEPTIONS
          invalid_parameter_combination = 1
          program_error                 = 2
          too_many_lines                = 3
          OTHERS                        = 4.
    ELSE.
      CALL METHOD go_grid->refresh_table_display
        EXPORTING
          is_stable      = CONV #( 'XX' )
          i_soft_refresh = abap_true
        EXCEPTIONS
          finished       = 1
          OTHERS         = 2.
    ENDIF.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& LIF_ALV~PAI
*&---------------------------------------------------------------------*
  METHOD lif_alv~pai.
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& SET_LAYOUT
*&---------------------------------------------------------------------*
  METHOD set_layout.

    CLEAR ms_layout.
    ms_layout-zebra = abap_true. " 斑马线
    ms_layout-cwidth_opt = abap_true. " 自动调整ALVL列宽
    ms_layout-sel_mode = 'A'. " 选择模式
*    MS_LAYOUT-CTAB_FNAME = 'T_SCOL'. " 单元格颜色设置
*    MS_LAYOUT-STYLEFNAME = 'T_STYL'. " 单元格控制

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& SET_FIELDCAT
*&---------------------------------------------------------------------*
  METHOD set_fieldcat.

    DATA ls_fieldcat TYPE lvc_s_fcat.

    DEFINE _init_fieldcat.
      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname = &1.
      ls_fieldcat-tooltip =
      ls_fieldcat-coltext =
      ls_fieldcat-seltext =
      ls_fieldcat-scrtext_l =
      ls_fieldcat-scrtext_m =
      ls_fieldcat-scrtext_s = &2.
      ls_fieldcat-ref_table = &3.
      ls_fieldcat-ref_field = &4.
      INSERT ls_fieldcat INTO TABLE mt_fieldcat.
    END-OF-DEFINITION.

    lcl_alv_logh=>set_fieldcat( CHANGING ct_fieldcat = mt_fieldcat ).
    _init_fieldcat 'PATH' '路径' '' ''.
    _init_fieldcat 'KEY' '字段名' '' ''.
    _init_fieldcat 'VALUE' '字段值' '' ''.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& SET_EVENT
*&---------------------------------------------------------------------*
  METHOD set_event.

    SET HANDLER on_double_click FOR go_grid.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& SET_COLOR
*&---------------------------------------------------------------------*
  METHOD set_color.
    DATA ls_scol TYPE lvc_s_scol.

    LOOP AT mt_data REFERENCE INTO DATA(lr_data).
      CLEAR lr_data->t_scol.
      IF lr_data->mtype = 'S'.
        lr_data->t_scol = VALUE #( BASE lr_data->t_scol
          color = gs_color-green
          ( fname = 'MTYPE'  )
          ( fname = 'MSG' ) ).
      ELSE.
        lr_data->t_scol = VALUE #( BASE lr_data->t_scol
          color = gs_color-red
          ( fname = 'MTYPE'  )
          ( fname = 'MSG' ) ).
      ENDIF.
    ENDLOOP.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& ON_DOUBLE_CLICK
*&---------------------------------------------------------------------*
  METHOD on_double_click.

    READ TABLE mt_data REFERENCE INTO DATA(lr_data) INDEX e_row-index.
    CHECK sy-subrc = 0.
    CASE e_column.
      WHEN 'ZRAW_ICON'.
        lcl_alv_logh=>display_rawdata(
            i_uuid = lr_data->uuid
            i_zitem = lr_data->zitem ).
      WHEN 'FUNC_NAME'.
        lcl_alv_logh=>nav_se37( lr_data->func_name ).
    ENDCASE.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& LIF_ALV~GET_SELECTED_UUID
*&---------------------------------------------------------------------*
  METHOD lif_alv~get_selected_uuid.

    " 获取ALV选取行
    DATA lt_rows TYPE lvc_t_row.
    CALL METHOD go_grid->get_selected_rows
      IMPORTING
        et_index_rows = lt_rows.

    LOOP AT lt_rows INTO DATA(ls_row).
      READ TABLE mt_data REFERENCE INTO DATA(lr_unit) INDEX ls_row-index.
      CHECK sy-subrc = 0.
      IF et_uuid IS SUPPLIED.
        APPEND lr_unit->uuid TO et_uuid.
      ENDIF.
      IF et_uuid_opt IS SUPPLIED.
        INSERT VALUE #( sign = 'I' option = 'EQ' low = lr_unit->uuid ) INTO TABLE et_uuid_opt.
      ENDIF.
    ENDLOOP.

    SORT et_uuid BY table_line.
    DELETE ADJACENT DUPLICATES FROM et_uuid COMPARING table_line.
    DELETE et_uuid WHERE table_line IS INITIAL.

    SORT et_uuid_opt BY low.
    DELETE ADJACENT DUPLICATES FROM et_uuid_opt COMPARING low.
    DELETE et_uuid_opt WHERE low IS INITIAL.

  ENDMETHOD.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_ALV_FLAT
*&---------------------------------------------------------------------*
CLASS lcl_alv_flat DEFINITION .
  PUBLIC SECTION.
    INTERFACES lif_alv.
    DATA mt_component_flat TYPE cl_abap_structdescr=>component_table.
    DATA mr_data TYPE REF TO data.
    DATA ms_layout TYPE lvc_s_layo.
    DATA mt_fieldcat TYPE STANDARD TABLE OF lvc_s_fcat.
    METHODS constructor.
    METHODS process_data.
    METHODS set_layout.
    METHODS set_fieldcat.
    METHODS set_event.
    METHODS set_color.
    METHODS on_double_click FOR EVENT double_click OF cl_gui_alv_grid
      IMPORTING
        e_row
        e_column
        es_row_no.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_ALV_FLAT
*&---------------------------------------------------------------------*
CLASS lcl_alv_flat IMPLEMENTATION.
*&---------------------------------------------------------------------*
*& CONSTRUCTOR
*&---------------------------------------------------------------------*
  METHOD constructor.
    process_data( ).
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& PROCESS_DATA
*&---------------------------------------------------------------------*
  METHOD process_data.

    " 计算最大字段数量
    DATA l_count_max TYPE i.
    LOOP AT gt_log REFERENCE INTO DATA(lr_log).
      LOOP AT lr_log->t_flatdt REFERENCE INTO DATA(lr_flatdt).
        IF l_count_max < lr_flatdt->position.
          l_count_max = lr_flatdt->position.
        ENDIF.
      ENDLOOP.
    ENDLOOP.

    " 抬头部分固定
    DATA lo_tabledescr TYPE REF TO cl_abap_tabledescr.
    DATA lo_structdescr TYPE REF TO cl_abap_structdescr.
    DATA lt_component TYPE cl_abap_structdescr=>component_table.
    DATA ls_component TYPE cl_abap_structdescr=>component.
    lo_structdescr ?= cl_abap_typedescr=>describe_by_data( VALUE ty_log_head( ) ).
    lt_component = lo_structdescr->get_components( ).

    " 扁平部分
    CLEAR ls_component.
    ls_component-name = gs_fieldname-fdlk.
    ls_component-type = cl_abap_elemdescr=>get_string( ).
    INSERT ls_component INTO TABLE mt_component_flat.
    CLEAR ls_component.
    ls_component-name = gs_fieldname-path.
    ls_component-type = cl_abap_elemdescr=>get_string( ).
    INSERT ls_component INTO TABLE mt_component_flat.
    DATA l_index TYPE i.
    DO l_count_max TIMES.
      l_index = l_index + 1.
      CLEAR ls_component.
      ls_component-name = |{ gs_fieldname-field }{ l_index }|.
      ls_component-type = cl_abap_elemdescr=>get_string( ). " 都创建为STRING即可
      INSERT ls_component INTO TABLE mt_component_flat.
    ENDDO.
    INSERT LINES OF mt_component_flat INTO TABLE lt_component.

    " 创建报表展示结构
    lo_structdescr = cl_abap_structdescr=>create( lt_component ).
    lo_tabledescr = cl_abap_tabledescr=>create( lo_structdescr ).
    CREATE DATA mr_data TYPE HANDLE lo_tabledescr.

    FIELD-SYMBOLS <fs_table> TYPE STANDARD TABLE.
    FIELD-SYMBOLS <fs_structure> TYPE any.
    FIELD-SYMBOLS <fs_field> TYPE any.
    ASSIGN mr_data->* TO <fs_table>.

    DATA(lo_progress) = NEW lcl_progress( ).
    lo_progress->m_name = '转化扁平结构'.
    lo_progress->start( lines( gt_log ) ).

    LOOP AT gt_log REFERENCE INTO lr_log.
      lo_progress->next( ).

      IF <fs_table> IS NOT INITIAL.
        DO p_ctl5 TIMES. " 间隔行,不然太紧凑不好看数据
          INSERT INITIAL LINE INTO TABLE <fs_table>.
        ENDDO.
      ENDIF.

      IF lr_log->r_flatdata IS NOT BOUND.
        INSERT INITIAL LINE INTO TABLE <fs_table> ASSIGNING <fs_structure>.
        <fs_structure> = CORRESPONDING #( lr_log->* ). " 日志抬头部分
        CONTINUE.
      ENDIF.

      FIELD-SYMBOLS <fs_flatdata_table> TYPE STANDARD TABLE.
      FIELD-SYMBOLS <fs_flatdata_structure> TYPE any.
      FIELD-SYMBOLS <fs_flatdata_field> TYPE any.
      UNASSIGN <fs_flatdata_table>.
      UNASSIGN <fs_flatdata_structure>.
      ASSIGN lr_log->r_flatdata->* TO <fs_flatdata_table>.

      LOOP AT <fs_flatdata_table> ASSIGNING <fs_flatdata_structure>.
        IF p_ctl2 <> abap_true. " 展示抬头
          ASSIGN COMPONENT gs_fieldname-fdlk OF STRUCTURE <fs_flatdata_structure> TO FIELD-SYMBOL(<fs_fdlk>).
          IF <fs_fdlk> IS ASSIGNED.
            CHECK <fs_fdlk> <> gs_fdlk-header.
          ENDIF.
        ENDIF.

        INSERT INITIAL LINE INTO TABLE <fs_table> ASSIGNING <fs_structure>.
        <fs_structure> = CORRESPONDING #( lr_log->* ). " 日志抬头部分
        LOOP AT mt_component_flat INTO ls_component.
          UNASSIGN <fs_flatdata_field>.
          UNASSIGN <fs_field>.
          ASSIGN COMPONENT ls_component-name OF STRUCTURE <fs_flatdata_structure> TO <fs_flatdata_field>.
          ASSIGN COMPONENT ls_component-name OF STRUCTURE <fs_structure> TO <fs_field>.
          IF <fs_flatdata_field> IS ASSIGNED AND <fs_field> IS ASSIGNED.
            <fs_field> = <fs_flatdata_field>.
          ENDIF.
        ENDLOOP.
      ENDLOOP.

    ENDLOOP.
    lo_progress->finish( ).

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& LIF_ALV~PBO
*&---------------------------------------------------------------------*
  METHOD lif_alv~pbo.

    IF go_container IS NOT BOUND.
      go_container = NEW cl_gui_custom_container( 'CON_9000' ).
    ENDIF.

    IF go_grid IS NOT BOUND.
      go_grid = NEW cl_gui_alv_grid( go_container ).
      set_layout( ).
      set_fieldcat( ).
      set_event( ).
      set_color( ).

      FIELD-SYMBOLS <fs_table> TYPE STANDARD TABLE.
      ASSIGN mr_data->* TO <fs_table>.

      DATA ls_variant TYPE disvariant.
      ls_variant = VALUE #( report = sy-repid handle = 3 ).
      CALL METHOD go_grid->set_table_for_first_display
        EXPORTING
          is_layout                     = ms_layout
          is_variant                    = ls_variant
          i_save                        = 'X'
        CHANGING
          it_outtab                     = <fs_table>
          it_fieldcatalog               = mt_fieldcat
        EXCEPTIONS
          invalid_parameter_combination = 1
          program_error                 = 2
          too_many_lines                = 3
          OTHERS                        = 4.
    ELSE.
      CALL METHOD go_grid->refresh_table_display
        EXPORTING
          is_stable      = CONV #( 'XX' )
          i_soft_refresh = abap_true
        EXCEPTIONS
          finished       = 1
          OTHERS         = 2.
    ENDIF.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& LIF_ALV~PAI
*&---------------------------------------------------------------------*
  METHOD lif_alv~pai.
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& SET_LAYOUT
*&---------------------------------------------------------------------*
  METHOD set_layout.

    CLEAR ms_layout.
*    MS_LAYOUT-ZEBRA = ABAP_TRUE. " 斑马线
    ms_layout-cwidth_opt = p_ctl4. " ABAP_TRUE. " 自动调整ALVL列宽
    ms_layout-sel_mode = 'A'. " 选择模式
    ms_layout-info_fname = 'RCOL'. " 单元格颜色设置
    ms_layout-ctab_fname = 'T_SCOL'. " 单元格颜色设置
*    MS_LAYOUT-STYLEFNAME = 'T_STYL'. " 单元格控制

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& SET_FIELDCAT
*&---------------------------------------------------------------------*
  METHOD set_fieldcat.

    DATA ls_fieldcat TYPE lvc_s_fcat.

    DEFINE _init_fieldcat.
      CLEAR ls_fieldcat.
      ls_fieldcat-fieldname = &1.
      ls_fieldcat-tooltip =
      ls_fieldcat-coltext =
      ls_fieldcat-seltext =
      ls_fieldcat-scrtext_l =
      ls_fieldcat-scrtext_m =
      ls_fieldcat-scrtext_s = &2.
      ls_fieldcat-ref_table = &3.
      ls_fieldcat-ref_field = &4.
      INSERT ls_fieldcat INTO TABLE mt_fieldcat.
    END-OF-DEFINITION.

    lcl_alv_logh=>set_fieldcat( CHANGING ct_fieldcat = mt_fieldcat ).
    _init_fieldcat gs_fieldname-path '路径' '' ''.
    LOOP AT mt_component_flat INTO DATA(ls_component_flat).
      IF ls_component_flat-name CS gs_fieldname-field.
        _init_fieldcat ls_component_flat-name '-' '' ''.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& SET_EVENT
*&---------------------------------------------------------------------*
  METHOD set_event.

    SET HANDLER on_double_click FOR go_grid.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& SET_COLOR
*&---------------------------------------------------------------------*
  METHOD set_color.

    DATA ls_scol TYPE lvc_s_scol.
    DATA ls_log_head TYPE ty_log_head.
    FIELD-SYMBOLS <fs_table> TYPE STANDARD TABLE.
    FIELD-SYMBOLS <fs_structure> TYPE any.
    FIELD-SYMBOLS <fs_field> TYPE any.
    FIELD-SYMBOLS <fs_t_scol> TYPE lvc_t_scol.

    ASSIGN mr_data->* TO <fs_table>.

    LOOP AT <fs_table> ASSIGNING <fs_structure>.
      IF <fs_structure> IS INITIAL.
        ASSIGN COMPONENT 'RCOL' OF STRUCTURE <fs_structure> TO FIELD-SYMBOL(<fs_rcol>).
        IF <fs_rcol> IS ASSIGNED.
          <fs_rcol> = 'C300'.
        ENDIF.
        CONTINUE.
      ENDIF.

      ASSIGN COMPONENT 'T_SCOL' OF STRUCTURE <fs_structure> TO <fs_t_scol>.
      CLEAR <fs_t_scol>.

      CLEAR ls_log_head.
      ls_log_head = CORRESPONDING #( <fs_structure> ).
      IF ls_log_head-mtype = 'S'.
        <fs_t_scol> = VALUE #( BASE <fs_t_scol>
          color = gs_color-green
          ( fname = 'MTYPE'  )
          ( fname = 'MSG' ) ).
      ELSE.
        <fs_t_scol> = VALUE #( BASE <fs_t_scol>
          color = gs_color-red
          ( fname = 'MTYPE'  )
          ( fname = 'MSG' ) ).
      ENDIF.

      CHECK p_ctl3 = abap_true. " 单元格着色
      UNASSIGN <fs_field>.
      ASSIGN COMPONENT gs_fieldname-fdlk OF STRUCTURE <fs_structure> TO <fs_field>.
      CHECK <fs_field> IS ASSIGNED.

      DATA ls_color LIKE gs_color-red.
      CLEAR ls_color.
      CASE <fs_field>.
        WHEN gs_fdlk-header.
          ls_color = gs_color-dept_blue.
        WHEN gs_fdlk-data.
*          LS_COLOR = GS_COLOR-BLUE.
        WHEN OTHERS.
          ls_color = gs_color-yellow.
      ENDCASE.
      LOOP AT mt_component_flat INTO DATA(ls_component).
        CLEAR ls_scol.
        ls_scol-fname = ls_component-name.
        ls_scol-color = ls_color.
        INSERT ls_scol INTO TABLE <fs_t_scol>.
      ENDLOOP.

    ENDLOOP.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& ON_DOUBLE_CLICK
*&---------------------------------------------------------------------*
  METHOD on_double_click.

    FIELD-SYMBOLS <fs_table> TYPE STANDARD TABLE.
    FIELD-SYMBOLS <fs_structure> TYPE any.
    ASSIGN mr_data->* TO <fs_table>.

    READ TABLE <fs_table> ASSIGNING <fs_structure> INDEX e_row-index.
    CHECK sy-subrc = 0.

    DATA ls_log_head TYPE ty_log_head.
    ls_log_head = CORRESPONDING #( <fs_structure> ).

    CASE e_column.
      WHEN 'ZRAW_ICON'.
        lcl_alv_logh=>display_rawdata(
            i_uuid = ls_log_head-uuid
            i_zitem = ls_log_head-zitem ).
      WHEN 'FUNC_NAME'.
        lcl_alv_logh=>nav_se37( ls_log_head-func_name ).
    ENDCASE.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& LIF_ALV~GET_SELECTED_UUID
*&---------------------------------------------------------------------*
  METHOD lif_alv~get_selected_uuid.

    FIELD-SYMBOLS <fs_table> TYPE STANDARD TABLE.
    FIELD-SYMBOLS <fs_structure> TYPE any.
    ASSIGN mr_data->* TO <fs_table>.

    " 获取ALV选取行
    DATA lt_rows TYPE lvc_t_row.
    CALL METHOD go_grid->get_selected_rows
      IMPORTING
        et_index_rows = lt_rows.

    LOOP AT lt_rows INTO DATA(ls_row).
      READ TABLE <fs_table> ASSIGNING <fs_structure> INDEX ls_row-index.
      CHECK sy-subrc = 0.
      ASSIGN COMPONENT 'UUID' OF STRUCTURE <fs_structure> TO FIELD-SYMBOL(<fs_uuid>).
      IF et_uuid IS SUPPLIED.
        APPEND <fs_uuid> TO et_uuid.
      ENDIF.
      IF et_uuid_opt IS SUPPLIED.
        INSERT VALUE #( sign = 'I' option = 'EQ' low = <fs_uuid> ) INTO TABLE et_uuid_opt.
      ENDIF.
    ENDLOOP.

    SORT et_uuid BY table_line.
    DELETE ADJACENT DUPLICATES FROM et_uuid COMPARING table_line.
    DELETE et_uuid WHERE table_line IS INITIAL.

    SORT et_uuid_opt BY low.
    DELETE ADJACENT DUPLICATES FROM et_uuid_opt COMPARING low.
    DELETE et_uuid_opt WHERE low IS INITIAL.

  ENDMETHOD.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LIF_LOG
*&---------------------------------------------------------------------*
INTERFACE lif_log.
  METHODS check_active
    RETURNING VALUE(r_result) TYPE xfeld.
  METHODS get_log_kind
    RETURNING VALUE(r_log_kind) TYPE ty_log_head-log_kind.
  METHODS get_data.
  METHODS redo
    IMPORTING it_uuid TYPE tt_uuid.
ENDINTERFACE.
*&---------------------------------------------------------------------*
*& LCL_LOG_PO
*&---------------------------------------------------------------------*
CLASS lcl_log_po DEFINITION .
  PUBLIC SECTION.
    INTERFACES lif_log.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_LOG_PO
*&---------------------------------------------------------------------*
CLASS lcl_log_po IMPLEMENTATION.
*&---------------------------------------------------------------------*
*& lif_log~get_log_kind
*&---------------------------------------------------------------------*
  METHOD lif_log~get_log_kind.
    r_log_kind = 'PO'.
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& LIF_LOG~GET_DATA
*&---------------------------------------------------------------------*
  METHOD lif_log~get_data.

    DATA ls_log TYPE ty_log.
    DATA ls_log_rawdata TYPE ty_log_rawdata.

    DATA:
      ls_clustkey TYPE sxmsclustkey,
      ls_clur     TYPE sxmsclur,
      lt_res      TYPE sxmsrest,
      lt_xres     TYPE sxmsxrest,
      lt_ns       TYPE sxmsnst.

    DATA l_from TYPE sxmspmast-sendtimest.
    DATA l_to TYPE sxmspmast-sendtimest.

    IF s_time[] IS NOT INITIAL.
      CONVERT DATE s_date[ 1 ]-low TIME s_time[ 1 ]-low INTO TIME STAMP l_from TIME ZONE sy-zonlo.
      CONVERT DATE s_date[ 1 ]-high TIME s_time[ 1 ]-high INTO TIME STAMP l_to TIME ZONE sy-zonlo.
    ELSE.
      CONVERT DATE s_date[ 1 ]-low TIME '000000' INTO TIME STAMP l_from TIME ZONE sy-zonlo.
      CONVERT DATE s_date[ 1 ]-high TIME '235959' INTO TIME STAMP l_to TIME ZONE sy-zonlo.
    ENDIF.

    " SXMSPEMAS,框架信息,发送SI,接收SI,命名空间,系统等
    " SXMSPMAST,主要信息,用户,开始结束时间等
    " SXMSCLUR,消息报文
    SELECT
      a~msgguid,
      a~pid,
      b~ob_system,
      b~ob_name,
      b~ib_system,
      b~ib_name,
      a~adminuser,
      a~sendtimest,
      a~msgtype
      FROM sxmspmast AS a
      JOIN sxmspemas AS b ON a~msgguid = b~msgguid
                         AND a~pid = b~pid
      WHERE a~msgguid IN @s_uuid
        AND a~sendtimest BETWEEN @l_from AND @l_to
        AND ( b~ob_name IN @s_siname OR b~ib_name IN @s_siname )
        AND a~adminuser IN @s_user
        AND a~msgtype IN @s_mtype
      INTO TABLE @DATA(lt_pilog).

    LOOP AT lt_pilog INTO DATA(ls_pilog).
      " 读取PO报文
      CLEAR ls_clustkey.
      CLEAR ls_clur    .
      CLEAR lt_res     .
      CLEAR lt_xres    .
      CLEAR lt_ns      .
      ls_clustkey-msgguid = ls_pilog-msgguid.
      ls_clustkey-pid = ls_pilog-pid.
      IMPORT
        lt_res TO lt_res
        lt_xres TO lt_xres
        lt_ns TO lt_ns
        FROM DATABASE sxmsclur(is) TO ls_clur ID ls_clustkey.

      CLEAR ls_log.
      ls_log-uuid = ls_pilog-msgguid.
      " 接口ID
      IF ls_pilog-ob_name IS NOT INITIAL.
        ls_log-intf_id = ls_pilog-ob_name.
      ELSE.
        ls_log-intf_id = ls_pilog-ib_name.
      ENDIF.
      ls_log-io_flag = ls_pilog-pid. " 接口方向
      ls_log-zsender = ls_pilog-ob_system. " 发送方
      ls_log-zreceiver = ls_pilog-ib_system. " 接受方
      CONVERT TIME STAMP ls_pilog-sendtimest TIME ZONE sy-zonlo INTO DATE ls_log-zdate TIME ls_log-ztime.
      ls_log-zuser = ls_pilog-adminuser.
      ls_log-mtype = ls_pilog-msgtype.
      INSERT ls_log INTO TABLE gt_log.

      READ TABLE lt_xres REFERENCE INTO DATA(lr_xres) INDEX 1.
      IF sy-subrc = 0 AND lr_xres->rescontent IS NOT INITIAL.
        CLEAR ls_log_rawdata.
        ls_log_rawdata-uuid = ls_pilog-msgguid.
        ls_log_rawdata-zformat = 'XML'.
        ls_log_rawdata-zrawx = lr_xres->rescontent. " 原始数据
        INSERT ls_log_rawdata INTO TABLE gt_log_rawdata.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& LIF_LOG~REDO
*&---------------------------------------------------------------------*
  METHOD lif_log~redo.
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& lif_log~check_active
*&---------------------------------------------------------------------*
  METHOD lif_log~check_active.
    r_result = p_po.
  ENDMETHOD.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_LOG_CUSTOM
*&---------------------------------------------------------------------*
CLASS lcl_log_custom DEFINITION .
  PUBLIC SECTION.
    INTERFACES lif_log.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_LOG_CUSTOM
*&---------------------------------------------------------------------*
CLASS lcl_log_custom IMPLEMENTATION.
*&---------------------------------------------------------------------*
*& lif_log~get_log_kind
*&---------------------------------------------------------------------*
  METHOD lif_log~get_log_kind.
    r_log_kind = 'CUSTOM'.
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& LIF_LOG~GET_DATA
*&---------------------------------------------------------------------*
  METHOD lif_log~get_data.

    " DATA ls_log TYPE ty_log.
    " DATA ls_log_rawdata TYPE ty_log_rawdata.
    " DATA l_zitem TYPE ty_log-zitem.

    " SELECT
    "   ztintf~sysid,
    "   ztintf~funcname,
    "   tftit~stext
    "   FROM ztintf
    "   JOIN tftit ON ztintf~funcname = tftit~funcname
    "             AND tftit~spras = '1'
    "   INTO TABLE @DATA(lt_ztintf).
    " SORT lt_ztintf BY sysid.

    " SELECT * FROM ztlog
    " WHERE zguid IN @s_uuid
    "   AND intfid IN @s_intfid
    "   AND zsender IN @s_snd
    "   AND zreceiver IN @s_rcv
    "   AND datum IN @s_date
    "   AND uzeit IN @s_time
    "   AND zstatus IN @s_mtype
    "   AND funcname IN @s_func
    " INTO TABLE @DATA(lt_ztlog).

    " SORT lt_ztlog BY zguid.

    " LOOP AT lt_ztlog INTO DATA(ls_ztlog).
    "   AT NEW zguid.
    "     CLEAR l_zitem.
    "   ENDAT.
    "   l_zitem = l_zitem + 1.

    "   CLEAR ls_log.
    "   ls_log-uuid = ls_ztlog-zguid.
    "   ls_log-zitem = l_zitem.
    "   ls_log-intf_id = ls_ztlog-intfid. " 接口ID
    "   READ TABLE lt_ztintf REFERENCE INTO DATA(lr_ztintf)
    "   WITH KEY sysid = ls_ztlog-intfid BINARY SEARCH.
    "   IF sy-subrc = 0.
    "     ls_log-func_name = lr_ztintf->funcname.
    "     ls_log-func_text = lr_ztintf->stext.
    "   ENDIF.
    "   ls_log-io_flag = ls_ztlog-zzio. " 接口方向
    "   ls_log-zsender = ls_ztlog-zsender. " 发送方
    "   ls_log-zreceiver = ls_ztlog-zreceiver. " 接受方
    "   ls_log-zdate = ls_ztlog-datum.
    "   ls_log-ztime = ls_ztlog-uzeit.
    "   ls_log-zuser = ls_ztlog-uname.
    "   ls_log-mtype = ls_ztlog-zstatus.
    "   ls_log-zconut = ls_ztlog-zcount. " 条目统计
    "   INSERT ls_log INTO TABLE gt_log.

    "   IF ls_ztlog-zjson IS NOT INITIAL.
    "     CLEAR ls_log_rawdata.
    "     ls_log_rawdata-uuid = ls_ztlog-zguid.
    "     ls_log_rawdata-zitem = l_zitem.
    "     ls_log_rawdata-zformat = 'JSON'.
    "     ls_log_rawdata-zraw = ls_ztlog-zjson. " 原始数据
    "     INSERT ls_log_rawdata INTO TABLE gt_log_rawdata.
    "   ENDIF.
    " ENDLOOP.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& LIF_LOG~REDO
*&---------------------------------------------------------------------*
  METHOD lif_log~redo.

    " DATA l_fsyid TYPE string.
    " DATA l_fname TYPE string.
    " DATA l_sdata TYPE string.
    " DATA l_infud TYPE string.
    " DATA l_ftname TYPE string.
    " DATA(lo_proxy) = NEW zcl_intf_proxy( ).

    " LOOP AT it_uuid INTO DATA(l_uuid).
    "   READ TABLE gt_log INTO DATA(ls_log) WITH KEY
    "   uuid = l_uuid
    "   io_flag = '2'.
    "   IF ls_log-mtype = 'S'.
    "     lcl_progress=>step( |{ l_uuid }不需要重处理| ).
    "     CONTINUE.
    "   ELSE.
    "     READ TABLE gt_log INTO ls_log WITH KEY
    "     uuid = l_uuid
    "     io_flag = '1'.
    "     IF sy-subrc = 0.
    "       READ TABLE gt_log_rawdata INTO DATA(ls_log_rawdata) WITH KEY
    "       uuid = ls_log-uuid
    "       zitem = ls_log-zitem
    "       BINARY SEARCH.
    "       IF sy-subrc = 0.
    "         l_fsyid = ls_log-zsender. " 外部系统
    "         l_fname = ls_log-intf_id. " 接口ID
    "         l_sdata = ls_log_rawdata-zraw. " 请求报文
    "         l_infud = ls_log-uuid. " 报文ID
    "         l_ftname = ls_log-func_name. " 对应函数名
    "         CASE ls_log-zsender.
    "           WHEN 'SAP'.
    "             CALL METHOD lo_proxy->sap_data_to_out
    "               EXPORTING
    "                 iv_fname  = l_fname
    "                 iv_sdata  = l_sdata
    "                 iv_infud  = l_infud
    "                 iv_ftname = l_ftname.
    "           WHEN OTHERS.
    "             CALL METHOD lo_proxy->out_data_to_sap
    "               EXPORTING
    "                 iv_fsyid = l_fsyid
    "                 iv_fname = l_fname
    "                 iv_sdata = l_sdata
    "                 iv_infud = l_infud.
    "         ENDCASE.
    "         lcl_progress=>step( |{ l_uuid }已提交重处理| ).
    "       ENDIF.
    "     ENDIF.
    "   ENDIF.
    " ENDLOOP.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& lif_log~check_active
*&---------------------------------------------------------------------*
  METHOD lif_log~check_active.
    r_result = p_custom.
  ENDMETHOD.
ENDCLASS.

*&---------------------------------------------------------------------*
*& FRM_INIT
*&---------------------------------------------------------------------*
FORM frm_init.

  GET PARAMETER ID 'LIB' FIELD s_func-low. " 更新参数相关

  b900 = '自定义选择条件'.
  %_p_custom_%_app_%-text = '查询自定义日志'.
  %_s_intfid_%_app_%-text = '接口编号'.
  b100 = '通用选择条件'.
  %_s_uuid_%_app_%-text = 'UUID'.
  %_s_func_%_app_%-text = '函数名'.
  %_s_snd_%_app_%-text = '发送方'.
  %_s_rcv_%_app_%-text = '接收方'.
  %_s_date_%_app_%-text = '处理日期'.
  %_s_time_%_app_%-text = '处理时间'.
  %_s_user_%_app_%-text = '处理人'.
  %_s_mtype_%_app_%-text = '状态'.
  b101 = '扩展选择条件'.
  %_s_field_%_app_%-text = '字段名'.
  %_s_value_%_app_%-text = '字段值'.
  b102 = 'PO选择条件'.
  %_p_po_%_app_%-text = '查询PO'.
  %_s_siname_%_app_%-text = '接口名称'.
  b200 = '报表样式'.
  %_p_logh_%_app_%-text = '日志抬头数据展示'.
  %_p_unit_%_app_%-text = '单元数据展示'.
  %_p_flat_%_app_%-text = '扁平结构展示'.
  b201 = '其他选项'.
  %_p_ctl1_%_app_%-text = '紧凑展示'.
  %_p_ctl2_%_app_%-text = '展示结构抬头'.
  %_p_ctl3_%_app_%-text = '单元格着色'.
  %_p_ctl4_%_app_%-text = '列宽自适应'.
  %_p_ctl5_%_app_%-text = '日志记录相隔空行'.

  gs_color-blue   = VALUE #( col = 4 ).
  gs_color-yellow = VALUE #( col = 3 ).
  gs_color-green  = VALUE #( col = 5 ).
  gs_color-red    = VALUE #( col = 6 ).
  gs_color-dept_blue   = VALUE #( col = 4 int = 1 ).
  gs_color-dept_yellow = VALUE #( col = 3 int = 1 ).
  gs_color-dept_green  = VALUE #( col = 5 int = 1 ).
  gs_color-dept_red    = VALUE #( col = 6 int = 1 ).

  PERFORM frm_find_log_impl. " 查找并创建日志实例

ENDFORM.
*&---------------------------------------------------------------------*
*& FRM_FIND_LOG_IMPL
*&---------------------------------------------------------------------*
FORM frm_find_log_impl.

  DATA lt_classname TYPE STANDARD TABLE OF seoclsname.

  DATA lo_classdescr TYPE REF TO cl_abap_classdescr.
  DATA lo_log TYPE REF TO lif_log.
  DATA(lo_scan) = NEW cl_ci_scan( cl_ci_source_include=>create( p_name = sy-repid ) ).
  LOOP AT lo_scan->tokens INTO DATA(ls_token) WHERE str = 'CLASS'.
    READ TABLE lo_scan->tokens INTO ls_token INDEX sy-tabix + 1.
    INSERT CONV #( ls_token-str ) INTO TABLE lt_classname.
  ENDLOOP.
  SORT lt_classname BY table_line.
  DELETE ADJACENT DUPLICATES FROM lt_classname COMPARING table_line.

  LOOP AT lt_classname INTO DATA(l_classname).
    lo_classdescr ?= cl_abap_classdescr=>describe_by_name( l_classname ).
    READ TABLE lo_classdescr->interfaces TRANSPORTING NO FIELDS WITH KEY name = 'LIF_LOG'.
    IF sy-subrc = 0.
      TRY.
          CREATE OBJECT lo_log TYPE (lo_classdescr->absolute_name).
          INSERT lo_log INTO TABLE gt_log_impl.
        CATCH cx_root.
      ENDTRY.
    ENDIF.
  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*& FRM_MAIN
*&---------------------------------------------------------------------*
FORM frm_main.

  " 为了方便读取时间+日期范围,稍微调整下日期格式
  LOOP AT s_date REFERENCE INTO DATA(lr_date_opt).
    IF lr_date_opt->high IS INITIAL.
      lr_date_opt->option = 'BT'.
      lr_date_opt->high = lr_date_opt->low.
    ENDIF.
  ENDLOOP.
  LOOP AT s_time REFERENCE INTO DATA(lr_time_opt).
    IF lr_time_opt->high IS INITIAL.
      lr_time_opt->option = 'BT'.
      lr_time_opt->high = lr_time_opt->low.
    ENDIF.
  ENDLOOP.

  lcl_progress=>step( '查询日志数据' ).
  PERFORM frm_get_data.

  lcl_progress=>step( '日志数据处理' ).
  PERFORM frm_process_data.

  PERFORM frm_display. " ALV展示

ENDFORM.
*&---------------------------------------------------------------------*
*& FRM_GET_DATA
*&---------------------------------------------------------------------*
FORM frm_get_data.

  DATA lt_log TYPE tt_log.
  LOOP AT gt_log_impl INTO DATA(lo_log).
    CHECK lo_log->check_active( ) = abap_true.
    CLEAR gt_log.
    lo_log->get_data( ). " 取数
    LOOP AT gt_log REFERENCE INTO DATA(lr_log).
      lr_log->log_kind = lo_log->get_log_kind( ).
      lr_log->zraw_icon = icon_detail.
    ENDLOOP.
    INSERT LINES OF gt_log INTO TABLE lt_log.
  ENDLOOP.
  gt_log = lt_log.

ENDFORM.
*&---------------------------------------------------------------------*
*& FRM_PROCESS_DATA
*&---------------------------------------------------------------------*
FORM frm_process_data.

  " 日期时间倒序
  SORT gt_log BY zdate DESCENDING
                 ztime DESCENDING
                 uuid
                 zitem DESCENDING.

  SORT gt_log_rawdata BY uuid zitem. " 用于弹窗展示原始数据

  " 不需要展示加工数据的话,没必要继续执行浪费时间
  IF p_logh = abap_true AND s_value[] IS INITIAL.
    RETURN.
  ENDIF.

*  " 模糊查询
*  LOOP AT s_value[] REFERENCE INTO DATA(lr_value_opt).
*    IF lr_value_opt->option = 'EQ'.
*      lr_value_opt->option = 'CP'.
*      lr_value_opt->low = |*{ lr_value_opt->low }*|.
*    ENDIF.
*  ENDLOOP.

  DATA(lo_progress) = NEW lcl_progress( ).
  lo_progress->m_name = '接口数据处理'.
  lo_progress->start( lines( gt_log ) ).

  LOOP AT gt_log REFERENCE INTO DATA(lr_log).
    lo_progress->next( ). " 进度展示

    " 转化为数据单元
    READ TABLE gt_log_rawdata REFERENCE INTO DATA(lr_log_rawdata) WITH KEY
    uuid = lr_log->uuid
    zitem = lr_log->zitem
    BINARY SEARCH.
    IF sy-subrc = 0.
      CASE lr_log_rawdata->zformat.
        WHEN 'JSON'.
          lr_log->t_unit = lcl_unit_helper=>parse_json( lr_log_rawdata->zraw ).
        WHEN 'XML'.
          lr_log->t_unit = lcl_unit_helper=>parse_xml( lr_log_rawdata->zrawx ).
      ENDCASE.
    ENDIF.
    CHECK lr_log->t_unit IS NOT INITIAL.

    " 扩展查询
    IF s_value[] IS NOT INITIAL.
      IF s_field[] IS NOT INITIAL.
        LOOP AT lr_log->t_unit TRANSPORTING NO FIELDS WHERE key IN s_field[] AND value IN s_value[].
          EXIT.
        ENDLOOP.
      ELSE.
        LOOP AT lr_log->t_unit TRANSPORTING NO FIELDS WHERE value IN s_value[].
          EXIT.
        ENDLOOP.
      ENDIF.
      " 过滤不符合条件的数据
      IF sy-subrc <> 0.
        DELETE gt_log.
        CONTINUE.
      ENDIF.
    ENDIF.

    CALL METHOD lcl_unit_helper=>generate_flatdata
      EXPORTING
        it_unit   = lr_log->t_unit
        i_compact = p_ctl1
      IMPORTING
        et_flatdt = lr_log->t_flatdt
      RECEIVING
        rr_data   = lr_log->r_flatdata.
  ENDLOOP.
  lo_progress->finish( ).

ENDFORM.
*&---------------------------------------------------------------------*
*& FRM_DISPLAY
*&---------------------------------------------------------------------*
FORM frm_display.

  CASE 'X'.
    WHEN p_logh. " 日志抬头数据展示
      go_alv ?= NEW lcl_alv_logh( ).
    WHEN p_unit. " 单元数据展示
      go_alv ?= NEW lcl_alv_unit( ).
    WHEN p_flat." 扁平结构展示
      go_alv ?= NEW lcl_alv_flat( ).
    WHEN OTHERS.
      MESSAGE '无效的展示方式' TYPE 'S' DISPLAY LIKE 'E'.
      RETURN.
  ENDCASE.
  CALL SCREEN '9000'.

ENDFORM.
*&---------------------------------------------------------------------*
*& FRM_REDO
*&---------------------------------------------------------------------*
FORM frm_redo.

  go_alv->get_selected_uuid( IMPORTING et_uuid = DATA(lt_uuid) ).
  CHECK lt_uuid IS NOT INITIAL.

  LOOP AT gt_log_impl INTO DATA(lo_log).
    CHECK lo_log->check_active( ) = abap_true.
    DATA(l_log_kind) = lo_log->get_log_kind( ).
    DATA lt_uuid_tmp TYPE tt_uuid.
    CLEAR lt_uuid_tmp.
    LOOP AT gt_log REFERENCE INTO DATA(lr_log) WHERE log_kind = l_log_kind.
      READ TABLE lt_uuid INTO DATA(l_uuid) WITH KEY table_line = lr_log->uuid BINARY SEARCH.
      IF sy-subrc = 0.
        INSERT l_uuid INTO TABLE lt_uuid_tmp.
      ENDIF.
    ENDLOOP.
    IF lt_uuid_tmp IS NOT INITIAL.
      lo_log->redo( lt_uuid_tmp ).
    ENDIF.
  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*& FRM_F4_INTFID
*&---------------------------------------------------------------------*
FORM frm_f4_intfid.

  " SELECT
  "   ztintf~sysnm,
  "   ztintf~sysid,
  "   ztintf~funcname,
  "   tftit~stext
  "   FROM ztintf
  "   JOIN tftit ON spras = '1'
  "             AND ztintf~funcname = tftit~funcname
  "   INTO TABLE @DATA(lt_ztintf).
  " 
  " DATA l_dynprofield TYPE dynfnam.
  " GET CURSOR FIELD l_dynprofield.
  " 
  " CALL FUNCTION 'F4IF_INT_TABLE_VALUE_REQUEST'
  "   EXPORTING
  "     retfield        = 'SYSID'
  "     value_org       = 'S'
  "     dynprofield     = l_dynprofield
  "     dynpprog        = sy-repid
  "     dynpnr          = sy-dynnr
  "   TABLES
  "     value_tab       = lt_ztintf
  "   EXCEPTIONS
  "     parameter_error = 1
  "     no_values_found = 2
  "     OTHERS          = 3.
  " IF sy-subrc <> 0.
  "   MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
  "           WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  " ENDIF.

ENDFORM.

*&---------------------------------------------------------------------*
*& 初始化
*&---------------------------------------------------------------------*
INITIALIZATION.
  PERFORM frm_init.

**&---------------------------------------------------------------------*
**& 自定义日志表接口ID搜索帮助
**&---------------------------------------------------------------------*
*AT SELECTION-SCREEN ON VALUE-REQUEST FOR s_intfid-low.
*  PERFORM frm_f4_intfid.
*
*AT SELECTION-SCREEN ON VALUE-REQUEST FOR s_intfid-high.
*  PERFORM frm_f4_intfid.

*&---------------------------------------------------------------------*
*& START-OF-SELECTION
*&---------------------------------------------------------------------*
START-OF-SELECTION.
  PERFORM frm_main.

*&---------------------------------------------------------------------*
*& MODULE STATUS_9000 OUTPUT
*&---------------------------------------------------------------------*
MODULE status_9000 OUTPUT.
  go_alv->pbo( ).
  SET PF-STATUS 'STATUS'.
ENDMODULE.
*&---------------------------------------------------------------------*
*& MODULE USER_COMMAND_9000  INPUT
*&---------------------------------------------------------------------*
MODULE user_command_9000 INPUT.
  CASE sy-ucomm.
    WHEN 'REDO'. " 错误重处理
      PERFORM frm_redo.
    WHEN '&F03' OR '&F15' OR '&F12'.
      LEAVE TO SCREEN 0.
    WHEN OTHERS.
      go_alv->pai( ).
  ENDCASE.
ENDMODULE.
LIF_LOG实例参考代码
*&---------------------------------------------------------------------*
*& LCL_LOG_CUSTOM
*&---------------------------------------------------------------------*
CLASS lcl_log_custom DEFINITION .
  PUBLIC SECTION.
    INTERFACES lif_log.
ENDCLASS.
*&---------------------------------------------------------------------*
*& LCL_LOG_CUSTOM
*&---------------------------------------------------------------------*
CLASS lcl_log_custom IMPLEMENTATION.
*&---------------------------------------------------------------------*
*& lif_log~get_log_kind
*&---------------------------------------------------------------------*
  METHOD lif_log~get_log_kind.
    r_log_kind = 'CUSTOM'.
  ENDMETHOD.
*&---------------------------------------------------------------------*
*& LIF_LOG~GET_DATA
*&---------------------------------------------------------------------*
  METHOD lif_log~get_data.

    DATA ls_log TYPE ty_log.
    DATA ls_log_rawdata TYPE ty_log_rawdata.
    DATA l_zitem TYPE ty_log-zitem.

    SELECT
      ztintf~sysid,
      ztintf~funcname,
      tftit~stext
      FROM ztintf
      JOIN tftit ON ztintf~funcname = tftit~funcname
                AND tftit~spras = '1'
      INTO TABLE @DATA(lt_ztintf).
    SORT lt_ztintf BY sysid.

    SELECT * FROM ztlog
    WHERE zguid IN @s_uuid
      AND intfid IN @s_intfid
      AND zsender IN @s_snd
      AND zreceiver IN @s_rcv
      AND datum IN @s_date
      AND uzeit IN @s_time
      AND zstatus IN @s_mtype
      AND funcname IN @s_func
    INTO TABLE @DATA(lt_ztlog).

    SORT lt_ztlog BY zguid.

    LOOP AT lt_ztlog INTO DATA(ls_ztlog).
      AT NEW zguid.
        CLEAR l_zitem.
      ENDAT.
      l_zitem = l_zitem + 1.

      CLEAR ls_log.
      ls_log-uuid = ls_ztlog-zguid.
      ls_log-zitem = l_zitem.
      ls_log-intf_id = ls_ztlog-intfid. " 接口ID
      READ TABLE lt_ztintf REFERENCE INTO DATA(lr_ztintf)
      WITH KEY sysid = ls_ztlog-intfid BINARY SEARCH.
      IF sy-subrc = 0.
        ls_log-func_name = lr_ztintf->funcname.
        ls_log-func_text = lr_ztintf->stext.
      ENDIF.
      ls_log-io_flag = ls_ztlog-zzio. " 接口方向
      ls_log-zsender = ls_ztlog-zsender. " 发送方
      ls_log-zreceiver = ls_ztlog-zreceiver. " 接受方
      ls_log-zdate = ls_ztlog-datum.
      ls_log-ztime = ls_ztlog-uzeit.
      ls_log-zuser = ls_ztlog-uname.
      ls_log-mtype = ls_ztlog-zstatus.
      ls_log-zconut = ls_ztlog-zcount. " 条目统计
      INSERT ls_log INTO TABLE gt_log.

      IF ls_ztlog-zjson IS NOT INITIAL.
        CLEAR ls_log_rawdata.
        ls_log_rawdata-uuid = ls_ztlog-zguid.
        ls_log_rawdata-zitem = l_zitem.
        ls_log_rawdata-zformat = 'JSON'.
        ls_log_rawdata-zraw = ls_ztlog-zjson. " 原始数据
        INSERT ls_log_rawdata INTO TABLE gt_log_rawdata.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& LIF_LOG~REDO
*&---------------------------------------------------------------------*
  METHOD lif_log~redo.

    DATA l_fsyid TYPE string.
    DATA l_fname TYPE string.
    DATA l_sdata TYPE string.
    DATA l_infud TYPE string.
    DATA l_ftname TYPE string.
    DATA(lo_proxy) = NEW zcl_intf_proxy( ).

    LOOP AT it_uuid INTO DATA(l_uuid).
      READ TABLE gt_log INTO DATA(ls_log) WITH KEY
      uuid = l_uuid
      io_flag = '2'.
      IF ls_log-mtype = 'S'.
        lcl_progress=>step( |{ l_uuid }不需要重处理| ).
        CONTINUE.
      ELSE.
        READ TABLE gt_log INTO ls_log WITH KEY
        uuid = l_uuid
        io_flag = '1'.
        IF sy-subrc = 0.
          READ TABLE gt_log_rawdata INTO DATA(ls_log_rawdata) WITH KEY
          uuid = ls_log-uuid
          zitem = ls_log-zitem
          BINARY SEARCH.
          IF sy-subrc = 0.
            l_fsyid = ls_log-zsender. " 外部系统
            l_fname = ls_log-intf_id. " 接口ID
            l_sdata = ls_log_rawdata-zraw. " 请求报文
            l_infud = ls_log-uuid. " 报文ID
            l_ftname = ls_log-func_name. " 对应函数名
            CASE ls_log-zsender.
              WHEN 'SAP'.
                CALL METHOD lo_proxy->sap_data_to_out
                  EXPORTING
                    iv_fname  = l_fname
                    iv_sdata  = l_sdata
                    iv_infud  = l_infud
                    iv_ftname = l_ftname.
              WHEN OTHERS.
                CALL METHOD lo_proxy->out_data_to_sap
                  EXPORTING
                    iv_fsyid = l_fsyid
                    iv_fname = l_fname
                    iv_sdata = l_sdata
                    iv_infud = l_infud.
            ENDCASE.
            lcl_progress=>step( |{ l_uuid }已提交重处理| ).
          ENDIF.
        ENDIF.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.
*&---------------------------------------------------------------------*
*& lif_log~check_active
*&---------------------------------------------------------------------*
  METHOD lif_log~check_active.
    r_result = p_custom.
  ENDMETHOD.
ENDCLASS.