注意事项:
- 同步前、中、后函数需要统一设置入参 ,类型为Map,参数名为syncArg。返回值也必须是map。
- 在同步后函数中,不能同时存在Fx.object.update操作,和已映射字段(包括数据范围使用的字段),避免同步后修改数据导致循环同步。以上逻辑会在启用集成流时进行校验。如果能确定不会触发循环同步,且需要有上面的情况,请咨询集成平台研发提供绕过的方法。
一、syncArg的属性
syncDataId 同步数据id
sourceTenantId 源企业id
sourceObjectApiName 源对象apiName
sourceEventType 源事件类型, 1是 "新增", 2是“更新”
destTenantId 目标企业id
destObjectApiName 目标对象apiName
destEventType 目标事件类型, 1是 "新增", 2是“更新”
objectData 主对象数据, 可以通过字段APIname获取字段的值。
details 从对象数据, 可以通过字段APIname获取字段的值。
说明: 同步前的函数,objectData ,details 携带的是源对象数据。且不支持修改目标对象数据。
同步中objectData ,details 携带的是源对象数据, 且支持修改目标对象数据。
同步后objectData ,details 携带的是目标对象数据。
调用目标系统写接口之前执行 同步中函数,调用写接口结束后执行 同步后函数。
从对象数据要是新增事件才有数据。
二、同步前函数
在同步前自定义函数做数据过滤
返回结果中包含"isExec":false,即该次同步将不再往后执行,目标数据将不会做出相应的更改。
返回结果中包含"isCover":false, 则自定义函数中返回的结果,不会覆盖原始数据。
objectData里面有可能是主对象的数据也有可能是从对象的数据,要根据object_describe_api_name判断当前经过同步前函数的是主对象数据还是从对象数据。
测试场景: 在自定义函数返回的Map中加入"isExec":"false",或"isExec":false目标数据将不会发生更改。
Map map = ["details":syncArg.details, "objectData":syncArg.objectData, "isExec":false, "isCover":false];
return map;
三、同步中函数
在同步中自定义函数中,先修改数据,再把修改的数据写入目标系统
log.info("事件类型:" + syncArg.destEventType);
log.info(syncArg.objectData);
log.info(syncArg.details);
Map objectData = syncArg.objectData as Map;
objectData.name= "自定义函数修改了该字段"
Map map = ["details":syncArg.details, "objectData":syncArg.objectData];
log.info(map);
return map;
四、同步后函数
K3Cloud订单从CRM同步到ERP后回填订单id和编码
使用场景:如题
示例函数:
//指定对象,订单
def sourceObjectApiName = 'SalesOrderObj'
//回填ERPId字段
def erpIdF = "field_86Xfn__c"
//回填ERP编码字段
def erpNoF = "field_K4xdf__c"
def destEventType = syncArg.destEventType
def completeDataWriteResult = syncArg.completeDataWriteResult as Map
if( syncArg.sourceObjectApiName!= sourceObjectApiName||
completeDataWriteResult.destEventType!=1||
!completeDataWriteResult.success
){
log.info("不处理")
//以下情况直接返回
//不是处理订单
//不是新增
//没成功
return syncArg;
}
log.info("开始回填id")
String sourceDataId = syncArg.sourceDataId
def writeResult = completeDataWriteResult.writeResult as Map
String destDataId = writeResult.destDataId
def split = destDataId.split("#", 2)
def upArg = [(erpIdF):split[0],(erpNoF):split[1]]
log.info(upArg)
def upR = Fx.object.update(sourceObjectApiName, sourceDataId, upArg )
log.info("回填结果:"+upR)
五、转义自定义函数(访问ERP接口做格式转换)
ERP系统原生API,如果不是标准的开放接口,没有预置到集成平台中,则需要通过转义自定义函数去调用ERP的接口,再转为标准API。
如果要做条件过滤,不能放在转义自定义函数中,数据范围设置能满足的用数据范围,不满足的用同步前函数。
参数的类型必须是Map, 名字固定为 syncArg。每个接口的参数,可以通过log.info("请求参数:"+syncArg); 打印出来观察。
queryMasterBatch 获取系统传递的参数
//调用参数:
log.info("请求参数:"+syncArg);
Integer offset=syncArg["objectData"]==null?0:(Integer)syncArg["objectData"]["offset"];
Integer limit=syncArg["objectData"]==null?10:(Integer)syncArg["objectData"]["limit"];
Long startTime=syncArg["objectData"]==null?0:(Long)syncArg["objectData"]["startTime"];
Long endTime=syncArg["objectData"]==null?0:(Long)syncArg["objectData"]["endTime"];
queryMasterById 获取系统传递的参数
//调用参数:
log.info("请求参数:"+syncArg);
String dataId=syncArg["objectData"]["dataId"];
六、异步推送接口通过自定义函数转义
接口URL://www.sxgtbz.com/erp/syncdata/open/objdata/asyncpush
使用场景:客户推送的数据格式跟平台要求的数据格式不一致,需要通过自定义函数将推送的数据格式转成平台要求的数据格式
推送执行逻辑:外部接口主动调用集成平台的推送接口把数据推送的到集成平台的缓存里。平台会异步的从缓存表拉取数据,如果写入缓存表失败则直接返回报错信息,如果是从缓存拉取数据后处理失败需要到集成平台的数据维护里面查看数据错误原因。
请求方式:POST
请求 Header 参数:
字段 | 说明 |
token | 请求认证字符串【请联系纷享研发侧提供】 |
tenantId | 请联系纷享研发侧提供 |
dataCenterId | 单账套不需要推送该字段,多账套必须推送并填写; |
objectApiName | ERP 侧真实的对象名称数据同步设置->ERP 对象设置->ERP 对象编码可以找到 |
version | v1 |
operationType | 3 作废,其他状态不需要推送该字段; |
推送步骤:
1、创建erp对象
2、创建同步策略.
3、增加推送的自定义函数(不管非标还是标准推送,只要走推送接口就需要增加这个函数)
log.info("请求参数:"+Fx.json.toJson(syncArg))
String dataStr=syncArg["objectData"]["pushData"];
Map pushDataMap=Fx.json.parse(dataStr);
List<Map> pushDatas=[];
if( pushDataMap["data"] instanceof List){//
/*推送一个数据示例数据
{
"data": {
"number": "A2001",
"lrap_days": "2",
"name": "测试产品41"
}
}
*/
pushDatas=pushDataMap["data"] as List
}else if( pushDataMap["data"] instanceof Map ){
/*//推送多个数据示例数据
{
"data": [
{
"number": "A2001",
"lrap_days": "2",
"name": "测试产品41"
}
]
}
*/
pushDatas.add(pushDataMap["data"] as Map)
}
List resultList=[]
pushDatas.each{
map->
Map dataMap=map as Map;
String id=map["code"];//获取主对象中哪个字段可以标识唯一值
dataMap.put("id",id);//很重要必须有这个key和值
Map data=["masterFieldVal":dataMap]//转成平台的格式
resultList.add(data)
}
return ["dataList":resultList];//返回的是数组类型的
推送失败示例
推送成功
七、场景函数
根据源数据ID触发源系统数据往目标系统同步(必须实现getById接口)
使用场景:可用于解决指定源数据id同步数据的需求
示例函数:
Map header=[:]
Map params1=[:]
params1.put("erpObjectApiName","xxObj"); //目标对象apiName,参数名称有点奇怪,因为一开始只支持crm->erp的,
//erp中间对象apiname
params1.put("crmDataId","id"); //源数据Id,参数名称有点奇怪,因为一开始只支持crm->erp的
params1.put("crmObjectApiName","xxxobj"); //源对象apiName,参数名称有点奇怪,因为一开始只支持crm->erp的
Map commonParam=["type":"manualSyncCrm2Erp","params":Fx.json.toJson(params1)];
def result1=Fx.proxy.callAPI("erp.syncData.executeCustomFunction",header,commonParam);
log.info(result1)
根据CRM数据ID触发ERP往CRM数据同步更新(必须存在映射关系)
//必填,CRM对象apiName
def crmObjApiName = context.data.object_describe_api_name;
//必填,CRM数据Id
def crmDataId = context.data._id
//必填,ERP中间对象apiName
def erpObjApiName = "BD_Customer.BillHead"
//该类型用于触发ERP往CRM数据同步,使用crm数据id
def type = "manualSyncErpDataByCrmDataId"
def params = ["crmObjectApiName":crmObjApiName,
"crmDataId":crmDataId,
"erpObjectApiName":erpObjApiName]
def arg = ["type":type,
"params":Fx.json.toJson(params)]
def ret = Fx.proxy.callAPI("erp.syncData.executeCustomFunction", [:], arg)
//同步结果处理逻辑:
log.info(ret)
调用金蝶云星空的webapi
//下面的代码片段,演示在纷享的CRM中如何调用webapi。
// K3Cloud地址
String baseUrl = "http://172.31.100.60/K3Cloud/"
//url接口变量
String auth = "Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUser.common.kdsvc"
String query = "Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.ExecuteBillQuery.common.kdsvc"
String save = "Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Save.common.kdsvc"
String submit = "Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Submit.common.kdsvc"
String audit = "Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Audit.common.kdsvc"
//登录信息
List param = ["5ec229fad54306", "ces", "888888", 2052]
//请求参数体 Map body = ["parameters": param]
Map headers = ["Content-Type": "application/json; charset=utf-8"]
Map content = [:]
//登录连接
def (Boolean error, HttpResult data, String errorMessage) = Fx.http.post(baseUrl + auth, headers, body)
Map cookieHeader = [:]
if (!error)
{
if (data.statusCode == 200)
{
content = json.parse(data.content as String)
String logInType = content."LoginResultType"
if (logInType == "1" || logInType == "-5")
{
log.info("登录成功,content:" + content)
} else {
log.info("登录失败,content:" + content)
}
} else {
log.info("请求失败,HttpResult:" + data)
}
} else {
log.info("请求错误,errorMessage:" + errorMessage)
}
//单据查询示例(查询库存)
body.put("parameters", [ [ "FormId" : "STK_Inventory",
"FieldKeys" : "FID,FStockId.FNumber,FMaterialId.FNumber,FMaterialId.FName,FLot.FNumber,FLot,FS tockLocId,FBaseQty,FBaseAVBQty,FUpdateTIme",
"FilterString": "FMaterialId.FNumber='CH4453'",
"OrderString" : "",
"TopRowCount" : "0", "StartRow" : "0", "Limit" : "0" ] ])
(error, data, errorMessage) = Fx.http.post(baseUrl + query, headers, body)
log.info(data)
/*---------------------------创建订单示例---------------------------------------*/
//从crm获取订单数据
Map salesOrderMap = context.data log.info("订单:"+json.toJson(salesOrderMap))
//获取产品数据(一个)
//订单产品
Map salesProductMap = ((List)context.details.SalesOrderProductObj)[0]
log.info("订单产品:"+salesProductMap)
//产品 Map productData
(error, productData, errorMessage) = object.findById("ProductObj", salesProductMap.product_id as String) log.info("product:"+productData)
// 新增订单
Map model = [:]
//单据类型,必填
model.put("FBillTypeID", ["FNumber": "XSDD01_SYS"])
//销售组织,必填
model.put("FSaleOrgId", ["FNumber": "003"])
//客户,必填
model.put("FCustId", ["FNumber": "4326rtyu"])
//日期,必填
model.put("FDate", "2020-05-22 00:00:00")
//销售员,必填
model.put("FSalerId", ["FNumber": "88888"])
model.put("FSaleDeptId", ["FNumber": ""])
//k3cloud编码从crm订单同步过去
model.put("FBillNo ", salesOrderMap.name) //订单明细
Map material = [:]
//物料编码,必填,物料需分配到对应组织
material.put("FMaterialId", ["FNumber": productData.product_code])
//销售单位,必填
material.put("FUnitID", ["FNumber": "Pcs"])
//销售数量
material.put("FQty", salesProductMap.quantity)
//含税单价
material.put("FTaxPrice", salesProductMap.sales_price)
//订单明细,必填
model.put("FSaleOrderEntry", [material])
//财务信息
Map finance = [:]
//结算币别,必填
finance.put("FSettleCurrId", ["FNumber": "PRE001"])
finance.put("FExchangeTypeId", ["FNumber": "HLTX01_SYS"])
finance.put("FExchangeRate", 1)
model.put("FSaleOrderFinance",finance)
body.put("parameters",["SAL_SaleOrder", json.toJson(["Model":model])])
log.info(json.toJson(model))
(error, data, errorMessage) = Fx.http.post(baseUrl + save, headers, body)
log.info(data)
//提交
body.put("parameters",["SAL_SaleOrder", ["Numbers": [salesOrderMap.name]]])
(error, data, errorMessage) = Fx.http.post(baseUrl + submit, headers, body) log.info(data)
//审核
(error, data, errorMessage) = Fx.http.post(baseUrl + audit, headers, body)
log.info(data)
//这里只是示例,请按照当前自定义函数要求的实际返回类型 进行返回。
return "111"
通过CRM自定义函数操作中间表
Map header=[:]
//创建数据映射
Map param1=["ployDetailId":"155bd981457343f291e0edc13776217f",//策略明细id(见下图),如果策略被删除重新建了,这里需要改动
"sourceObjectApiName":"AccountObj",//源对象apiName,如果对象apiName变了,这里要改动
"destObjectApiName":"BD_Customer.BillHead",//目标对象apiName,如果对象apiName变了,这里要改动
"sourceDataId":"sourceDataId123",//源对象数据id
"destDataId":"destDataId123666",//目标对象数据id
"sourceDataName":"sourceDataName3666",//源对象数据name属性
"destDataName":"destDataName66",//目标对象数据name属性
"remark":"remark1341"];//备注
def result1=Fx.proxy.callAPI("erp.syncData.createSyncDataMapping",header,param1);
//[false, HttpResult(statusCode=200, content={"errCode":"s106240000","errMsg":"成功"}, bytes=null), ] s106240000成功,其他失败
log.info(result1)
//根据源对象数据id更新目标对象数据id
Map param2=["sourceObjectApiName":"AccountObj",//源对象apiName,如果对象apiName变了,这里要改动
"destObjectApiName":"BD_Customer.BillHead",//目标对象apiName,如果对象apiName变了,这里要改动
"sourceDataId":"sourceDataId123",//源对象数据id
"destDataId":"destDataId123666"]//目标对象数据id
def result2=Fx.proxy.callAPI("erp.syncData.updateSyncDataMapping",header,param2);
//[false, HttpResult(statusCode=200, content={"errCode":"s106240000","errMsg":"成功"}, bytes=null), ] s106240000成功,其他失败
log.info(result2)
//查询源对象数据id是否存在映射关系
Map param3=["sourceObjectApiName":"AccountObj",//源对象apiName,如果对象apiName变了,这里要改动
"destObjectApiName":"BD_Customer.BillHead",//目标对象apiName,如果对象apiName变了,这里要改动
"sourceDataId":["sourceDataId123"]]//源对象数据ids,List
def result3=Fx.proxy.callAPI("erp.syncData.getSyncDataMappingBySourceDataId",header,param3);
// [false, HttpResult(statusCode=200, content={"data":{"sourceDataId123":{"sourceDataId":"sourceDataId123","isCreated":true,"destDataId":"destDataId123666","sourceDataName":"sourceDataName1233","updateTime":1611047455451,"lastSyncStatus":6,"destDataName":"destDataName123","destTenantId":"81138","sourceObjectApiName":"AccountObj","destObjectApiName":"BD_Customer.BillHead","sourceTenantId":"81138","statusName":"新增成功","id":"aa46ed320312476485e932a1ca4b4263","lastSyncDataId":"92c86fb175254e54b990bd86b6ce1145","status":1}},"errCode":"s106240000","errMsg":"成功"}, bytes=null), ]
//s106240000成功,其他失败
//data是一个Map,存储着存在映射关系的数据,key是源数据id,value是已存在的对应关系,
log.info(result3)
抓取策略明细ID ployDetailId
OA待办消息同步通过crm人员创建映射表
//channel合法值有 ERP_K3CLOUD,ERP_SAP,ERP_U8,OA,STANDARD_CHANNEL
//"dataType":"employee" 表示新建的是人员
//dataCenterId是数据中心id
//fsDataId是crm上的数据id.对于人员,这里是员工ID,不是crm人员对象的数据id.
//erpDataId是erp上的数据id
//fsDataName是crm的数据名称
//erpDataName是erp上的数据名称
Map data = [
"dataCenterId":"701916*******319168",
"channel":"OA",
"dataType":"employee_oa",
"fsDataId":fsDataId,
"fsDataName":fsDataName,
"erpDataId":erpDataId,
"erpDataName":erpDataName
];
def ret = Fx.proxy.callAPI("erp.syncData.createErpfieldmapping", [:], data);
Fx.log.info("ret is : "+ret)
八、标准API接口说明
标准API接口的入参和返回值,都有严格的限制,入参的名称叫syncArg,类型为Map类型,返回值为Map类型,Map里面的字段,必须严格按照集成平台
连接器管理→连接对象→生成API页面 http 请求response 对应的格式来返回数据,一定要严格按照这个格式来返回,谨记!
标准API接口返回值示例图:
对于k3cloud渠道,接口的返回值中code=0代表成功,非0代表失败,code=0时,data字段必须有值,并且必须正确返回masterDataId对应的主对象ID和明细对象ID字段,并且必须按照集成平台要求的格式和顺序返回明细对象的ID值(如果对象有明细对象的话)
对于标准渠道和SAP渠道,接口返回值中的code字段,message字段,data字段名称以及成功的code码都可以自定义,可以在集成平台 连接器管理→连接配置页面进行设置,如图:
这几个字段名称和成功码一旦配置,在自定义函数里面,就必须严格按照这个配置里面的字段来返回数据
8.1 asyncpush推送数据接口说明
asyncpush是异步执行的接口,接口返回成功,只是表示接口已经成功到达集成平台,后面同步过程是异步批量处理数据的。
除非对时效性有很高的要求,否则请采用查询接口 queryMasterBatch 返回数据。
使用查询接口返回数据的好处是,容错性好,刷历史数据,重新同步这些费时费力的操作都有成熟的工具支撑,而不再需要客户方IT人员干预。
推送参考文档:接口开发
8.2 create
CRM->ERP创建数据时,调用该接口。
该接口要求在返回值中带上 ERP主键,ERP主键不能异步返回。
如果ERP上该接口不能在30s内返回,则ERP侧需要进行判重处理。在CRM上用同样的数据进行重试的时候,要返回上一次成功创建的结果。
参数说明
{ "objAPIName":"ERP对象APIName", "masterFieldVal":"主数据", "detailFieldVals":"从数据列表" }
返回值说明:
{ "code": "错误返回码", "message": "错误提示语", "data": { "masterDataId": "主数据主键", "detailDataIds": { "每个明细对象的数据 主键列表" } } }
返回值示例:
8.3 update
CRM->ERP更新主数据时,调用该接口。 主从数据一起覆盖更新目标数据。
参数
{ "objAPIName":"ERP对象APIName", "masterFieldVal":"主数据", "detailFieldVals":"从数据列表" }
返回:
{ "code": "错误返回码", "message": "错误提示语", "data": { "masterDataId": "主数据主键", "detailDataIds": { "每个明细对象的数据 主键列表" } } }
返回值示例:
8.4 queryMasterBatch
ERP->CRM 增量数据查询接口,集成平台定时轮询,调用该接口获取增量变化的数据。
参数说明:
objAPIName:ERP对象APIName
startTime:数据变更的开始时间(unix时间戳,单位毫秒)
endTime:数据变更的结束时间(unix时间戳,单位毫秒)
includeDetail:返回结果是否包含从数据
offset:获取记录的偏移
limit:当前请求记录条数
返回值说明:
{ "code": "错误返回码", "message": "错误提示语", "data": { "totalNum": "总记录数", "dataList": [{ "objAPIName": "主对象名", "masterFieldVal": {主对象数据}, "detailFieldVals": { "从对象1": [{从对象1数据列表}], "从对象2": [{从对象2数据列表}] } }, { "objAPIName": "主对象名", "masterFieldVal": {主对象数据}, "detailFieldVals": { "从对象1": [{从对象1数据列表}], "从对象2": [{从对象2数据列表}] } }] } }
该时间片轮询终止说明:
(1)返回的数据条数为0。比如6分钟的数据,分10页返回,如果查询第2页时,返回0条数据,则从第3页开始往后不会再轮询了。会继续轮询下一个6分钟的数据。
(2)ERP侧接口报错,跳过本时间片轮询。会继续轮询下一个时间片。
返回值示例:
8.5 queryMasterById
ERP->CRM 通过主键ID获取数据。重新同步时会调用该接口获取最新的数据。
参数说明:
objAPIName:ERP对象APIName
dataId:数据主键
includeDetail: 返回结果是否包含从数据
返回值说明:
{ "code": "错误返回码", "message": "错误提示语", "data": { "objAPIName": "ERP对象APIName", "masterFieldVal": {主对象数据}, "detailFieldVals": { "从对象APIName": [{从对象数据列表}] } } }
返回值示例:
8.6 invalid
CRM->ERP 作废主数据时,调用该接口。
参数说明:
返回结果说明:
{
"errCode": "错误返回码",
"errMsg": "错误提示语"
}
{
"errCode": "错误返回码",
"errMsg": "错误提示语"
}
返回值示例:
8.7 XML格式接口的查询列表接口函数示例
/*列表查询*/
//调用参数:
log.info("请求参数:"+syncArg);
Integer offset=syncArg["objectData"]==null?0:(Integer)syncArg["objectData"]["offset"];//请求的偏移量
Integer limit=syncArg["objectData"]==null?50:(Integer)syncArg["objectData"]["limit"];//请求的数据量
Long startTime=syncArg["objectData"]==null?0:(Long)syncArg["objectData"]["startTime"];//请求开始时间,时间戳格式
Long endTime=syncArg["objectData"]==null?0:(Long)syncArg["objectData"]["endTime"];//请求结束时间,时间戳格式
//可以在此处修改请求的参数:主要是测试过程。直接将参数在这里设置好。方便调试
//转成页码格式
Integer page=((offset+limit)/limit).toInteger();
Integer pageSize=limit;
//时间戳转日期格式
DateTime startDt = DateTime.of(startTime);
DateTime endDt = DateTime.of(endTime);
Integer startmonth=startDt.month;
Integer startday=startDt.day;
Integer starthour=startDt.hour;
Integer startminute=startDt.minute;
Integer endmonth=endDt.month;
Integer endday=endDt.day;
String startDateStr=startDt.year+"-"+(startmonth<10?"0"+startmonth:startmonth)+"-"+(startday<10?"0"+startday:startday);
String endDateStr=endDt.year+"-"+(endmonth<10?"0"+endmonth:endmonth)+"-"+(endday<10?"0"+endday:endday);
String startTimeStr=""+(starthour<10?"0"+starthour:starthour)+":"+(startminute<10?"0"+startminute:startminute)+":00";
//请求URL:通过参数urlName控制器返回对应的对象对应的请求路径
Map urlParams= ["urlName":""];
//基础数据请求URL:func_Yd9n2__c为控制器函数,控制器函数主要是封装通用的逻辑或者配置信息
def (error,result,errorMessage) = Fx.function.executeFunc("func_Yd9n2__c",urlParams)
String requestUrl =result["url"];
//请求header:主要是请求的认证信息可以放到控制器里面去
Map header=["Authorization":"Basic " +Fx.crypto.base64.encode(Fx.utils.toUTF8Bytes("username:password"))
,"Content-Type":"text/xml; charset=utf-8"];
//请求报文
String requestXml = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:urn=\"urn:sap-com:document:sap:soap:functions:mc-style\">\n" +
" <soapenv:Header/>\n" +
" <soapenv:Body>\n" +
" <urn:Zcrmfu0003>\n" +
" <!--Optional:-->\n" +
" <Enddate>"+endDateStr+"</Enddate>\n" +
" <!--Optional:-->\n" +
" <Endtime>23:59:59</Endtime>\n" +
" <!--Optional:-->\n" +
" <Pagenum>"+page+"</Pagenum>\n" +
" <!--Optional:-->\n" +
" <Pagesize>"+pageSize+"</Pagesize>\n" +
" <!--Optional:-->\n" +
" <Rtdetail>\n" +
" </Rtdetail>\n" +
" <!--Optional:-->\n" +
" <Rtmaster>\n" +
" </Rtmaster>\n" +
" <!--Optional:-->\n" +
" <Startdate>"+startDateStr+"</Startdate>\n" +
" <!--Optional:-->\n" +
" <Starttime>"+startTimeStr+"</Starttime>\n" +
" <!--Optional:-->\n" +
" <Vbeln></Vbeln>\n" +
" </urn:Zcrmfu0003>\n" +
" </soapenv:Body>\n" +
"</soapenv:Envelope>";
log.info("请求路径:"+requestUrl);
log.info("请求报文:"+requestXml);
def (Boolean error2,HttpResult result2,String errorMessage2)= Fx.http.post(requestUrl,header,requestXml,30000,false,0)
result2["bytes"]=null;
if(result2==null){
Fx.message.throwErrorMessage("接口超时,请编辑需要同步字段重新触发同步");
}
if( error2 ){
log.info("返回报文:"+result2);
Fx.message.throwErrorMessage(errorMessage2);
}else if(result2["statusCode"]!=200){
log.info("返回报文:"+result2);
Fx.message.throwErrorMessage("请检查参数是否错误");
}
//返回数据解析
def xmlSlurper =new XmlSlurper();
String content=result2["content"];
def responseMap=xmlSlurper.parseText(content)
def masterArrays=responseMap.Body["Zcrmfu0003Response"]["Rtmaster"] as NodeChildren;
def detailArrays=responseMap.Body["Zcrmfu0003Response"]["Rtdetail"] as NodeChildren;
List<Map> dataList=[];
//可以指定返回的字段(如果后期有字段需求变动erp对象需要修改。这个地方也是需要修改)。也可以把报文全部转成Map返回(后期变动只需要配置erp对象的字段就行)
List returnMasterFields=["Vbeln","Lfart","Vstel","Kunnr","Vkorg",........]
//
masterArrays.children().each{
String masterId=it["Vbeln"];
if((masterId!= null && ""!=masterId)){
Map dataMap=[:]
returnMasterFields.each{
field->
// it取到的节点是对象,一定需要转换成基础类型处理,不然序列化会是乱码
String fieldV=(it[field] as NodeChildren).text() as String;
if(fieldV!=null&&""!=fieldV ){
dataMap.put(field,fieldV)
}
}
dataMap["masterId"]= masterId;
dataMap["Vstel"]="工厂#"+dataMap["Vstel"];//特殊字段的处理。比如需要拼接字段
dataList.add(["masterFieldVal":dataMap])
}
}
//如果有明细记得处理明细数据["masterFieldVal":dataMap,"detailFieldVals":[["明细APIName1":[]]],["明细APIName2":[]]]]
Map resultMap=[:];
resultMap.put("dataList", dataList);
return resultMap;