泛微OA doCreateRequest流程创建接口封装
·
业务背景
最近在做SAP项目的时候,OA创建流程,SAP一直调用的是OA的标准接口/api/workflow/paService/doCreateRequest,但标准接口的调用需要在SAP存很多OA自己的数据,比如OAID,明细表明,workflow ID等等,这些数据都是OA本身的配置数据,领导说存在SAP不合理,也有风险,想要把OA接口封装一下,调用的时候就传简单的报文数据,其他数据在OA做自建表自己取。
实现思路
1:封装后报文格式
请求头:token(获取token参照OA标准的接口)
USERID(默认值即可,真正的流程创建人在报文中)
JSON:
{
"UUID": "UUID",
"USERID": "工号",
"SYSTEM": "系统标识",
"requestName": "业务请求描述",
"mainData": [
{
"fieldName": "field1",
"fieldValue": "vlaue1"
}
],
"detailData": [
{
"workflowRequestTableRecords": [
{
"recordOrder": 0,
"workflowRequestTableFields": [
{
"fieldName": "field1",
"fieldValue": "vlaue1"
},
{
"fieldName": "field2",
"fieldValue": "vlaue1"
}
]
}
]
}
]
}
2:在OA建配置表
根据报文中的业务请求描述,在封装的接口中取出workflow id和明细表名
3:根据工号在表hrmresource中取到OAID并加密
4:调用前告知OA相关运维人员,进行接口注册,OA会提供相关数据(APPID、SPK、公钥、密钥等数据)
PS:获取token的接口需要用公钥加密密钥(第三方系统实现)
根据USERID获取workcode后也需要进行加密处理(本接口中实现)
封装后代码
package com.api.encapsulation;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.api.customization.hxwn.web.ForwardController;
import com.customization.hxwn.util.HttpKit;
import com.customization.hxwn.util.RequestUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.zxing.oned.OneDimensionalCodeWriter;
import org.apache.bcel.generic.NEW;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import weaver.conn.RecordSet;
import weaver.general.BaseBean;
import weaver.interfaces.util.GenUtil;
import zipkin2.Call;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* @Package: com.api.encapsulation
* @Author: cwt
* @DateTime: 2025/8/27
* @Description: SAP2OA通用接口封装
*/
@Path("/encapsulation/***/***")
public class SAP2OARequestDealCmd {
Logger logger = LoggerFactory.getLogger(SAP2OARequestDealCmd.class);//初始化日志监控器
HttpKit httpkit = new HttpKit();//实例化HTTP工具类
RequestUtil requestUtil = new RequestUtil();//实例化请求工具类
@Path("/***")
@POST
@Produces(MediaType.APPLICATION_JSON)
public JSONObject apiProcess(@Context HttpServletRequest req, @Context HttpServletResponse resp) throws IOException, InterruptedException {
HashMap<String, Object> logMap = new HashMap<>();
logger.info("doCreateRequest接口封装日志记录开始!!!");
RecordSet rs = new RecordSet();
String result = null;
int logId ;
//定义接口返回消息
JSONObject returnRes = new JSONObject();
JSONObject data = new JSONObject();
JSONObject auto_data = new JSONObject();//接口注册请求报文
JSONObject requestData = new JSONObject();//处理后的请求报文
JSONArray newDetailData = new JSONArray();//处理后的请求报文
try {
logger.info("请求头参数处理!!!");
Map<String, String> header = new HashMap<>();
header.put("token", req.getHeader("token"));
header.put("appid", "ecology");
String reqJSON = RequestUtil.getText(req);
JSONObject reqJSONObj = JSONObject.parseObject(reqJSON);
//字段校验
if (reqJSONObj == null) {
throw new Exception("请求参数不能为空");
}
if (!reqJSONObj.containsKey("USERID")) {
throw new Exception("USERID 参数缺失");
}
if (!reqJSONObj.containsKey("requestName")) {
throw new Exception("requestName 参数缺失");
}
if (!reqJSONObj.containsKey("SYSTEM")) {
throw new Exception("SYSTEM 参数缺失");
}
if (!reqJSONObj.containsKey("UUID")) {
throw new Exception("UUID 参数缺失");
}
logMap.put("uuid", reqJSONObj.get("UUID"));
logMap.put("systemName", reqJSONObj.get("SYSTEM"));
logMap.put("requestName", reqJSONObj.get("requestName"));
logMap.put("requestmessage", reqJSON);
//根据工号获取OAID
logger.info("根据工号处理OAID!!!");
String workCode = reqJSONObj.getString("USERID");
String sql = "select id from hrmResource where workCode = ?";
String id = "";
if ("1".equals(workCode)) {
id = "1";
} else {
rs.executeQuery(sql, workCode);
if (rs.next()) {
id = rs.getString("id");
} else {
throw new Exception("请联系管理员,根据用户工号未查询到用户ID!");
}
}
//给ID加密
sql = "SELECT * FROM ECOLOGY_BIZ_EC WHERE APPID = ?";
rs.executeQuery(sql, "ecology");
String rsaUserId = "";
if (rs.next()) {
rsaUserId = rs.getString("SERVER_PUBLIC_KEY");
} else {
throw new Exception("请联系管理员,用户账号加密失败!");
}
RSA rsa = new RSA(null, rsaUserId);
String encryptSecret = rsa.encryptBase64(id, StandardCharsets.UTF_8, KeyType.PublicKey);
header.put("userId", encryptSecret);
logger.info("流程userId加密参数:{}", encryptSecret);
//根据业务请求描述获取流程id和明细表名
requestData.put("requestName", reqJSONObj.getString("requestName"));
logger.info("业务请求描述:{}", reqJSONObj.getString("requestName"));
logger.info("WORKFLOW_ID、DETAIL_DATA字段处理!!!");
String REQUEST_NAME = reqJSONObj.getString("requestName");
sql = "select * from uf_sap2oapam where REQUEST_NAME = ?";
rs.executeQuery(sql, REQUEST_NAME);
String workflowId;
String detailData;
logger.info("获取流程ID以及明细表名!!!");
if (rs.next()) {
workflowId = rs.getString("WORKFLOW_ID");
logger.info("workflowId:{}", workflowId);
detailData = rs.getString("DETAIL_DATA");
logger.info("detailData:{}", detailData);
} else {
throw new Exception("请联系管理员,根据业务请求描述未找到流程ID!");
}
//WORKFLOW_ID(业务流程ID嵌套至json串顶部)
logger.info("添加流程id和主表数据至报文中");
requestData.put("workflowId", workflowId);
requestData.put("mainData", reqJSONObj.getJSONArray("mainData"));
//明细表detailData数据处理
// String[] detailDataName = detailData.split(",");
JSONArray detailDataArr = reqJSONObj.getJSONArray("detailData");
if (detailDataArr != null) {
logger.info("报文中存在明细表数据!");
for (int i = 0; i < detailDataArr.size(); i++) {
JSONObject detailObj = new JSONObject();
if (i > 0) {
if (rs.next()) {
detailData = rs.getString("DETAIL_DATA");
} else {
throw new Exception("OA配置明细行数据与传入明细报文不一致,请联系管理员检查!");
}
}
detailObj.put("tableDBName", detailData);
detailObj.put("workflowRequestTableRecords", reqJSONObj.getJSONArray("detailData").getJSONObject(i).getJSONArray("workflowRequestTableRecords"));
newDetailData.add(detailObj);
}
requestData.put("detailData", newDetailData);
logger.info("添加明细表数据至报文中值为:{}", detailData);
}
logger.info("数据填充完毕");
returnRes.put("code", "PARAM_ERROR");
returnRes.put("data", data);
returnRes.put("msg", "");
returnRes.put("errMsg", "");
returnRes.put("reqFailMsg", "");
String oaHost = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort();
logger.info("weaver-流程创建参数:Header:{},Param:{}", JSONObject.toJSONString(header), requestData.toJSONString());
result = httpkit.doPost(oaHost + "/api/workflow/paService/doCreateRequest", header, requestData.toJSONString());
logger.info("weaver-流程创建结果:{}", result);
JSONObject res = JSONObject.parseObject(result);
returnRes.put("data", res.getJSONObject("data"));
returnRes.put("msg", res.getJSONObject("msg"));
returnRes.put("code", res.getString("code"));
returnRes.put("errMsg", res.getString("errMsg"));
returnRes.put("reqFailMsg", res.getString("reqFailMsg"));
} catch (IOException e) {
returnRes.put("code", "PARAM_ERROR");
returnRes.put("msg", "接口异常!" + e.getMessage());
logger.error("weaver-流程创建失败:", e);
} catch (Exception e) {
returnRes.put("code", "PARAM_ERROR");
returnRes.put("msg", "数据处理异常!" + e.getMessage());
logger.error("数据处理异常:", e);
}
if ("success".equals(returnRes.getString("code"))){
String respJson = returnRes.toString();
logMap.put("type", "S");
logMap.put("message", "流程创建成功!");
logMap.put("responsemessage", respJson);
}else {
String respJson = returnRes.toString();
logMap.put("type", "E");
logMap.put("message", "流程创建失败!");
logMap.put("responsemessage", respJson);
}
String getLogSql = "select * from uf_sap2oapam where REQUEST_NAME = ?" ;
String modeId = "" ;
rs.executeQuery(getLogSql,"流程创建接口日志表") ;
if (rs.next()){
modeId = rs.getString("WORKFLOW_ID") ;
logId = GenUtil.createModeData(Integer.parseInt(modeId),1,logMap) ;
(new BaseBean()).writeLog("流程创建接口日志表ID为:" + logId) ;
logger.info("日志存储成功!");
}else {
logger.info("日志表存储失败,请检查配置表:uf_sap2oapam !");
}
return returnRes;
}
}
PS:非专业java工作者,第一次写Java可能有很多问题,用的话建议根据实际业务再优化
SAP调用类封装
1:PO或其他接口中间件配置(就不说了)
2:SAP调用封装



