一、话费充值回调业务补偿
业务需求:供应商对接下单成功后充吧系统将订单状态更改为:等待确认中,此时等待供应商系统进行回调,当供应商系统回调时说明供应商充值成功,供应商回调充吧系统将充吧的订单改为充值成功,如果在这个过程中出现了故障,比如供应商没有回调,或者回调过程中产生了异常,或者回调不通,回调失败,如果我们不对这种情况进行处理的话,那么我们的订单状态永远得不到修改.
解决方案:
1:供应商会提供一个查询订单充值状态的接口,当我们系统对接下单成功后添加一个查询状态的任务,该任务1分钟或者几分钟后去调用供应商查询状态的API,我们根据供应商返回的结果进行充吧订单状态的修改
2:如果供应商系统能够正常回调,则将查询状态的任务取消即可
1.1.状态检查接口开发
步骤1:我们在chongba_recharge_mock工程中模拟的供应商都提供了状态检查接口,同理我需要将供应商状态检查接口api地址配置到对接模块chongba_recharge_supplier中,修改application-dev.yml配置文件如下:
supplier:
jisu_url: "http://127.0.0.1:8090/jisuapi/mobilerecharge"
juhe_url: "http://127.0.0.1:8090/juheapi"
apis: {jisuapi: "${supplier.jisu_url}/recharge",
juheapi: "${supplier.juhe_url}/recharge"}
maxrepeat: 4
checkStateApis: {jisuapi: "${supplier.jisu_url}/orderState",
juheapi: "${supplier.juhe_url}/orderState"}
stateCheckTime: 1 #状态检查时间
并修改配置类:SupplierConfig
@Data
@Component
@ConfigurationProperties(prefix = "supplier")
public class SupplierConfig {
private Map<String,String> apis; //加载供应商api地址
private int maxrepeat;//最大重试次数
private Map<String,String> checkStateApis;
private int stateCheckTime; //订单充值状态检查时间
}
步骤2:在chongba_recharge_supplier模块的对接供应商接口:SupplierService中添加检查充值状态的接口方法
public interface SupplierService {
/**
* 对接供应商下单
* @param rechargeRequest
*/
public void recharge(RechargeRequest rechargeRequest);
/**
* 对接下单成功后检查充值状态
* @param checkStatusRequest
*/
public void checkStatus(CheckStatusRequest checkStatusRequest);
}
在chongba_common工程中的:com.chongba.recharge包下创建实体:
CheckStatusRequest
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CheckStatusRequest {
private String supplier;
private String orderNo;
private String tradeNo;
}
参数说明:
supplier:不同供应商查询订单充值状态API地址不一样,需要根据供应商编号去获取
orderNo:充吧系统生成的订单号,充吧支付成功后通知对接模块下单时已经将充吧订单号传递过来了
tradeNo:供应商系统生成的唯一的交易号,当我们调用供应商对接下单API后由供应商生成的唯一交易号,并返回给了充吧系统
orderNo和tradeNo参数是供应商状态检查接口需要传递的参数!企业开发中看第三方的供应商系统接口需要传递什么参数就传递什么参数。
步骤3:在SupplierServiceImpl类中实现检查状态的方法
@Override
public void checkStatus(CheckStatusRequest checkStatusRequest) {
//获取状态检查接口地址
String checkStatusApi = supplierConfig.getCheckStateApis().get(checkStatusRequest.getSupplier());
//设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//封装请求参数---实际业务中看供应商需要传递哪些参数,实际情况中可能要根据不同的供应商传递不同的
//参数,那就要在这个逻辑中添加不同的条件分支
MultiValueMap<String,String> map = new LinkedMultiValueMap<>();
map.add("outorderNo",checkStatusRequest.getOrderNo());
map.add("tradeNo",checkStatusRequest.getTradeNo());
HttpEntity<MultiValueMap<String,String>> httpEntity = new HttpEntity<>(map,headers);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(checkStatusApi, httpEntity, String.class);
Result<RechargeResponse> result = JSON.parseObject(responseEntity.getBody(), new TypeReference<Result<RechargeResponse>>() {});
if(result.getCode() == StatusCode.OK){
log.info("订单状态检查,订单成功{}",checkStatusRequest);
updateTrade(checkStatusRequest.getOrderNo(),result.getData().getStatus());
}else{
//订单失败
log.info("订单状态检查,订单失败{}",checkStatusRequest);
updateTrade(checkStatusRequest.getOrderNo(),OrderStatusEnum.FAIL.getCode());
}
}
1.2.添加和消费状态查询任务
有了对接供应商状态查询的接口,下面要做的就是在对接供应商下单成功之后添加一个查询状态的任务,该任务可在1分钟后执行,即1分钟后去查询供应商充值状态
步骤1:在chongba_recharge_supplier模块的任务接口:SupplierTask中新增两个方法,
添加查询状态任务方法:addCheckStatusTask(CheckStatusRequest checkStatusRequest);
消费查询状态任务方法:checkStatus();
/**
* 添加状态检查查询任务
* @param checkStatusRequest
*/
public void addCheckStatusTask(CheckStatusRequest checkStatusRequest);
/**
* 状态检查任务
*/
public void checkStatus();
参数说明:
添加任务是为了消费任务,消费状态检查任务就是要对接供应商状态检查接口,而该接口需要传递的参数就是CheckStatusRequest ,包含了属性supplier,orderNo,tradeNo,所以添加任务时需要将CheckStatusRequest 作为任务对象Task的parameter属性值。
步骤2:实现添加和消费任务方法
@Autowired
private SupplierConfig supplierConfig;
@Override
public void addCheckStatusTask(CheckStatusRequest checkStatusRequest) {
Task task = new Task();
TaskTypeEnum taskTypeEnum = TaskTypeEnum.STATECHECK;
task.setTaskType(taskTypeEnum.getTaskType());
task.setPriority(taskTypeEnum.getPriority());
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE,supplierConfig.getStateCheckTime());
task.setExecuteTime(calendar.getTimeInMillis());
task.setParameters(ProtostuffUtil.serialize(checkStatusRequest));
//添加状态检查任务
ResponseMessage result = taskServiceClient.push(task);
}
@Override
@Scheduled(fixedRate = 1000)
public void checkStatus() {
//消费状态检查任务-----发起状态检查
TaskTypeEnum statecheck = TaskTypeEnum.STATECHECK;
ResponseMessage poll = taskServiceClient.poll(statecheck.getTaskType(), statecheck.getPriority());
if(poll.isFlag()){
if(poll.getData()!=null){
String taskStr = JSON.toJSONString(poll.getData());
Task task = JSON.parseObject(taskStr,new TypeReference<Task>(){});
CheckStatusRequest statusRequest = ProtostuffUtil.deserialize(task.getParameters(), CheckStatusRequest.class);
log.info("消费任务时从拉取的任务数据{}",statusRequest);
//调用状态检查接口进行状态检查
supplierService.checkStatus(statusRequest);
}
}
}
步骤3:对接供应商下单成功后添加状态检查任务,完善SupplierServiceImpl类中的recharge方法
@Override
public void recharge(RechargeRequest rechargeRequest) {
if(result !=null){
//判断成功还是失败
if(result.getCode() == StatusCode.OK){
log.info("下单成功,等待充值处理回调!");
//特别注意此时订单状态还不能修改为充值成功-----供应商回调之后才能修改为成功
updateTrade(rechargeRequest.getOrderNo(),OrderStatusEnum.UNAFFIRM.getCode());//充值处理中等待确认
log.info("下单成功,添加状态检查任务,1分钟后进行状态检查");
supplierTask.addCheckStatusTask(new CheckStatusRequest(rechargeRequest.getSupply(),
result.getData().getOrderNo(),result.getData().getTradeNo()));
return;
}else {
//.....................后面的代码省略
}
}
}
步骤3:测试:
现在要测试供应商回调不成功由充吧系统主动发起状态查询的情形,在chongba_recharge_mock工程中的MockJisuRechargeController中注释掉回调的方法
启动所有系统,进行话费充值业务。
1.3.回调成功后取消任务
当供应商成功回调后我们需要取消状态查询任务,取消任务需要任务id,而在供应商的回调方法中,只有供应商返回的订单号和交易号,没有任务id,因此在充吧添加状态查询任务时需要将充吧订单号和任务id进行关联存储,
步骤1:在SupplierTaskImpl类中完善:addCheckStatusTask添加状态检查任务方法
@Autowired
private CacheService cacheService;
@Override
public void addCheckStatusTask(CheckStatusRequest checkStatusRequest) {
Task task = new Task();
TaskTypeEnum taskTypeEnum = TaskTypeEnum.STATECHECK;
task.setTaskType(taskTypeEnum.getTaskType());
task.setPriority(taskTypeEnum.getPriority());
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE,supplierConfig.getStateCheckTime());
task.setExecuteTime(calendar.getTimeInMillis());
task.setParameters(ProtostuffUtil.serialize(checkStatusRequest));
//添加状态检查任务
ResponseMessage result = taskServiceClient.push(task);
//供应商系统能够正常回调则需要取消状态检查任务,取消任务需要任务id,回调成功后修改充吧订单状态知道订单号
//因此要将订单号和任务id做一个映射存储
if(result.getCode() == StatusCode.OK){
cacheService.hPut(Constants.order_checked,checkStatusRequest.getOrderNo(),String.valueOf(result.getData()));
}
}
步骤2:在SupplierTask中添加一个取消状态检查任务的方法,方法参数是订单号
/**
* 取消状态检查任务
* @param orderNo
*/
public void cancelCheckTask(String orderNo);
步骤3:在SupplierTaskImpl类中实现取消状态检查任务的方法
@Override
public void cancelCheckTask(String orderNo) {
String taskId = (String) cacheService.hGet(Constants.order_checked, orderNo);
if(taskId!=null){
taskServiceClient.cancel(Long.valueOf(taskId));
cacheService.hDelete(Constants.order_checked,orderNo);
}
}
步骤4:在供应商回调成功的方法中调用取消状态检查的任务,在chongba_recharge_supplier模块的:com.chongba.supplier.controller包下的RechargeNotifyController中的notify(@RequestBody String result)方法,调用取消状态检查方法
@Autowired
private SupplierTask supplierTask;
@RequestMapping(value = "/order/notify")
public String notify(@RequestBody String result) {
JSONObject jsonObject = (JSONObject) JSON.parse(result);
String orderNo= (String) jsonObject.get("orderNo");
int status= Integer.parseInt(jsonObject.get("status").toString());
log.info("充值回调成功修改订单{}的状态为{}",orderNo,status);
updateTrade(orderNo, status);
log.info("回调成功后取消状态检查任务");
supplierTask.cancelCheckTask(orderNo);
return "sucess";
}
步骤5:测试,
注意:在chongba_recharge_mock模块中将极速的回调通知方法放开,然后再测试,
启动所有工程,进行话费充值。