业务:
数据库用户与微信公众号实际关注用户的数据核对.
需求:
前端点击按钮[更新用户关注状态],后端启动线程异步更新数据,并且可以查询线程执行结果.
数据更新过程不允许重复执行数据更新.
设计:
1,前端设计
(1)点击按钮,ajax post请求启动异步线程更新数据,并调用执行结果接口;
(2)执行结果接口,每隔2秒查询执行结果,直到执行结束.
结合bootstrap模态框设计页面:
<input type="button" value="更新用户关注状态" id="updateSubscribe"/>
<!-- 模态框(Modal) -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4><span class="label bg-danger">更新微信用户关注状态</span></h4>
</div>
<div class="modal-body">
<section class="panel text-center">
<a class="btn btn-circle btn-facebook btn-large"><i class="icon-facebook"></i></a>
<div class="h4">正在核对微信用户关注公众号状态...请稍等!关闭窗口不影响业务.</div>
<div class="line m-l m-r"></div>
<h4 class="text-info"><strong id="msg">正在刷新...</strong></h4>
<small>* 蓝色文字代表执行结果</small>
</section>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal -->
</div>
知识兔View CodeJS实现如下:
$("#updateSubscribe").click(function(){
$('#myModal').modal('show');
$.ajax({
type: "POST",
cache: false,
url: "/updateSubscribe",
dataType: 'json',
success: function (data) {
if (data.errcode!='0') {
$("#msg").html(data['errmsg']);
}
updateResult();
}
});
});
var updateResult = function(){
$.ajax({
type: "POST",
cache: false,
url: "/updateSubscribeResult",
dataType: 'json',
success: function (data) {
if(data.success){
$("#msg").html(data.message);
}else{
setTimeout(function () {
updateResult();
}, 2000);
}
}
});
}
知识兔2,后端设计
使用springboot搭建框架
(1)Controller
需要判断线程是否存在,存在则不允许更新数据;不存在则新建线程进行数据更新.保证当前只能有一个线程处理数据更新.
@Controller
public class UpdateSubscribeController {
@Autowired
private UpdateSubscribeService updateSubscribeService;
@PostMapping(value = "/updateSubscribe")
@ResponseBody
public ResultBean get(){
ResultBean resultBean = new ResultBean();
Thread thread = getThreadByName("UpdateSubscribe");
if(null == thread){
UpdateSubscribeThread updateSubscribeThread = new UpdateSubscribeThread(updateSubscribeService);
updateSubscribeThread.setName("UpdateSubscribe");
updateSubscribeThread.start();
}else {
resultBean.setError(ErrorCode.SYSTEM_ERROR);
resultBean.setErrmsg("上一次更新尚未结束,请勿重复更新.");
}
return resultBean;
}
@PostMapping(value = "/updateSubscribeResult")
@ResponseBody
public JsonResult updateSubscribeResult(){
JsonResult result = new JsonResult();
result.setSuccess(false);
Thread thread = getThreadByName("UpdateSubscribe");
if(null == thread){ // 更新完成
result.setSuccess(true);
try {
result.setMessage(UpdateSubscribeResultSingleton.getInstance().getErrorMsg());
}catch (Exception e){
e.printStackTrace();
}
}
return result; // 还没有执行完毕
}
/**
* 通过线程名字得到线程对象
* @param threadName
* @return
*/
public static Thread getThreadByName(String threadName) {
for (Thread t : Thread.getAllStackTraces().keySet()) {
if (t.getName().equals(threadName)) {
return t;
}
}
return null;
}
}
知识兔View Code(2)线程类UpdateSubscribeThread
public class UpdateSubscribeThread extends Thread{
private UpdateSubscribeService updateSubscribeService;
public UpdateSubscribeThread(UpdateSubscribeService updateSubscribeService) {
this.updateSubscribeService = updateSubscribeService;
}
@Override
public void run(){
updateSubscribeService.checkSubscribe();
}
}
知识兔(3)业务实现类UpdateSubscribeService
@Service
public class UpdateSubscribeService {
private static final Logger log = LoggerFactory.getLogger(UpdateSubscribeService.class);
@Autowired
private WxMpService wxMpService;
@Autowired
private UserDao userDao;
@Autowired
private WxUserDao wxUserDao;
@Autowired
private EntityManagerFactory emf;
public void checkSubscribe(){
log.info("start :: UpdateSubscribeService ==> checkSubscribe method: 执行异步任务 {}",Thread.currentThread().getName());
UpdateSubscribeResultSingleton resultSingleton = UpdateSubscribeResultSingleton.getInstance();
resultSingleton.setSuccess(null);
try {
// Thread.currentThread().sleep(7000);
wxUserDao.deleteAll();
WxMpUserList wxMpUserList = wxMpService.userList(null);
Criteria<User> userCriteria = new Criteria<>() ;
userCriteria.add(Restrictions.eq("subscribe" ,true)) ;
Long userCount = userDao.count(userCriteria);
if(wxMpUserList.getTotal() > userCount ){
int check = 0;
while(null != wxMpUserList){
check += wxMpUserList.getCount();
wxMpUserList = getNextWxMpUserList(wxMpUserList, check);
}
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Query query = em.createNativeQuery("UPDATE user SET subscribe=1 WHERE weixin IN(SELECT id FROM wx_user)");
int num = query.executeUpdate();
log.info("更新{}个用户状态为已关注",num);
query = em.createNativeQuery("UPDATE user SET subscribe=0 WHERE weixin not IN(SELECT id FROM wx_user)");
int result2 = query.executeUpdate();
log.info("更新{}个用户状态为未关注",result2);
em.getTransaction().commit();
query = em.createNativeQuery("SELECT wu.* FROM wx_user wu left join user u ON wu.id=u.weixin WHERE u.weixin IS NULL",WxUser.class);
List<WxUser> list = query.getResultList();
Date nowTime = new Date();
for(WxUser wxUser : list){
String openId = wxUser.getId();
WxMpUser wxMpUser = wxMpService.userInfo(openId,"zh_CN");
log.info("手动补充关注用户::{} -> {}",openId,wxMpUser.getNickname());
User user = new User();
user.setWeixin(openId);
user.setName(wxMpUser.getNickname());
user.setUnionId(wxMpUser.getUnionId());
user.setUrl(wxMpUser.getHeadImgUrl());
user.setCreateAt(nowTime);
user.setLastLogin(nowTime);
user.setSubscribe(true);
userDao.saveAndFlush(user);
}
em.close();
}
log.info("end :: UpdateSubscribeService ==> checkSubscribe method: 执行异步任务");
resultSingleton.setSuccess(true);
resultSingleton.setErrorMsg("更新成功");
resultSingleton.setLastUpdateTime(new Date());
} catch (Exception e) {
log.info("UpdateSubscribeService ==> checkSubscribe method: 执行异步任务失败,原因::{}",e.toString());
e.printStackTrace();
resultSingleton.setSuccess(false);
resultSingleton.setErrorMsg("更新失败,原因::"+e.toString());
resultSingleton.setLastUpdateTime(new Date());
}
}
private WxMpUserList getNextWxMpUserList(WxMpUserList wxMpUserList , int check) throws WxErrorException {
List<String> openIdList = wxMpUserList.getOpenIds();
List<WxUser> wxUserList = new ArrayList<>();
for(String openId : openIdList){
WxUser wxUser = new WxUser();
wxUser.setId(openId);
wxUserList.add(wxUser);
}
wxUserDao.save(wxUserList);
if(check == wxMpUserList.getTotal()){
return null;
}
return wxMpService.userList(null);
}
}
知识兔View Code(4)设计单例类保存线程执行结果
public class UpdateSubscribeResultSingleton {
private static class UpdateSubscribeResultHandler{
private static UpdateSubscribeResultSingleton intstance = new UpdateSubscribeResultSingleton();
}
private UpdateSubscribeResultSingleton(){
}
public static UpdateSubscribeResultSingleton getInstance(){
return UpdateSubscribeResultHandler.intstance;
}
private Boolean isSuccess;
private String errorMsg ;
private Date lastUpdateTime;
public Boolean getSuccess() {
return isSuccess;
}
public void setSuccess(Boolean success) {
isSuccess = success;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public Date getLastUpdateTime() {
return lastUpdateTime;
}
public void setLastUpdateTime(Date lastUpdateTime) {
this.lastUpdateTime = lastUpdateTime;
}
}
知识兔View Code