场景
需求中需要在联系人变更后动态地去触发联系人账号的注销和注册功能。
这里做法有两种:
1.逐一找到联系人变更的地方,在联系人update后添加该联系人账号的注销/注册逻辑,显然这种方式比较繁琐,且由于我们联系人变更有时候是需要走流程审批的(即联系人变更的入口比较多),所以此方法不好
2.利用aspect的代理拦截update方法,并对方法进行增强逻辑处理,织入账号注销/注册逻辑,达到效果
统一入口
第一步是找到联系人变更的统一入口,如果有必要可根据情况将update逻辑放入统一方法中。
@Override
public void saveOrUpdateByBatch(List<SupplierContact> contactList, SupplierDaoQueryDTO daoQueryDTO) {
if(!CollectionUtils.isEmpty(contactList)){
for(SupplierContact contact:contactList){
SupplierContactInfoEntity supplierContactInfoEntity = CopyBeanUtil.copyBean(contact, SupplierContactInfoEntity.class);
if(null == supplierContactInfoEntity.getId()){
baseMapper.insert(supplierContactInfoEntity);
}else{
baseMapper.updateById(supplierContactInfoEntity);
}
}
}
if(null != daoQueryDTO){
baseMapper.deleteByNotExistIds(daoQueryDTO);
}
}
如上面的代码将修改和删除方法(新增、修改、删除都需要监听到,以便注销和注册)放到同一方法,然后对此方法进行拦截即可。
aspect拦截方法
public class SupplierContactSaveAspect {
private SupplierContactRepostitory supplierContactRepostitory;
private ApplicationContext applicationContext;
@Pointcut(value = "execution( * com.huazhu.erp.supplierbc.infrastructure.persistence.repositoryimpl.SupplierContactInfoRepImpl.saveOrUpdateByBatch(java.util.List<com.huazhu.erp.supplierbc.domain.supplier.SupplierContact>,com.huazhu.erp.supplierbc.application.dto.SupplierDaoQueryDTO))&& args(contactList,daoQueryDTO)", argNames = "contactList,daoQueryDTO")
public void pointcut(List<SupplierContact> contactList, SupplierDaoQueryDTO daoQueryDTO) {
// should be empty by aspectj practice
}
@Around(value = "pointcut(contactList,daoQueryDTO)", argNames = "pjp,contactList,daoQueryDTO")
public Object aroundUpdateContact(ProceedingJoinPoint pjp, List<SupplierContact> contactList,SupplierDaoQueryDTO daoQueryDTO) throws Throwable {
String supplierNo;
Set<String> newPhoneSet = new HashSet<>();
if(CollectionUtils.isEmpty(contactList)){
supplierNo = daoQueryDTO.getSupplierNo();
}else {
supplierNo = contactList.get(0).getSupplierNo();
newPhoneSet = contactList.stream().map(SupplierContact::getPhone).collect(Collectors.toSet());
}
List<SupplierContact> oldSupplierContacts = supplierContactRepostitory.queryContactInfoBySupplierNo(supplierNo);
Set<String> oldPhoneSet = oldSupplierContacts.stream().map(SupplierContact::getPhone).collect(Collectors.toSet());
//修改的联系人也可能是需要逻辑删除(注销)的
newPhoneSet.addAll(oldPhoneSet);
//调用目标方法
Object proceed = pjp.proceed();
// 注册事务同步处理,如果有事务确保目标方法所在事务提交之后再执行后置操作
if(TransactionSynchronizationManager.isSynchronizationActive()){
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
//执行后置操作
dealAdAccount(supplierNo, newPhoneSet);
}
});
}else {
dealAdAccount(supplierNo, newPhoneSet);
}
}
/**
* 供应商变更事件处理ad账号
* 1.注销无效号码
* 2.开通有效账号
*/
public void dealAdAccount(String supplierNo, Collection<String> newPhoneSet) {
if (StringUtils.isBlank(supplierNo)) {
return;
}
List<SupplierContact> supplierContactInfos = supplierContactRepostitory.queryContactInfoBySupplierNo(supplierNo);
SupplierContactUpdated supplierContactUpdated = new SupplierContactUpdated();
supplierContactUpdated.setSupplierNo(supplierNo);
supplierContactUpdated.setContactList(supplierContactInfos);
supplierContactUpdated.setNewPhoneList(newPhoneSet);
applicationContext.publishEvent(supplierContactUpdated);
}
}
需要注意上述关于事务的处理,因为切点方法可能处于某一事务当中,我们的逻辑是需要在事务提交后才能做后续的处理。
所以这里利用了TransactionSynchronizationAdapter.afterCommit()方法,确保执行的顺序性,然后异步执行dealAdAccount()方法。
关于事务提交后触发异步方法还有另外一种处理方法,即在异步方法dealAdAccount()中做事务限制。具体代码如下:
@Component
public class SupplierContactUpdatedListener {
private final SupplierMemberUserService memberUserService;
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
@Async
public void onApplicationEvent(SupplierContactUpdated supplierContactUpdated) {
memberUserService.handleTermination(supplierContactUpdated.getNewPhoneList());
memberUserService.batchRegister(supplierContactUpdated.getContactList());
}
@Autowired
public SupplierContactUpdatedListener(SupplierMemberUserService memberUserService) {
this.memberUserService = memberUserService;
}
}
利用@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)注解,保证所监听方法的事务提交后再进行其他操作,这种方式的好处是不需要在切面拦截中加入TransactionSynchronizationAdapter.afterCommit()方法,最大程度的降低对切入点的影响,将事务的处理放到异步方法dealAdAccount()中。
码字不易,如有帮助,还请点赞支持!