METHOD data_process_send2oa.
DATA : lv_uuid TYPE guid,
lcl_po TYPE REF TO zclco_si_s4_oa060_out_syn,
ls_response TYPE zclmt_oa060_resp.
MOVE-CORRESPONDING in_t_data TO it_data_table .
CALL METHOD me->get_token. "获取token
CHECK es_return IS INITIAL.
TRY.
lv_uuid = cl_uuid_factory=>create_system_uuid( )->create_uuid_x16( ).
CATCH cx_uuid_error INTO DATA(exception).
es_return-msg = exception->if_message~get_text( ) .
ENDTRY.
CHECK es_return IS INITIAL.
ls_request-mt_oa060_req-token = token .
ls_request-mt_oa060_req-sysid = 'SAP' .
ls_request-mt_oa060_req-request_name = in_v_requestname .
ls_request-mt_oa060_req-userid = sy-uname .
ls_request-mt_oa060_req-uuid = lv_uuid .
CALL METHOD me->data_mapping.
"调用接口
TRY.
CREATE OBJECT lcl_po.
CALL METHOD lcl_po->si_s4_oa060_out_syn
EXPORTING
output = ls_request
IMPORTING
input = ls_response.
IF ls_response-mt_oa060_resp-code <> 'SUCCESS'.
es_return-type = 'E'.
es_return-msg = ls_response-mt_oa060_resp-msg.
ELSE. "流程提交成功
es_return-type = 'S'.
es_return-msg = '流程创建成功!'.
ENDIF.
CATCH cx_ai_system_fault INTO DATA(lcx_excep).
es_return-type = 'E'.
es_return-msg = lcx_excep->get_text( ).
ENDTRY.
ENDMETHOD.

