1,模拟支付宝转账到余额宝的整个操作流程图如下
2,直接上代码
AccountMapper.xml
update account set amount = amount-#{amount},gmtModified=now() where id= #{id}
MessageMapper.xml
insert into message(accountId,price,status,gmtCreate,gmtModified) values(#{accountId},#{price},#{status},now(),now())
select @@identity
update message set status = #{status},gmtModified=now() where id = #{id}
Mapper代码
AccountDao.java
public interface AccountDao {
/**
* 查询余额
* @param accountId
* @return
*/
Account getAccountById(Integer accountId);
/**
* 转账-->出钱
* @param accountId 转账人id
* @param amount 转账金额
* @param messageId 消息id
*/
void updateAccountById(Account account);
}
MessageDao.java
public interface MessageDao {
/**
* 新增message
* @param message 实体参数
*/
void insertMessage(MessageMQ message);
/**
* 查询
* @param id
* @return
*/
MessageMQ getMessageById(Integer id);
/**
* 扫描未确认的消息
* @return
*/
List listMessageByStatus();
/**
* 修改状态
* @param id
* @param status
*/
void updateStatusById(MessageMQ message);
}
Service代码
AccountService.java
public interface AccountService {
Account getAccountById(Integer accountId);
/**
* 转账
* @param accountId 转账人id
* @param amount 转账金额
* @param messageId 消息id
* @param messageStatus 消息状态
*/
void updateAccountById(Account account);
/**
*
* @param accountId
* @param amount
* @return
*/
boolean transfer(Integer accountId, Integer amount);
void testUpdateAccountById(Account account);
}
messageService.java
public interface MessageService {
/**
* 新增message
* @param message 实体参数
*/
void insertMessage(MessageMQ message);
/**
* 查询
* @param id
* @return
*/
MessageMQ getMessageById(Integer id);
/**
* 扫描未确认的消息
* @return
*/
List listMessageByStatus();
/**
* 修改状态
* @param id
* @param status
*/
void updateStatusById(String param);
}
QueueProducerService.java
public interface QueueProducerService {
/**
* 发送Object queue消息
* @param message 消息内容
* @return
*/
public void sendObjectMessage(MessageMQ message);
/**
* 发送text queue消息
* @param message 消息内容
* @return
*/
public void sendTextMessage(String message);
}
ServiceImpl代码
AccountServiceImpl.java
@Service("accountService")
public class AccountServiceImpl implements AccountService {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private AccountDao accountDao;
@Autowired
private MessageService messageService;
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private QueueProducerService queueProducerService;
/**
* 转账流程:
* 1,修改金额;
* 2,修改金额成功,新增一条未确认消息,修改金额失败,不新增消息;
* 3,新增消息成功,向MQ发送一条消息。
* 如果向MQ发送消息失败(消息丢失),用一个定时任务,定时扫描未确认的消息用于消息(发送MQ消息).
*/
public boolean transfer(Integer id, Integer amount) {
// 未提交状态
Integer stats = 1;
MessageMQ message = new MessageMQ(id,amount,stats);
Boolean flag = transactionTemplate.execute(new TransactionCallback() {
@Override
public Boolean doInTransaction(TransactionStatus status) {
try {
logger.info("###########支付宝方查询余额操作###########id==" + id);
Account account = accountDao.getAccountById(id);
Integer oldAmount = account.getAmount();
logger.info("###########支付宝方查询当前账号余额###########OldAmount==" + oldAmount);
if (oldAmount < amount) {
logger.info("###########支付宝方余额不足###########" + account);
return false;
}
// 设置余额
account.setAmount(amount);
logger.info("###########支付宝方扣款操作###########" + account);
accountDao.updateAccountById(account);
logger.info("###########支付宝方添加消息操作###########" + message);
// 新增一条消息
messageService.insertMessage(message);
return true;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
});
// 失败
if (!flag) {
return false;
}
// 获取新增message的id
Integer messageId = message.getId();
// 向MQ发送一条消息
logger.info("###########支付宝方向MQ发送消息###########" + message);
queueProducerService.sendObjectMessage(message);
return true;
}
/**
* 转账流程:
* 1,修改金额;
* 2,修改金额成功,新增一条未确认消息,修改金额失败,不新增消息;
* 3,新增消息成功,向MQ发送一条消息。
* 如果向MQ发送消息失败(消息丢失),用一个定时任务,定时扫描未确认的消息用于消息(发送MQ消息).
*/
@Transactional(rollbackFor = Throwable.class)
public boolean transfer1(Integer id, Integer amount) {
logger.info("###########支付宝方查询余额操作###########id==" + id);
Account account = accountDao.getAccountById(id);
Integer oldAmount = account.getAmount();
logger.info("###########支付宝方查询当前账号余额###########OldAmount==" + oldAmount);
if (oldAmount < amount) {
logger.info("###########支付宝方余额不足###########" + account);
return false;
}
// 设置余额
account.setAmount(amount);
logger.info("###########支付宝方扣款操作###########" + account);
accountDao.updateAccountById(account);
// 未确认状态
Integer stats = 1;
MessageMQ message = new MessageMQ(id, amount, stats);
logger.info("###########支付宝方添加消息操作###########" + message);
// 新增一条消息
messageService.insertMessage(message);
// 获取新增message的id
Integer messageId = message.getId();
// 向MQ发送一条消息
if (null != messageId) {
logger.info("###########支付宝方向MQ发送消息###########" + message);
queueProducerService.sendObjectMessage(message);
}
return true;
}
@Override
public Account getAccountById(Integer accountId) {
return accountDao.getAccountById(accountId);
}
@Override
public void updateAccountById(Account account) {
accountDao.updateAccountById(account);
}
@Override
public void testUpdateAccountById(Account account) {
Boolean flag = (Boolean) transactionTemplate.execute(new TransactionCallback
MessageServiceImpl.java
@Service("messageService")
public class MessageServiceImpl implements MessageService {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private MessageDao messageDao;
@Override
public void insertMessage(MessageMQ message) {
messageDao.insertMessage(message);
}
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void updateStatusById(String param) {
try {
// JSONObject jsonObject = JSONObject.parseObject(param);
// String messageId = jsonObject.getString("messageId");
// String respCode = jsonObject.getString("respCode");
logger.info("############支付宝收到回调参数##########"+param);
Integer id = Integer.parseInt(param);
MessageMQ message = this.getMessageById(id);
logger.info("############支付宝根据根据回调参数查询消息##########"+message);
Integer search_id = message.getId();
if (id.equals(search_id)) {
Integer search_status = message.getStatus();
if(search_status==1) {
Integer status = 0;
message.setStatus(status);
logger.info("############支付宝根据根据回调修改消息状态##########"+message);
messageDao.updateStatusById(message);
logger.info("############支付宝消息已更新确认状态##########"+message);
}
}
} catch (Exception e) {
logger.error("############消息更新失败############。。。"+param);
throw e;
}
}
@Override
public MessageMQ getMessageById(Integer id) {
return messageDao.getMessageById(id);
}
@Override
public List listMessageByStatus() {
return messageDao.listMessageByStatus();
}
}
QueueProducerSerivceImpl.java
@Service("queueProducerService")
public class QueueProducerSerivceImpl implements QueueProducerService {
@Autowired
private JmsTemplate jmsTemplate;
@Override
public void sendObjectMessage(MessageMQ message) {
//设置发送地址
this.jmsTemplate.setDefaultDestinationName("distributedTransaction-message");
jmsTemplate.send(new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createObjectMessage(message);
}
});
}
@Override
public void sendTextMessage(String message) {
//设置发送地址
this.jmsTemplate.setDefaultDestinationName("text-message");
jmsTemplate.send(new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(message);
}
});
}
}
MessageScheduledJob.java
/**
* 定时扫描消息发送失败的消息
* 重发失败消息
* @author reyco
*
*/
public class MessageScheduledJob extends QuartzJobBean {
protected Logger logger = LoggerFactory.getLogger(getClass());
/**
* messageService
*/
private MessageService messageService;
/**
* MQ queueService
*/
private QueueProducerService queueProducerService;
public void setMessageService(MessageService messageService) {
this.messageService = messageService;
}
public void setQueueProducerService(QueueProducerService queueProducerService) {
this.queueProducerService = queueProducerService;
}
@Override
protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
logger.info("--------------------------------------------------");
logger.info("###################定时任务被执行啦################");
List messages = messageService.listMessageByStatus();
logger.info("###################发送失败的消息################"+messages);
if(messages.size() < 1) {
logger.info("###################没有发送失败的消息################");
return;
}
//
MessageTask task = new MessageTask();
task.setMessages(messages);
task.setQueueProducerService(queueProducerService);
task.run();
}
}
messageTask.java
/**
* 消费信息任务
*
* @author reyco
*
*/
public class MessageTask {
protected Logger logger = LoggerFactory.getLogger(getClass());
/**
* 信息集合
*/
private List messages;
private QueueProducerService queueProducerService;
public void setMessages(List messages) {
this.messages = messages;
}
public void setQueueProducerService(QueueProducerService queueProducerService) {
this.queueProducerService = queueProducerService;
}
public void run() {
Integer size = messages.size();
Integer countSize = this.getCountSize(size);
Integer threalPoolSize = size / countSize + 1;
List newMessages = new ArrayList<>();
for (int i = 0; i < threalPoolSize; i++) {
if((i+1) == threalPoolSize) {
int startIndex = i * countSize;
int endIndex = size;
newMessages = messages.subList(startIndex, endIndex);
}else {
int startIndex = i * countSize;
int endIndex = (i+1) * countSize;
newMessages = messages.subList(startIndex, endIndex);
}
logger.info("################newMessages size################"+newMessages.size());
new Thread(new Runnable() {
@Override
public void run() {
for(MessageMQ message : messages) {
logger.info("################定时任务向MQ发送消息################"+message);
queueProducerService.sendObjectMessage(message);
}
}
}).start();
}
}
/**
* 获取每个线程池执行任务数
*
* @param messages
* @return
*/
private Integer getCountSize(int size) {
Integer countSize = 10;
if (null == messages) {
return 0;
}
if (size < 5) {
countSize = 5;
} else if (size >= 5 && size < 33) {
countSize = 5;
} else if (size >= 33 && size < 133) {
countSize = 10;
} else if (size >= 133 && size < 533) {
countSize = 20;
} else if (size >= 533 && size < 1333) {
countSize = 30;
} else if (size >= 1333 && size < 5000) {
countSize = 60;
} else {
countSize = 200;
}
return countSize;
}
}
AccountController.java
@RequestMapping("account")
@Controller
public class AccountController {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private AccountService accountService;
@Autowired
private MessageService messageService;
@ResponseBody
@RequestMapping("transfer")
public String transfer(Integer accountId,Integer amount) {
if(null == accountId || null == amount) {
return "转账失败。。。";
}
logger.info("Parameter:\taccountId==="+accountId+"\tamount==="+amount);
boolean flag = accountService.transfer(accountId, amount);
if(flag) {
logger.info("##############支付宝方转账成功,预计24小时之内到账,请注意查收!##############");
return "转账成功,预计24小时之内到账,请注意查收!";
}
return "转账失败。。。";
}
@ResponseBody
@RequestMapping("callback")
public String updateStatus(String param) {
if(null == param || "".equals(param)) {
return "fail";
}
logger.info("################Parameter:\t#################param==="+param.toString());
try {
messageService.updateStatusById(param);
return "success";
} catch (Exception e) {
return "fail";
}
}
@ResponseBody
@RequestMapping("test")
public String testUpdateStatus() {
Account account = new Account();
account.setId(1);
accountService.testUpdateAccountById(account);
return "ok";
}
}
accountMapper.xml
update account set amount = amount + #{amount},gmtModified=now() where id=#{id}
messageMapper.xml
insert into message(id,accountId,price,status,gmtCreate,gmtModified) values(#{id},#{accountId},#{price},#{status},now(),now())
accountDao.java
public interface AccountDao {
/**
* 查询余额
* @param accountId
* @return
*/
Account getAccountById(Integer id);
/**
* 转账-->出钱
* @param accountId 转账人id
* @param amount 转账金额
* @param messageId 消息id
*/
void updateAccountById(Account account);
}
messageDao.java
public interface MessageDao {
/**
* 查询
* @param id
* @return
*/
Integer countMessageById(Integer id);
/**
* 新增message
* @param message 实体参数
*/
void insertMessage(MessageMQ message);
}
AccountService.java
public interface AccountService {
/**
* 查询余额
* @param accountId
* @return
*/
Account getAccountById(Integer id);
/**
* 转账---收钱
* @param accountId 转账人id
* @param amount 转账金额
*/
void updateAccountById(Account account);
}
messageService.java
public interface MessageService {
/**
* 查询
* @param id
* @return
*/
Integer countMessageById(Integer id);
/**
* 新增message
* @param message 实体参数
*/
void insertMessage(MessageMQ message);
}
AccountServiceImpl.java
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void updateAccountById(Account account) {
Integer accountId = account.getId();
Account ac = getAccountById(accountId);
if(null == ac) {
return;
}
// 修改金额
accountDao.updateAccountById(account);
}
@Override
public Account getAccountById(Integer accountId) {
return accountDao.getAccountById(accountId);
}
}
MessageServiceImpl.java
@Service("messageService")
public class MessageServiceImpl implements MessageService {
@Autowired
private MessageDao messageDao;
@Override
public Integer countMessageById(Integer id) {
return messageDao.countMessageById(id);
}
@Override
public void insertMessage(MessageMQ message) {
messageDao.insertMessage(message);
}
}
QueueMessageListener.java
@Component("messageListener")
public class QueueMessageListener implements MessageListener {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private AccountService accountService;
@Autowired
private MessageService messageService;
/**
* 收款流程:
* 1,监听消息,拿到MQ消息;
* 2,到消息表查询该消息是否消费过;
* 3,该消息如未消费(没有查到该消息),执行本地事务;
* 4,执行本地事务1,首先修改账号金额;
* 5,执行本地事务2,向消息表新增消息;
* 6,回调
*/
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void onMessage(Message message) {
try {
if (message instanceof ObjectMessage) {
ObjectMessage om = (ObjectMessage) message;
Object data = om.getObject();
if (data instanceof MessageMQ) {
MessageMQ mcMQ = (MessageMQ) data;
Integer mcMQId = mcMQ.getId();
// 先去查询是否消费过
Integer count = messageService.countMessageById(mcMQId);
// 没有消费过
if (0 == count) {
Integer accountId = mcMQ.getAccountId();
Integer amount = mcMQ.getPrice();
Account account = new Account();
account.setId(accountId);
account.setAmount(amount);
// 收钱
logger.info("###########余额宝方收款操作###########"+amount);
accountService.updateAccountById(account);
logger.info("###########余额宝方收款成功###########"+amount);
mcMQ.setStatus(0);
logger.info("###########余额宝方新增消息操作###########"+mcMQ);
// 新增消息
messageService.insertMessage(mcMQ);
logger.info("###########余额宝方新增消息成功###########"+mcMQ);
RestTemplate restTemplate = this.getRestTemplate();
// 请求参数
//JSONObject jsonObject = new JSONObject();
//jsonObject.put("messageId", mc.getId());
//jsonObject.put("respCode", 200);
//String url = "http://localhost:80/alipay/account/callback.do?param=" + jsonObject.toString();
//ResponseEntity