虚拟电商-话费充值业务(四)供应商轮转逻辑
一、供应商轮转逻辑编写
1.1.供应商轮转
测试对接重试是手动的将对接的返回业务状态码设置称为:StatusCode.ORDER_REQ_FAILED,真实情况下,我们需要根据对接供应商返回的错误信息码来决定充吧系统添加什么类型的任务,所以需要判断对接返回的业务状态码,
步骤1:默认对接聚合,聚合返回的状态码是:StatusCode.BALANCE_NOT_ENOUGH 余额不足,我们需要轮转下一个供应商:极速
@Override
public void recharge(RechargeRequest rechargeRequest) {
//限制最大重试次数 如果超过了最大次数,停止供应商对接,订单失败---实际业务中是对接订单服务
if(rechargeRequest.getRepeat() > supplierConfig.getMaxrepeat()){
updateTrade(rechargeRequest.getOrderNo(), OrderStatusEnum.FAIL.getCode());
return;
}
Result<RechargeResponse> result = doDispatchSupplier(rechargeRequest);
if(result !=null){
//需要根据对接返回的业务状态码来确定后续任务的类型及操作逻辑
if(result.getCode() == StatusCode.BALANCE_NOT_ENOUGH){
//模拟余额不足 轮转--到极速
rechargeRequest.setSupply(Constants.jisuapi);
rechargeRequest.setRepeat(0);
rechargeRequest.setErrorCode(StatusCode.BALANCE_NOT_ENOUGH);
}else if(result.getCode() == StatusCode.ORDER_REQ_FAILED) {
//重试逻辑的编写---添加重试任务
rechargeRequest.setErrorCode(StatusCode.ORDER_REQ_FAILED);
}
supplierTask.addRetryTask(rechargeRequest);
}
}
步骤2:添加完轮转任务后,需要去消费轮转任务,在SupplierTaskImpl类中再次添加一个方法用于消费轮转任务,
需要进行方法的改造:在SupplierTaskImpl类中已有一个消费对接重试的任务,消费轮转的任务和重试任务二者之间就是任务的类型取值不同,消费逻辑一样,可抽取
在SupplierTask接口中新增轮转方法
/**
* 供应商轮转
*/
public void roundRecharge();
@Override
@Scheduled(fixedRate = 1000)
public void retryRecharge() {
retry(TaskTypeEnum.ORDER_REQ_FAILED);
}
@Override
@Scheduled(fixedRate = 1000)
public void roundRecharge() {
retry(TaskTypeEnum.BALANCE_NOT_ENOUGH);
}
private void retry(TaskTypeEnum taskTypeEnum){
ResponseMessage responseMessage = taskServiceClient.poll(taskTypeEnum.getTaskType(), taskTypeEnum.getPriority());
if(responseMessage.isFlag()){
if(responseMessage.getData() !=null){
String taskStr = JSON.toJSONString(responseMessage.getData());
Task task = JSON.parseObject(taskStr,Task.class);
RechargeRequest rechargeRequest = ProtostuffUtil.deserialize(task.getParameters(), RechargeRequest.class);
rechargeRequest.setRepeat(rechargeRequest.getRepeat()+1);
System.out.println("消费了"+taskTypeEnum.getDesc()+"任务,"+rechargeRequest);
supplierService.recharge(rechargeRequest);
}
}
}
步骤3:轮转任务消费后就会去对接极速的接口,我们需要在对接供应商接口实现类:SupplierServiceImpl中完成和极速的对接
对接极速和聚合不一样,聚合要求传递的是JSON格式的数据,而极速要求传递的就是普通表单的key/value数据,并且我们模拟一下请求极速返回错误的情形
private Result<RechargeResponse> doPostJisu(RechargeRequest rechargeRequest) {
log.info("doPostJisu,{}",rechargeRequest);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//设置表单参数
MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
map.add("mobile",rechargeRequest.getMobile());
map.add("amount",rechargeRequest.getPamt()+"");
map.add("outorderNo", rechargeRequest.getOrderNo());
map.add("repeat", ""+rechargeRequest.getRepeat());
//模拟请求失败
map.add("req_status", ""+StatusCode.ERROR);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(map, headers);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(rechargeRequest.getRechargeUrl(), requestEntity , String.class);
//转换成统一对象
Result<RechargeResponse> result= JSON.parseObject(responseEntity.getBody(), new TypeReference<Result<RechargeResponse>>(){});
return result;
}
步骤4:测试:
依次启动:chongba_schedule_service,chongba_schedule_job,chongba_recharge_web,chongba_recharge_mock,chongba_recharge_supplier
访问:http://localhost:191/,进行话费充值
1.2.供应商排除
默认供应商聚合我们因为余额不足无法调用,我们是手动编写轮转到极速,在实际业务中,我们系统可能会接很多个供应商,当我们在默认供应商处余额不足时要从剩下的供应商中选出一个供应商进行对接的调用。此外还需将余额不足的供应商做一个排除,下次调用时跳过这些供应商
1:轮转时:缓存集成,将那些因为我们系统在对方平台余额不足的供应商编号缓存到redis中,使用Set集合存储,轮转时获取下一个供应商则直接从供应商配置类中读取所有的供应商地址列表,找出一个作为新的供应商,但得保证不是被排除的
2:与供应商对接前先判断RechargeRequest对象中默认传递过来的供应商是否可用,如果不可用则自动更换供应商。
@Autowired
private CacheService cacheService;
@Override
public void recharge(RechargeRequest rechargeRequest) {
//限制最大重试次数 如果超过了最大次数,停止供应商对接,订单失败---实际业务中是对接订单服务
if(rechargeRequest.getRepeat() > supplierConfig.getMaxrepeat()){
updateTrade(rechargeRequest.getOrderNo(), OrderStatusEnum.FAIL.getCode());
return;
}
String checkSupply = checkSupply(rechargeRequest.getSupply());
if(checkSupply!=null){
rechargeRequest.setSupply(checkSupply);
}else {
updateTrade(rechargeRequest.getOrderNo(),OrderStatusEnum.FAIL.getCode());
return;
}
Result<RechargeResponse> result = doDispatchSupplier(rechargeRequest);
if(result !=null){
if(result.getCode() == StatusCode.BALANCE_NOT_ENOUGH){
//模拟余额不足 轮转--到极速
/* rechargeRequest.setSupply(Constants.jisuapi);
rechargeRequest.setRepeat(0);
rechargeRequest.setErrorCode(StatusCode.BALANCE_NOT_ENOUGH);*/
//将我们余额不足的供应商放入reids 排除集合中
cacheService.sAdd(Constants.exclude_supplier,rechargeRequest.getSupply());
String nextSupply = nextSupply();
System.out.println("轮转到新的供应商为:"+nextSupply);
if(nextSupply !=null){
rechargeRequest.setSupply(nextSupply);
rechargeRequest.setRepeat(0);
rechargeRequest.setErrorCode(StatusCode.BALANCE_NOT_ENOUGH);
}else {
//没有供应商了---实际业务中是对接订单服务
updateTrade(rechargeRequest.getOrderNo(),OrderStatusEnum.FAIL.getCode());
return;
}
}else if(result.getCode() == StatusCode.ORDER_REQ_FAILED) {
//重试逻辑的编写---添加重试任务
rechargeRequest.setErrorCode(StatusCode.ORDER_REQ_FAILED);
}
supplierTask.addRetryTask(rechargeRequest);
}
}
private String checkSupply(String supply) {
Set<String> excludes = cacheService.setMembers(Constants.exclude_supplier);
if(excludes.contains(supply)){
return nextSupply();
}else {
return supply;
}
}
private String nextSupply() {
Set<String> excludes = cacheService.setMembers(Constants.exclude_supplier);
Map<String, String> allApis = supplierConfig.getApis();
for (String supply : allApis.keySet()) {
if(!excludes.contains(supply)){
return supply;
}
}
return null;
}
再次启动相关工程进行测似
1.3.供应商恢复
如果我们平台在某些供应商处余额不足,我们做了供应商排除,每次对接时都会过滤掉该供应商,一般供应商平台会通知我们进行充值,一旦充值成功,该供应商的接口应该恢复调用,因此我们需要提供一个接口用于恢复供应商,即将该供应商编号从缓存redis中的排除名单中删掉即可。
步骤1:在chongba_recharge_supplier模块下创建一个包:com.chongba.supplier.controller,在该包下创建RechargeNotifyController,提供一个供应商恢复的web层接口
@Slf4j
@RestController
public class RechargeNotifyController {
@Autowired
private CacheService cacheService;
@RequestMapping("/recovery")
public String recovery(String supply){
Set<String> excluedes = cacheService.setMembers(Constants.exclude_supplier);
if(excluedes.contains(supply)){
cacheService.sRemove(Constants.exclude_supplier,supply);
return "恢复成功!";
}else {
return "供应商编号不存在!";
}
}
}