METHOD field_process.
IF lv_type_kind = 'D' AND lv_value IS NOT INITIAL. "日期类型需要做内外码转换
lv_value = lv_value+0(4) && '-' && lv_value+4(2) && '-' && lv_value+6(2).
ELSEIF lv_type_kind = 'D' AND lv_value IS INITIAL.
CLEAR lv_value .
ELSEIF lv_type_kind = 'P'. "去空格
CONDENSE lv_value NO-GAPS.
ELSEIF lv_type_kind = 'T'. "时间类型处理
IF lv_value IS INITIAL.
CLEAR lv_value.
ELSE.
lv_value = lv_value+0(2) && ':' && lv_value+2(2).
ENDIF.
ENDIF.
ENDMETHOD.
METHOD data_mapping.
DATA: lr_head TYPE REF TO cl_abap_structdescr,
lr_item TYPE REF TO cl_abap_structdescr,
lr_descr TYPE REF TO cl_abap_tabledescr,
ls_comp TYPE abap_compdescr.
FIELD-SYMBOLS : <lt_data> TYPE STANDARD TABLE .
"将主表数据填充至REQ中
LOOP AT it_data_table ASSIGNING FIELD-SYMBOL(<ft_item>) WHERE type = '1' .
ASSIGN <ft_item>-table->* TO <lt_data> .
IF <lt_data> IS ASSIGNED .
lr_descr ?= cl_abap_typedescr=>describe_by_data( <lt_data> ).
lr_head ?= lr_descr->get_table_line_type( ).
READ TABLE <lt_data> ASSIGNING FIELD-SYMBOL(<fs_item>) INDEX 1 .
LOOP AT lr_head->components INTO ls_comp.
APPEND INITIAL LINE TO ls_request-mt_oa060_req-main_data ASSIGNING FIELD-SYMBOL(<fs_main_data>) .
<fs_main_data>-field_name = ls_comp-name .
ASSIGN COMPONENT ls_comp-name OF STRUCTURE <fs_item> TO FIELD-SYMBOL(<fs_value>) .
IF <fs_value> IS ASSIGNED .
me->field_process( EXPORTING lv_type_kind = ls_comp-type_kind CHANGING lv_value = <fs_value> ).
<fs_main_data>-field_value = <fs_value> .
ENDIF .
ENDLOOP .
ENDIF .
ENDLOOP .
"将明细表数据填充至REQ中
LOOP AT it_data_table ASSIGNING <ft_item> WHERE type = '2' .
ASSIGN <ft_item>-table->* TO <lt_data> .
IF <lt_data> IS ASSIGNED .
lr_descr ?= cl_abap_typedescr=>describe_by_data( <lt_data> ).
lr_item ?= lr_descr->get_table_line_type( ).
APPEND INITIAL LINE TO ls_request-mt_oa060_req-detail_data ASSIGNING FIELD-SYMBOL(<fs_detail_data>) .
LOOP AT <lt_data> ASSIGNING <fs_item> .
APPEND INITIAL LINE TO <fs_detail_data>-workflow_request_table_records ASSIGNING FIELD-SYMBOL(<fs_records>) .
<fs_records>-record_order = '0' .
LOOP AT lr_item->components INTO ls_comp.
APPEND INITIAL LINE TO <fs_records>-workflow_request_table_fields ASSIGNING FIELD-SYMBOL(<fs_fields>) .
<fs_fields>-field_name = ls_comp-name .
ASSIGN COMPONENT ls_comp-name OF STRUCTURE <fs_item> TO <fs_value> .
IF <fs_value> IS ASSIGNED .
me->field_process( EXPORTING lv_type_kind = ls_comp-type_kind CHANGING lv_value = <fs_value> ).
<fs_fields>-field_value = <fs_value> .
ENDIF .
ENDLOOP.
ENDLOOP .
ENDIF .
ENDLOOP .
ENDMETHOD.

