组织项、用户同步并处理事务问题

项目上需要同步用户给第三方,所以增删改用户信息时,会发布用户同步事件,在涉及到组织项增删改查时,会发布同步事件,然后在监听器处理组织项同步逻辑,发到http、mq等组件上,让第三方去获取。这里就会关系到事务问题,因为用户保存逻辑处理、同步事件发布都在同一个方法中,方法执行完之后才会提交事务,若提交事务时出现sql异常,但是这时已经发布了同步事件了,造成数据不一致。所以得需要用户逻辑处理完,提交了保存事件之后再进行同步逻辑处理。只需要在事务监听器上配好TransactionPhase.AFTER_COMMIT即可。这如果事务提交有异常,就不会进行用户同步了。

贴代码:

          新建事件类:

/**
 * 组织项同步事件
 * @author lfq
 * @version 1.0
 * @date 2022/10/21
 */
@Getter
@Setter
@ToString
public class OrganSynchEvent extends ApplicationEvent {
   private static final long serialVersionUID = 617313570247857471L;
   
   private List<OrganInfo> organInfoList;
   private List<UserInfo> userInfoList;
   private SyncTypeEnum operateType;
   private String roleId;
   private List<String> childIds;

   public OrganSynchEvent(Object source) {
      super(source);
   }

   public OrganSynchEvent(Object source, List<UserInfo> userInfoList, List<OrganInfo> organInfoList, SyncTypeEnum operateType) {
      super(source);
      this.userInfoList = userInfoList;
      this.operateType = operateType;
      this.organInfoList = organInfoList;
   }

   public OrganSynchEvent(Object source, String roleId, List<String> childIds, SyncTypeEnum operateType) {
      super(source);
      this.roleId = roleId;
      this.operateType = operateType;
      this.childIds = childIds;
   }
}

监听器类:

/**
 * 组织项同步监听器
 * @author lfq
 * @date 2022/10/21
 */
@Component
@Slf4j
public class OrganSynchListener {

    @Autowired
    private OrganSynchProperties properties;

    @Autowired(required = false)
    private OrganSynchService organSynchService;

    @Autowired(required = false)
    @Qualifier("taskExecutor")
    private ThreadPoolTaskExecutor taskExecutor;

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, classes = OrganSynchEvent.class)
    public void onApplicationEvent(OrganSynchEvent event) {
        if (taskExecutor != null && properties.isEnabled() && organSynchService != null) {
            taskExecutor.execute(() -> {
                List<UserInfo> userInfoList = event.getUserInfoList();
                List<OrganInfo> organInfoList = event.getOrganInfoList();
                SyncTypeEnum operateType = event.getOperateType();
                String roleId = event.getRoleId();
                List<String> childIds = event.getChildIds();
                try {
                    if ((SyncTypeEnum.USER_ADD.equals(operateType) || SyncTypeEnum.USER_UPDATE.equals(operateType)) && !CollectionUtils.isEmpty(userInfoList)) {
                        for (UserInfo userInfo : userInfoList) {
                            SynchModel synchModel = new SynchModel(operateType.name(), UserSyncModel.toUserSyncModel(userInfo));
                            organSynchService.synch(synchModel);
                        }
                    } else if ((SyncTypeEnum.ORG_ADD.equals(operateType) || SyncTypeEnum.ORG_UPDATE.equals(operateType)) && !CollectionUtils.isEmpty(organInfoList)) {
                        for (OrganInfo organInfo : organInfoList) {
                            SynchModel synchModel = new SynchModel(operateType.name(), OrganSyncModel.toOrganSyncModel(organInfo));
                            organSynchService.synch(synchModel);
                        }
                    } else if ((SyncTypeEnum.USER_DELETE.equals(operateType) || SyncTypeEnum.ORG_DELETE.equals(operateType)) && !CollectionUtils.isEmpty(organInfoList)) {
                        Set<String> ids = organInfoList.stream().map(OrganInfoBase::getRid).collect(Collectors.toSet());
                        Map<String, Set<String>> idMap = new HashMap<>();
                        idMap.put("ids", ids);
                        SynchModel synchModel = new SynchModel(operateType.name(), idMap);
                        organSynchService.synch(synchModel);
                    } else if ((SyncTypeEnum.ROLE_ORGAN_BIND.equals(operateType) || SyncTypeEnum.ROLE_ORGAN_UNBIND.equals(operateType)) && StringUtils.isNotBlank(roleId) && !CollectionUtils.isEmpty(childIds)) {
                        Map<String, Object> map = new HashMap<>();
                        map.put("roleId", roleId);
                        map.put("ids", childIds);
                        SynchModel synchModel = new SynchModel(operateType.name(), map);
                        organSynchService.synch(synchModel);
                    }
                } catch (Exception e) {
                    //todo 同步失败处理
                    log.info(e.getMessage());
                }
            });
        }
    }
}

