Skip to content

项目学习-大营销平台系统设计实现-用户行为返利入账 #21

Description

@cyjxiaozhi

一、本节诉求
按照用户行为返利的需求设计,创建相应的库表,开发 rebate 返利领域,提供返利订单创建接口。并在写入订单后发送 MQ 消息。后续则处理奖励入账。

用户的行为动作返利,是一种日常的活动类型。比如;你在某个平台创建了新账号,就会给你发一堆的开户优惠券,这些都是日常的返利活动。

日常的返利会根据用户所完成的行为动作来触达,这包括;打卡、签到、连签、支付、开户、交易、信贷、拉新等各类的动作。我们本节中所涉及的,主要是日常的日历签到行为,并也从功能实现上扩展出各类型的任务,以便于后续扩展。

image

一个用户行为可能会给多种奖励,所以在接收到用户信息后,会根据配置组装聚合对象。【聚合的目的就是为了做一个统一的事务】
一个聚合对象中包含了返利的订单实体对象,写入task的实体对象。它们是一个事务入库。
另外是发送MQ消息,在完成入口动作后,会直接发送MQ消息,并且如果发送失败,会有任务兜底。【这样是面试中经常问到的点,如果MQ消息发送失败了,你是怎么处理的。】

二、代码实现

本节的流程比较简单,就是把用户的行为动作,根据配置,创建出聚合对象,之后写账入库即可。
注意;仓储的使用、MQ消息事件的定义、聚合对象的创建。

@OverRide
public void saveUserRebateRecord(String userId, List behaviorRebateAggregates) {
try {
dbRouter.doRouter(userId);
transactionTemplate.execute(status -> {
try {
for (BehaviorRebateAggregate behaviorRebateAggregate : behaviorRebateAggregates) {
BehaviorRebateOrderEntity behaviorRebateOrderEntity = behaviorRebateAggregate.getBehaviorRebateOrderEntity();
// 用户行为返利订单对象
UserBehaviorRebateOrder userBehaviorRebateOrder = new UserBehaviorRebateOrder();
userBehaviorRebateOrder.setUserId(behaviorRebateOrderEntity.getUserId());
userBehaviorRebateOrder.setOrderId(behaviorRebateOrderEntity.getOrderId());
userBehaviorRebateOrder.setBehaviorType(behaviorRebateOrderEntity.getBehaviorType());
userBehaviorRebateOrder.setRebateDesc(behaviorRebateOrderEntity.getRebateDesc());
userBehaviorRebateOrder.setRebateType(behaviorRebateOrderEntity.getRebateType());
userBehaviorRebateOrder.setRebateConfig(behaviorRebateOrderEntity.getRebateConfig());
userBehaviorRebateOrder.setBizId(behaviorRebateOrderEntity.getBizId());
userBehaviorRebateOrderDao.insert(userBehaviorRebateOrder);
// 任务对象
TaskEntity taskEntity = behaviorRebateAggregate.getTaskEntity();
Task task = new Task();
task.setUserId(taskEntity.getUserId());
task.setTopic(taskEntity.getTopic());
task.setMessageId(taskEntity.getMessageId());
task.setMessage(JSON.toJSONString(taskEntity.getMessage()));
task.setState(taskEntity.getState().getCode());
taskDao.insert(task);
}
return 1;
} catch (DuplicateKeyException e) {
status.setRollbackOnly();
log.error("写入返利记录,唯一索引冲突 userId: {}", userId, e);
throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
}
});
} finally {
dbRouter.clear();
}

// 同步发送MQ消息
for (BehaviorRebateAggregate behaviorRebateAggregate : behaviorRebateAggregates) {
    TaskEntity taskEntity = behaviorRebateAggregate.getTaskEntity();
    Task task = new Task();
    task.setUserId(taskEntity.getUserId());
    task.setMessageId(taskEntity.getMessageId());
    try {
        // 发送消息【在事务外执行,如果失败还有任务补偿】
        eventPublisher.publish(taskEntity.getTopic(), taskEntity.getMessage());
        // 更新数据库记录,task 任务表
        taskDao.updateTaskSendMessageCompleted(task);
    } catch (Exception e) {
        log.error("写入返利记录,发送MQ消息失败 userId: {} topic: {}", userId, task.getTopic());
        taskDao.updateTaskSendMessageFail(task);
    }
}

}

设置路由 dbRouter.doRouter(userId); 在这个操作内,所有的流程都走一个路由结果,并且在同一个数据库链接。
transactionTemplate.execute 是手动开启数据库事务的操作。
status.setRollbackOnly(); 是手动回滚事务的操作,如果有唯一索引冲突则回滚事务。
最后同步发送MQ,避免强依赖 task,让 task 只做补偿,提高 MQ 发送效率。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions