有個業務場景,業務數據審核通過后需要給用戶發短信,發短信過程比較耗時,可能需要幾秒甚至十幾秒,因此使用異步發短信
使用了注解@Async來實現:
1.SpringApplication啟用注解@EnableAsync
@SpringBootApplication
@ImportResource(locations = { "classpath:/spring/spring-*.xml" })
@EnableTransactionManagement(proxyTargetClass=true)
@EnableScheduling
@EnableAutoConfiguration(exclude = { FreeMarkerAutoConfiguration.class })
@EnableSwagger2
@ServletComponentScan(basePackages="com.xx")
@EnableMongoRepositories(basePackages = "com.xx.xx.xx.xx")
@EnableAsync
public class IemsApplication {
public static void main(String[] args) {
SpringApplication.run(IemsApplication.class, args);
}
...
2.在業務層(@Service)具體的審核方法上添加注釋@Async
@Async
public void cancelAudit(DefectForm defectForm) {
Map<String,Object> params = new HashMap<>();
params.put("defectId", defectForm.getDefectId()); //缺陷記錄ID
params.put("defectStatus", 3); //更新缺陷記錄狀態審核拒絕
params.put("reason", defectForm.getReason()); //拒絕理由
defectRecordDao.updateByPrimaryKeySelective(params);
//上面是業務處理,下面是發短信
//審核拒絕發送短信,短信發送給缺陷上報人,缺陷內容,審核拒絕理由
Account account = accountDao.findAccountById(xxx);
if(account != null && StringUtils.isNotBlank(account.getMobile())){
String mobile = account.getMobile();
String defectContent = defectForm.getDefectContent();
String reason = defectForm.getReason();
Map<String,String> templateData = new HashMap<>();
templateData.put("defectContent", defectContent);
templateData.put("reason", reason);
smsService.sendSms(null, mobile, SmsConstant.DEFECT_REFUSRD_CODE, templateData,false);
logger.debug("缺陷上報記錄審核拒絕,發送短信給缺陷記錄上報人******");
}
}
3.前端邏輯:
/**
* 審核拒絕,確定
* @returns
*/
function makeRefuse(){
var reason = $("#refuse_reason").val();
if (reason==null || reason.trim()==""){
AppUtils.showTooltip("請填寫拒絕理由!",false);
return;
}
var curDefect = recordsJson[xxx];
$.ajax({
url: path + '/xxx/xxx/qqq/cancelAudit',
type: 'post',
dataType: 'json',
data:curDefect,
success: function(data){
if(data=="ok"){
AppUtils.showTooltip("審核拒絕成功!",true);
$("#topForm").attr("action",path + '/xxx/xxx/xxx');
$("#topForm").submit();
}
}
});
}
4.Controller層
@RequestMapping("/xxx/xxx/cancelAudit")
@ResponseBody
public String cancelAudit(DefectForm defectForm){
defectRecordService.cancelAudit(defectForm);
return "ok";
}
經測試,可以異步更新、發送短信
但是,發現一個嚴重的問題:前台頁面點擊取消審核后頁面狀態偶爾能刷新過來,偶爾還是之前的狀態,重新查詢一次后,頁面顯示正常
分析代碼:Controller層代碼寫的有問題,Controller層調用Service層(defectRecordService.cancelAudit(defectForm);),Service層cancelAudit(DefectForm defectForm)方法整個是@Async,
主線程會直接返回,而新啟的線程處理Service層的邏輯。這樣ajax返回前台,前台再去刷新數據的時候,可能新啟線程Service的更新邏輯還沒處理完,這樣就導致了頁面刷新狀態錯誤的問題
其實:我們期望的是,業務邏輯(更新操作)執行完成后再返回;整個業務邏輯(更新操作完成,返回)與發短信異步
修改后的代碼:
1.Controller層
@RequestMapping("/xxx/xxx/cancelAudit")
@ResponseBody
public String cancelAudit(DefectForm defectForm){
defectRecordService.cancelAudit(defectForm); //更新操作,成功后往下走,sendCancelAuditMsg會新啟一個線程處理,主線程繼續往下走,走到return "ok";返回
//審核拒絕:業務操作完成后發短信
defectRecordService.sendCancelAuditMsg(defectForm);
return "ok";
}
2.Service層
//這里我們就不需要添加異步注解了
public void cancelAudit(DefectForm defectForm) {
Map<String,Object> params = new HashMap<>();
params.put("defectId", defectForm.getDefectId()); //缺陷記錄ID
params.put("defectStatus", 3); //更新缺陷記錄狀態審核拒絕
params.put("reason", defectForm.getReason()); //拒絕理由
defectRecordDao.updateByPrimaryKeySelective(params);
}
//把發短信的邏輯抽出來,單獨一個方法,使用異步注解
@Async
public void sendCancelAuditMsg(DefectForm defectForm){
//審核拒絕發送短信,短信發送給缺陷上報人,缺陷內容,審核拒絕理由
Account account = accountDao.findAccountById(defectForm.getCreatorUserid());
if(account != null && StringUtils.isNotBlank(account.getMobile())){
String mobile = account.getMobile();
String defectContent = defectForm.getDefectContent();
String reason = defectForm.getReason();
Map<String,String> templateData = new HashMap<>();
templateData.put("defectContent", defectContent);
templateData.put("reason", reason);
smsService.sendSms(null, mobile, SmsConstant.DEFECT_REFUSRD_CODE, templateData,false);
logger.debug("缺陷上報記錄審核拒絕,發送短信給缺陷記錄上報人******");
}
}
至此問題就解決了,寫博客標注一下