http同步实现类,有多种同步方式,这里只贴http的同步方式

/**
 * 以http接口调用方式同步组织项
 * @author lfq
 */
@Slf4j
public class OrganSynchSynchByHttp implements OrganSynchService{

    private OrganSynchProperties properties;

    private RestTemplate restTemplateBalanced;

    private RestTemplate restTemplate;

    public OrganSynchSynchByHttp() {}

    public OrganSynchSynchByHttp(OrganSynchProperties properties, RestTemplate restTemplateBalanced, RestTemplate restTemplate) {
        this.properties = properties;
        this.restTemplate = restTemplate;
        this.restTemplateBalanced = restTemplateBalanced;
    }

    @Override
    public void synch(SynchModel synchModel) {
        log.info(String.format("同步操作:type = [%s], content = [%s]" ,  synchModel.getType(), synchModel.getContent()));
        //设置请求头参数
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
        HttpEntity request = new HttpEntity<>(JsonUtil.toJsonString(synchModel), headers);
        String url = properties.getSynchPath();
        if (StringUtils.isBlank(url)){
            throw new ServiceException("请配置第三方同步接口:mainweb.organ.sync.synchPath");
        }
        ResponseEntity<Object> responseEntity = null;
        try {
            responseEntity = getRestTemplate(url).exchange(url, HttpMethod.POST, request, Object.class);
        }catch (Exception e){
            if (e instanceof HttpServerErrorException){
                HttpServerErrorException d = (HttpServerErrorException) e;
                throw new ServiceException(String.format("%s请求异常, body:{%s},message: {%s}", url,d.getResponseBodyAsString(Charset.forName("UTF-8")), e.getMessage()), e);
            }else if (e instanceof HttpClientErrorException){
                HttpClientErrorException d = (HttpClientErrorException) e;
                throw new ServiceException(String.format("%s请求异常, body:{%s},message: {%s}", url,d.getResponseBodyAsString(Charset.forName("UTF-8")), e.getMessage()), e);
            } else {
                throw new ServiceException(e);
            }
        }
        checkSuccess(url, responseEntity);
        log.info(String.format("%s请求响应, statusCode:{%s},body:{%s}", url, responseEntity.getStatusCodeValue(), responseEntity.getBody()));
    }

    private RestTemplate getRestTemplate(String url){
        Integer st = url.indexOf("//") + 2;
        Integer en = url.indexOf("/",st);
        String d = url.substring(st, en);
        if (d.contains(".")){
            return restTemplate;
        }else {
            return restTemplateBalanced;
        }
    }

    /**
     * 检查返回是否成功,Http状态小于400
     * @param url
     * @param response
     */
    private static void checkSuccess(String url, ResponseEntity<?> response) {
        if(response != null && response.getStatusCodeValue() >= 400) {
            if (response.getBody() instanceof Byte){
                try {
                    throw new ServiceException("远程调用出错[" + url + "]:"
                            +response.getStatusCode()+", content:" + new String((byte[]) response.getBody(), "utf-8"));
                }catch (Exception e){}
            }
            throw new ServiceException("远程调用出错[" + url + "]:"
                    +response.getStatusCode()+", content:" + response.getBody());
        }
    }
}

使用:

// 发布 用户同步 事件
applicationContext.publishEvent(new OrganSynchEvent("", Arrays.asList(userInfo), null, SyncTypeEnum.USER_ADD));

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值