METHOD get_token.
DATA:lcl_po TYPE REF TO zpoco_si_s4_oa006_out_syn,
ls_request TYPE zpomt_oa006_req,
ls_response TYPE zpomt_oa006_resp.
ls_request-mt_oa006_req-appid = 'ecology'.
IF sy-mandt = '200' OR sy-mandt = '300' OR sy-mandt = '400' .
ls_request-mt_oa006_req-secret = 'sxIXPsCg+PKlJDcIrD5RJpFzayz1CL6JmeVzHcu4PmX/bYAkOhnc4DauiEBh/U1xSuhB' &&
'1gTMqhVTfa/sCwQPPdqDz9x8c+hwTmRWlpQEj1bn60qU0bIdaMv0NHiWV4FEAY74dqul' &&
'WIwdDtXdCrJOx4vzTp/Cu/KV8QpjbOaj0tb/aeUgDD+5fWi6p57AkJ+7R9I+m5jcYaXK' &&
'NTg3ZXjJKsDU5thVQM9IqZfrnw1YHNkuupmkj/ulJzzXgK1h25CogvouXgUh9TugHgvR' &&
'c7S9Hirzc29XgIpUpiXChPhw6IODqa9+5d4IZzaYgz227rJ8y0VhESTHDuoXNM9Jg=='.
ELSEIF sy-mandt = '500' OR sy-mandt = '600' .
ELSEIF sy-mandt = '800' .
ENDIF .
TRY.
CREATE OBJECT lcl_po.
CALL METHOD lcl_po->si_s4_oa006_out_syn
EXPORTING
output = ls_request
IMPORTING
input = ls_response.
token = ls_response-mt_oa006_resp-token.
IF token IS INITIAL.
es_return-type = 'E'.
es_return-msg = ls_response-mt_oa006_resp-msg.
ENDIF.
CATCH cx_ai_system_fault INTO DATA(lcx_excep).
es_return-type = 'E'.
es_return-msg = lcx_excep->get_text( ).
ENDTRY.
ENDMETHOD.
SAP调用案例:
*-------------------------------------Str--------------------------------------*
"可封装于include中
TYPES : BEGIN OF ty_ref ,
type TYPE char1,
table TYPE REF TO data,
END OF ty_ref .
DATA : zcl_test TYPE REF TO zcl_send2oa_apigeneral,
it_tab TYPE TABLE OF ty_ref.
FIELD-SYMBOLS : <fs_outer> TYPE ty_ref,
<fs_inner_table> TYPE STANDARD TABLE.
DEFINE _data_process.
APPEND INITIAL LINE TO it_tab ASSIGNING <fs_outer>.
CREATE DATA <fs_outer>-table LIKE lt_data.
ASSIGN <fs_outer>-table->* TO <fs_inner_table>.
MOVE-CORRESPONDING &1 TO <fs_inner_table> .
<fs_outer>-type = &2 .
END-OF-DEFINITION.
*-------------------------------------End--------------------------------------*
"主表测试数据
SELECT * FROM mara INTO TABLE @DATA(lt_data) WHERE MATNR = '123' .
_data_process lt_data '1'.
"明细表测试数据
SELECT * FROM ekko INTO TABLE @DATA(lt_ekko) .
_data_process lt_ekko '2'.
TRY.
CREATE OBJECT zcl_test.
CALL METHOD zcl_test->data_process_send2oa
EXPORTING
in_t_data = it_tab
in_v_requestname = '流程名称'.
CATCH cx_ai_system_fault INTO DATA(lcx_excep).
ENDTRY.


更多推荐


所有评论(0)