为使得指示性更强,有以下名词说明
Client
Server
webservice
接受参数,比较人性化的系统会使用 http + json
方式接受参数 @ErpDataField(name = "FSaler", dataType = ErpBaseInfoTypeEnum.Employee,keyName = "FSTAFFNUMBER")
private Integer fsaleAid;
@ErpDataField(name = "F_PAEZ_Text4")
private String fsoNumber;
fastjson
的@JSONField
注解一样,对字段进行重命名,根据目标系统的字段名进行关联其实也就是将注解的内容 放进数据库里进行维护,通过如前端填写注解表格进行 对接交互,可实时增删改,更为灵活
我们知道 A系统中供应商id 为 333
的供应商 = B系统中 供应商唯一主键 为 S303
的供应商,我们需要在值转换阶段,将其转换成 B系统能够接受的值
dataType
属性)public interface ErpBaseInfoHandler {
String handleField(ErpJsonField jsonField,String extInfo);
}
public enum ErpBaseInfoTypeEnumAdmin{
None(0,"不是基本信息", NotBaseInfoHandler.class),
Channel(1,"渠道客户", ChannelBaseInfoHandler.class),
XyCompany(2,"公司主体Id", XyCompanyBaseInfoHandler.class),
Employee(3,"员工faid", EmployeeBaseInfoHandler.class),
Currency(4,"币种", CurrencyBaseInfoHandler.class),
//....
}
- 输入的参数含有
需要转换的A系统的员工id值 : A23
,要转换成 B系统的值 为B100567
- 以下 值映射关系 是指 A系统的值 => B系统的值,如A系统员工id A23 => B系统 的 B100567
也就是说
A23 => B100567
在某时刻是成立的,但是之后又不成立了
此种情况只能通过 B系统开发转换接口,然后A系统每次转换值时 调用接口 实时查询对应的值
public class EmployeeBaseInfoHandler implements ErpBaseInfoHandler{
@Override
public String handleField(ErpJsonField jsonField,String extInfo){
//在数据库中查询
String dbFnumber = this.searchInDb(jsonField);
if (!StringUtils.isEmpty(dbFnumber)) {
return dbFnumber;
}
String fname = this.getFname(jsonField);
//在第三方系统,如 erp 中查询
String fnumberByName = this.searchInErpByFname(jsonField, fname);
if (!StringUtils.isEmpty(fnumberByName)){
return fnumberByName;
}
//使用现有的员工信息,保存至第三方系统
String fnumber = saveToGetFnumber(jsonField, fname);
return fnumber;
}
}
前文我们提到过 任何一个网络请求 都可能出现 失败情况,因此我们必须要考虑加入重试功能
推送服务接收到 数据后,立即将能唯一确定数据的 信息保存在数据库,然后将 原始数据 临时缓存在 Redis等缓存容器
保存在数据库的信息,必须能保证足够可以用从 缓存容器 中重新取出 原始数据
最后再进行 值转换、发送网络请求等操作
所有操作成功后,将缓存删除;否则,延长 缓存时间
记得缓存时间 要大于 预期下一次定时任务处理时间,否则还没等到 定时任务来处理自己,缓存的数据就没了
为什么会出现上图的结果?关键在于 A系统在请求失败后无法判断请求是否成功了
想一下,如果你是B系统的设计者,你要怎样才能让 A系统 知道是否成功了呢?
幂等机制需要 B系统来支持,什么样的系统才能支持幂等机制?我以 金蝶K3Cloud ERP 中销售订单表单(下称 SO单)举例,以下是简介:
FbillNo
) 是唯一的,支持创建的时候自定义订单号(关键1),如不自定义则自动生成FerpId
吧FerpId
(关键2)如果提供以上的条件,就可以实现幂等机制:因为你可以根据上次请求预期结果,来查询上次预期结果是否成功。
问:这种字段非常多,能传值的字段都是可以指定值的字段,是否需要挑选值做了唯一索引的呢?
答:有更好,没有也可以。为什么呢?因为此值是 A系统自己控制的,A系统在传值时完全可以构造一个全局唯一的 值,比如加入时间戳、全局id生成器之类的。
再问:如果我选的 K 字段没有唯一索引,A系统传的值为 :k123-20200613
,此时保存成功了但A系统没有收到结果,超时失败了;但是B系统的前端用户想搞事情,也创建了一张 K 字段为 k123-20200613
,保存成功了。后来A系统重试时,查询时发现了两张单,不知道哪张单是它推送过去的了,这怎么办?
再答:这就是为啥说 有唯一索引更好 的原因。个人觉得程序无法解决,只能人工来处理,揪出这个”捣蛋鬼”了。
这个是幂等机制的基础!因为没有 这个字段或者查询接口不支持 ,则无法在重试前查询 上一次推送的结果是否成功了
FerpId
存入数据库,并标记为已成功推送日志是 事后定位bug 重要内容,最好不要只停留在日志文件中,数据库中应该也保留最近一次的错误原因等重要信息
如果B系统会在某个时间回调 A系统的某些接口,一定要打印接收到的原始数据,因为这是 追责的重要依据。