一起探讨学习
每天给大家提供技术干货
springboot实现高并发红包系统(全网最全)
下面的业务处理请根据你们实际的场景进行处理
1.sql设计
CREATE TABLE ` red_packet_info` (
` id` int ( 0 ) NOT NULL AUTO_INCREMENT COMMENT '自增id' ,
` packet_id` varchar ( 100 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '红包id' ,
` type` char ( 1 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '红包类型(0=拼手气红包,1=普通红包,2=文字口令红包,3=语音口令红包)' ,
` watchword_content` varchar ( 255 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '口令内容' ,
` user_id` int ( 0 ) NOT NULL COMMENT '用户id,哪个用户发的红包' ,
` total_amount` decimal ( 8 , 2 ) NOT NULL COMMENT '红包总金额' ,
` total_packet` int ( 0 ) NOT NULL COMMENT '红包个数' ,
` amount_one` decimal ( 8 , 2 ) NULL DEFAULT NULL COMMENT '单个红包金额' ,
` blessing` varchar ( 100 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '红包祝福语' ,
` cover` varchar ( 255 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '红包封面' ,
` left_amount` decimal ( 8 , 2 ) NOT NULL COMMENT '剩余红包金额' ,
` left_packet` int ( 0 ) NOT NULL COMMENT '剩余红包个数' ,
` expire_time` datetime ( 0 ) NOT NULL COMMENT '红包过期时间' ,
` send_type` char ( 1 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '发送红包类型(0=私发,1=群发)' ,
` status` char ( 2 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '红包状态(1=已创建,-1=已失效,2=已抢完)' ,
` create_time` datetime ( 0 ) NOT NULL COMMENT '创建时间' ,
` update_time` datetime ( 0 ) NOT NULL COMMENT '更新时间' ,
` deleted` char ( 1 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0' COMMENT '是否删除' ,
` creator` varchar ( 64 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者' ,
` updater` varchar ( 64 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '更新者' ,
` tenant_id` bigint ( 0 ) NOT NULL DEFAULT 0 COMMENT '租户编号' ,
PRIMARY KEY ( ` id` ) USING BTREE ,
UNIQUE INDEX ` packet_id` ( ` packet_id` ) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 775 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '红包表' ROW_FORMAT = Dynamic;
CREATE TABLE ` red_packet_records` (
` id` int ( 0 ) NOT NULL AUTO_INCREMENT ,
` user_id` int ( 0 ) NOT NULL COMMENT '用户id' ,
` amount` decimal ( 8 , 2 ) NOT NULL COMMENT '抢到的金额' ,
` watchword_content` varchar ( 255 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '口令内容' ,
` red_packet_id` varchar ( 100 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '红包id' ,
` creator` varchar ( 64 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者' ,
` create_time` datetime ( 0 ) NOT NULL COMMENT '创建时间' ,
` updater` varchar ( 64 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者' ,
` update_time` datetime ( 0 ) NOT NULL COMMENT '更新时间' ,
` deleted` char ( 1 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '1' COMMENT '是否删除' ,
` tenant_id` bigint ( 0 ) NOT NULL DEFAULT 0 COMMENT '租户编号' ,
PRIMARY KEY ( ` id` ) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 223 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '红包记录表' ROW_FORMAT = Dynamic;
2.实体类
RedPacketInfoDO
package cn. inno. pala. module. packet. dal. dataobject ;
import cn. inno. pala. framework. tenant. core. db. TenantBaseDO ;
import com. baomidou. mybatisplus. annotation. IdType ;
import com. baomidou. mybatisplus. annotation. TableField ;
import com. baomidou. mybatisplus. annotation. TableId ;
import com. baomidou. mybatisplus. annotation. TableName ;
import lombok. Data ;
import lombok. EqualsAndHashCode ;
import lombok. experimental. Accessors ;
import java. math. BigDecimal ;
import java. util. Date ;
@Data
@EqualsAndHashCode ( callSuper = false )
@Accessors ( chain = true )
@TableName ( "red_packet_info" )
public class RedPacketInfoDO extends TenantBaseDO {
private static final long serialVersionUID = 1L ;
@TableId ( value = "id" , type = IdType . AUTO )
private Integer id;
private String packetId;
private String type;
private String watchwordContent;
private Integer userId;
private BigDecimal totalAmount;
private Integer totalPacket;
private BigDecimal amountOne;
private String blessing;
private String cover;
private BigDecimal leftAmount;
private Integer leftPacket;
private String sendType;
private Date expireTime;
private String status;
@TableField ( exist = false )
private String nickname;
@TableField ( exist = false )
private String avatar;
}
RedPacketRecordsDO
package cn. inno. pala. module. packet. dal. dataobject ;
import cn. inno. pala. framework. tenant. core. db. TenantBaseDO ;
import com. baomidou. mybatisplus. annotation. IdType ;
import com. baomidou. mybatisplus. annotation. TableField ;
import com. baomidou. mybatisplus. annotation. TableId ;
import com. baomidou. mybatisplus. annotation. TableName ;
import lombok. Data ;
import lombok. EqualsAndHashCode ;
import lombok. experimental. Accessors ;
import java. math. BigDecimal ;
@Data
@EqualsAndHashCode ( callSuper = false )
@Accessors ( chain = true )
@TableName ( "red_packet_records" )
public class RedPacketRecordsDO extends TenantBaseDO {
private static final long serialVersionUID = 1L ;
@TableId ( value = "id" , type = IdType . AUTO )
private Integer id;
private Integer userId;
private BigDecimal amount;
private String watchwordContent;
private String redPacketId;
@TableField ( exist = false )
private String nickname;
@TableField ( exist = false )
private String avatar;
}
3.Mapper
RedPacketInfoMapper
package cn. inno. pala. module. packet. dal. mysql ;
import cn. inno. pala. framework. mybatis. core. mapper. BaseMapperX ;
import cn. inno. pala. module. packet. dal. dataobject. RedPacketInfoDO ;
import org. apache. ibatis. annotations. Mapper ;
import org. apache. ibatis. annotations. Param ;
@Mapper
public interface RedPacketInfoMapper extends BaseMapperX < RedPacketInfoDO > {
RedPacketInfoDO getRedPacket ( @Param ( "packetId" ) String packetId) ;
}
RedPacketRecordsMapper
package cn. inno. pala. module. packet. dal. mysql ;
import cn. inno. pala. framework. mybatis. core. mapper. BaseMapperX ;
import cn. inno. pala. module. packet. dal. dataobject. RedPacketRecordsDO ;
import org. apache. ibatis. annotations. Mapper ;
import org. apache. ibatis. annotations. Param ;
import java. util. List ;
@Mapper
public interface RedPacketRecordsMapper extends BaseMapperX < RedPacketRecordsDO > {
RedPacketRecordsDO getRedPacketRecord ( @Param ( "packetId" ) String packetId, @Param ( "userId" ) Long userId) ;
List < RedPacketRecordsDO > getRedPacketRecordList ( @Param ( "packetId" ) String packetId) ;
}
4.xml
RedPacketInfoMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<! DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
< mapper namespace = " cn.inno.pala.module.packet.dal.mysql.RedPacketInfoMapper" >
< resultMap id = " redPacketResultMap" type = " RedPacketInfoDO" >
< id column = " id" property = " id" > </ id>
< result column = " packet_id" property = " packetId" > </ result>
< result column = " type" property = " type" > </ result>
< result column = " watchword_content" property = " watchwordContent" > </ result>
< result column = " user_id" property = " userId" > </ result>
< result column = " total_amount" property = " totalAmount" > </ result>
< result column = " total_packet" property = " totalPacket" > </ result>
< result column = " amount_one" property = " amountOne" > </ result>
< result column = " blessing" property = " blessing" > </ result>
< result column = " cover" property = " cover" > </ result>
< result column = " left_amount" property = " leftAmount" > </ result>
< result column = " left_packet" property = " leftPacket" > </ result>
< result column = " expire_time" property = " expireTime" > </ result>
< result column = " send_type" property = " sendType" > </ result>
< result column = " status" property = " status" > </ result>
< result column = " create_time" property = " createTime" > </ result>
< result column = " update_time" property = " updateTime" > </ result>
< result column = " deleted" property = " deleted" > </ result>
< result column = " creator" property = " creator" > </ result>
< result column = " updater" property = " updater" > </ result>
< result column = " tenant_id" property = " tenantId" > </ result>
</ resultMap>
< select id = " getRedPacket" resultMap = " redPacketResultMap" >
SELECT r.*, m.nickname, m.avatar
FROM `red_packet_info` r
INNER JOIN member_user m ON r.user_id = m.id
WHERE r.packet_id = #{packetId}
</ select>
</ mapper>
RedPacketRecordsMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<! DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
< mapper namespace = " cn.inno.pala.module.packet.dal.mysql.RedPacketRecordsMapper" >
< resultMap id = " redPacketRecordResultMap" type = " RedPacketRecordsDO" >
< id column = " id" property = " id" > </ id>
< result column = " user_id" property = " userId" > </ result>
< result column = " amount" property = " amount" > </ result>
< result column = " watchword_content" property = " watchwordContent" > </ result>
< result column = " red_packet_id" property = " redPacketId" > </ result>
< result column = " create_time" property = " createTime" > </ result>
< result column = " nickname" property = " nickname" > </ result>
< result column = " avatar" property = " avatar" > </ result>
</ resultMap>
< select id = " getRedPacketRecord" resultMap = " redPacketRecordResultMap" >
SELECT r.id, r.user_id,r.watchword_content, r.amount, r.red_packet_id, r.create_time, m.nickname, m.avatar
FROM `red_packet_records` r
INNER JOIN member_user m ON r.user_id = m.id
WHERE r.red_packet_id = #{packetId}
AND r.user_id = #{userId}
ORDER BY r.create_time ASC
</ select>
< select id = " getRedPacketRecordList" resultMap = " redPacketRecordResultMap" >
SELECT r.id, r.user_id,r.watchword_content, r.amount, r.red_packet_id, r.create_time, m.nickname, m.avatar
FROM `red_packet_records` r
INNER JOIN member_user m ON r.user_id = m.id
WHERE r.red_packet_id = #{packetId}
ORDER BY r.create_time ASC
</ select>
</ mapper>
5.Service
RedPacketInfoService
package cn. inno. pala. module. packet. service ;
import cn. inno. pala. module. packet. controller. app. dto. DismantleRedPacketDTO ;
import cn. inno. pala. module. packet. controller. app. dto. RedPacketsDTO ;
import cn. inno. pala. module. packet. controller. app. vo. RedPacketInfoVO ;
import cn. inno. pala. module. packet. controller. app. vo. RedPacketQualificationsVO ;
import cn. inno. pala. module. packet. dal. dataobject. RedPacketInfoDO ;
import com. baomidou. mybatisplus. extension. service. IService ;
public interface RedPacketInfoService extends IService < RedPacketInfoDO > {
String sendRedPacket ( RedPacketsDTO packetsDTO) ;
RedPacketQualificationsVO grabPacket ( String packetId) ;
RedPacketInfoVO getPacket ( DismantleRedPacketDTO dismantleRedPacketDTO) ;
RedPacketInfoVO getPacketRecord ( String packetId) ;
}
6.impl
RedPacketInfoServiceImpl
package cn. inno. pala. module. packet. service. impl ;
import cn. hutool. core. date. DateUtil ;
import cn. hutool. core. util. IdUtil ;
import cn. hutool. core. util. ObjectUtil ;
import cn. hutool. core. util. RandomUtil ;
import cn. hutool. core. util. StrUtil ;
import cn. inno. pala. framework. common. util. collection. ObjectConvertUtil ;
import cn. inno. pala. framework. common. util. md5. MD5Util ;
import cn. inno. pala. framework. common. util. validation. AssertUtils ;
import cn. inno. pala. framework. security. core. LoginUser ;
import cn. inno. pala. framework. security. core. util. SecurityFrameworkUtils ;
import cn. inno. pala. framework. tenant. core. context. TenantContextHolder ;
import cn. inno. pala. module. packet. constants. Constant ;
import cn. inno. pala. module. packet. controller. app. bo. RedPacketRecordsBO ;
import cn. inno. pala. module. packet. controller. app. dto. DismantleRedPacketDTO ;
import cn. inno. pala. module. packet. controller. app. dto. RedPacketsDTO ;
import cn. inno. pala. module. packet. controller. app. vo. RedPacketInfoVO ;
import cn. inno. pala. module. packet. controller. app. vo. RedPacketQualificationsVO ;
import cn. inno. pala. module. packet. controller. app. vo. RedPacketRecordsVO ;
import cn. inno. pala. module. packet. dal. dataobject. RedPacketInfoDO ;
import cn. inno. pala. module. packet. dal. dataobject. RedPacketRecordsDO ;
import cn. inno. pala. module. packet. dal. mysql. RedPacketInfoMapper ;
import cn. inno. pala. module. packet. dal. mysql. RedPacketRecordsMapper ;
import cn. inno. pala. module. packet. dal. redis. packet. RedPacketQualificationDAO ;
import cn. inno. pala. module. packet. dal. redis. packet. RedPacketRedisDAO ;
import cn. inno. pala. module. packet. dal. redis. records. RedPacketRecordsRedisDAO ;
import cn. inno. pala. module. packet. enums. RedPacketSendTypeEnum ;
import cn. inno. pala. module. packet. enums. RedPacketStatusEnum ;
import cn. inno. pala. module. packet. enums. RedPacketTypeEnum ;
import cn. inno. pala. module. packet. service. RedPacketInfoService ;
import cn. inno. pala. module. pay. enums. wallet. PalaWalletEnum ;
import cn. inno. pala. module. pay. wallet. ApiWalletService ;
import cn. inno. pala. module. pay. wallet. vo. ApiJudgeAmplePalaCoinReqVO ;
import cn. inno. pala. module. pay. wallet. vo. ApiReducePalaCoinReqVO ;
import cn. inno. pala. module. pay. wallet. vo. ApiReducePalaCoinRespVO ;
import cn. inno. pala. module. system. api. dict. DictDataApi ;
import com. baomidou. mybatisplus. extension. service. impl. ServiceImpl ;
import lombok. extern. slf4j. Slf4j ;
import org. redisson. api. RLock ;
import org. redisson. api. RedissonClient ;
import org. springframework. amqp. rabbit. core. RabbitTemplate ;
import org. springframework. beans. BeanUtils ;
import org. springframework. beans. factory. annotation. Value ;
import org. springframework. stereotype. Service ;
import org. springframework. transaction. annotation. Transactional ;
import javax. annotation. Resource ;
import java. math. BigDecimal ;
import java. math. RoundingMode ;
import java. util. * ;
import static cn. inno. pala. module. packet. enums. ErrorCodeConstants . * ;
@Service
@SuppressWarnings ( "rawtypes" )
@Slf4j
public class RedPacketInfoServiceImpl extends ServiceImpl < RedPacketInfoMapper , RedPacketInfoDO > implements RedPacketInfoService {
@Value ( "${red-packet.backtrack.delayed-exchange-name}" )
public String delayedExchangeName;
@Value ( "${red-packet.backtrack.delayed-routing-key}" )
public String delayedRoutingKey;
@Value ( "${red-packet.warehousing.entry-exchange-name}" )
public String entryExchangeName;
@Value ( "${red-packet.warehousing.entry-routing-key}" )
public String entryRoutingKey;
@Resource
private RedPacketRedisDAO redPacketRedisDAO;
@Resource
private DictDataApi dictDataApi;
@Resource
private RedPacketInfoMapper redPacketInfoMapper;
@Resource
private RedPacketRecordsMapper redPacketRecordsMapper;
@Resource
private RedPacketRecordsRedisDAO redPacketRecordsRedisDAO;
@Resource
private RedPacketQualificationDAO redPacketQualificationDAO;
@Resource
private RabbitTemplate rabbitTemplate;
@Resource
private ApiWalletService apiWalletService;
@Resource
private RedissonClient redissonClient;
@Override
@Transactional ( rollbackFor = Exception . class )
public String sendRedPacket ( RedPacketsDTO packetsDTO) {
log. info ( "request param 【{}】" , packetsDTO) ;
LoginUser loginUser = SecurityFrameworkUtils . getLoginUser ( ) ;
Long loginUserId = loginUser. getId ( ) ;
RedPacketInfoDO redPackets = new RedPacketInfoDO ( ) ;
RLock lock = redissonClient. getLock ( Constant . RED_PACKET_LOCK_KEY + loginUserId) ;
lock. lock ( ) ;
try {
if ( StrUtil . isBlank ( packetsDTO. getBlessing ( ) ) ) {
packetsDTO. setBlessing ( Constant . DEFAULT_BLESSING_MESSAGE ) ;
}
if ( StrUtil . isBlank ( packetsDTO. getCover ( ) ) ) {
packetsDTO. setCover ( Constant . DEFAULT_RED_PACKET_COVER ) ;
}
int totalPacket = Integer . valueOf ( packetsDTO. getTotalPacket ( ) ) ;
BigDecimal totalAmount = packetsDTO. getTotalAmount ( ) ;
BigDecimal amountOne = packetsDTO. getAmountOne ( ) ;
AssertUtils . isTrue ( totalPacket <= 0 , NUMBER_NOT_ZERO ) ;
if ( packetsDTO. getType ( ) . equals ( RedPacketTypeEnum . LUCKY . getType ( ) ) || packetsDTO. getType ( ) . equals ( RedPacketTypeEnum . TEXT_WATCHWORD . getType ( ) ) || packetsDTO. getType ( ) . equals ( RedPacketTypeEnum . VOICE_WATCHWORD . getType ( ) ) ) {
AssertUtils . isTrue ( null == totalAmount, TOTAL_AMOUNT_NOT_NULL ) ;
amountOne = totalAmount. divide ( BigDecimal . valueOf ( totalPacket) , 2 , RoundingMode . DOWN ) ;
}
if ( packetsDTO. getType ( ) . equals ( RedPacketTypeEnum . ORDINARY . getType ( ) ) ) {
AssertUtils . isTrue ( null == amountOne, ONE_AMOUNT_NOT_NULL ) ;
totalAmount = amountOne. multiply ( BigDecimal . valueOf ( totalPacket) ) ;
}
Integer minRedPacket = Integer . valueOf ( dictDataApi. dictDatalist ( "min_red_packet" ) . get ( 0 ) . getValue ( ) ) ;
Integer maxRedPacket = Integer . valueOf ( dictDataApi. dictDatalist ( "max_red_packet" ) . get ( 0 ) . getValue ( ) ) ;
Integer minTotalPacket = Integer . valueOf ( dictDataApi. dictDatalist ( "min_total_packet" ) . get ( 0 ) . getValue ( ) ) ;
Integer maxTotalPacket = Integer . valueOf ( dictDataApi. dictDatalist ( "max_total_packet" ) . get ( 0 ) . getValue ( ) ) ;
AssertUtils . isTrue ( amountOne. compareTo ( BigDecimal . valueOf ( minRedPacket) ) == - 1 , MIN_AMOUNT_ERROR , minRedPacket) ;
AssertUtils . isTrue ( amountOne. compareTo ( BigDecimal . valueOf ( maxRedPacket) ) == 1 , MAX_AMOUNT_ERROR , maxRedPacket) ;
AssertUtils . isTrue ( Integer . valueOf ( packetsDTO. getTotalPacket ( ) ) < minTotalPacket, TOTAL_PACKET ) ;
AssertUtils . isTrue ( Integer . valueOf ( packetsDTO. getTotalPacket ( ) ) > maxTotalPacket, TOTAL_PACKET ) ;
AssertUtils . isTrue ( BigDecimal . valueOf ( totalAmount. intValue ( ) ) . compareTo ( totalAmount) == - 1 , NOT_DECIMAL ) ;
Boolean result = apiWalletService. judgeAmplePalaCoin ( new ApiJudgeAmplePalaCoinReqVO ( loginUserId, totalAmount) ) ;
AssertUtils . isTrue ( ! result, INSUFFICIENT_BALANCE ) ;
BeanUtils . copyProperties ( packetsDTO, redPackets) ;
redPackets. setUserId ( loginUserId. intValue ( ) ) ;
redPackets. setCreateTime ( new Date ( ) ) ;
redPackets. setTotalPacket ( totalPacket) ;
redPackets. setTotalAmount ( totalAmount) ;
redPackets. setWatchwordContent ( packetsDTO. getWatchwordContent ( ) ) ;
redPackets. setLeftAmount ( totalAmount) ;
redPackets. setLeftPacket ( totalPacket) ;
redPackets. setStatus ( RedPacketStatusEnum . CREATED . getStatus ( ) ) ;
redPackets. setSendType ( packetsDTO. getSendType ( ) ) ;
redPackets. setNickname ( loginUser. getNickname ( ) ) ;
redPackets. setAvatar ( loginUser. getAvatar ( ) ) ;
redPackets. setPacketId ( String . valueOf ( IdUtil . getSnowflake ( ( long ) ( 0 + Math . random ( ) * ( 30 - 0 + 1 ) ) , ( long ) ( 0 + Math . random ( ) * ( 30 - 0 + 1 ) ) ) . nextId ( ) ) ) ;
Date date = new Date ( ) ;
redPackets. setExpireTime ( DateUtil . offsetDay ( date, 1 ) ) ;
ApiReducePalaCoinReqVO palaCoinReq = new ApiReducePalaCoinReqVO ( ) . setUserId ( loginUserId) . setBusiness ( PalaWalletEnum . REDUCE_HAND_OUT_RED_ENVELOPES . getStatus ( ) ) . setTargetId ( redPackets. getPacketId ( ) ) . setOperationPalaCoin ( totalAmount) . setContent ( RedPacketSendTypeEnum . PRIVATE . getStatus ( ) . equals ( packetsDTO. getSendType ( ) ) ? "给昵称" + packetsDTO. getNickname ( ) + "发红包" : "世界频道发出红包" ) ;
ApiReducePalaCoinRespVO apiReducePalaCoinResp = apiWalletService. reducePalaCoin ( palaCoinReq) ;
AssertUtils . isTrue ( ! apiReducePalaCoinResp. getState ( ) , DEDUCTION_ACCOUNT_ERROR ) ;
log. info ( "扣减账户额度成功状态【{}】" , apiReducePalaCoinResp. getState ( ) ) ;
redPacketInfoMapper. insert ( redPackets) ;
log. info ( "用户【{}】,插入红包成功记录【{}】" , loginUserId, redPackets) ;
rabbitTemplate. convertAndSend ( delayedExchangeName, delayedRoutingKey, redPackets. getPacketId ( ) , correlationData -> {
correlationData. getMessageProperties ( ) . setDelay ( 24 * 60 * 60 * 1000 ) ;
correlationData. getMessageProperties ( ) . setHeader ( "tenant-id" , TenantContextHolder . getTenantId ( ) ) ;
correlationData. getMessageProperties ( ) . setHeader ( "user-id" , loginUserId) ;
return correlationData;
} ) ;
log. info ( "往mq发送延迟消息" ) ;
redPacketRedisDAO. set ( redPackets. getPacketId ( ) , redPackets) ;
log. info ( "红包记录写入redis" ) ;
return redPackets. getPacketId ( ) ;
} finally {
lock. unlock ( ) ;
}
}
@Override
public RedPacketQualificationsVO grabPacket ( String packetId) {
Long loginUserId = SecurityFrameworkUtils . getLoginUserId ( ) ;
judge ( loginUserId, packetId, new RedPacketInfoDO ( ) , new RedPacketRecordsDO ( ) ) ;
String md5 = MD5Util . getMD5 ( loginUserId. toString ( ) ) ;
redPacketQualificationDAO. set ( Constant . RED_PACKET_QUALIFICATIONS_KEY + loginUserId + "_" + packetId, md5) ;
return RedPacketQualificationsVO . builder ( ) . build ( ) . setFlag ( true ) . setSign ( md5) ;
}
@Override
@Transactional ( rollbackFor = Exception . class )
public RedPacketInfoVO getPacket ( DismantleRedPacketDTO dismantleRedPacketDTO) {
LoginUser loginUser = SecurityFrameworkUtils . getLoginUser ( ) ;
Long loginUserId = SecurityFrameworkUtils . getLoginUserId ( ) ;
RedPacketInfoDO redPackets = new RedPacketInfoDO ( ) ;
RedPacketRecordsDO redPacketRecords = new RedPacketRecordsDO ( ) ;
RLock lock = redissonClient. getLock ( Constant . RED_PACKET_LOCK_KEY + dismantleRedPacketDTO. getPacketId ( ) ) ;
lock. lock ( ) ;
try {
String value = redPacketQualificationDAO. get ( Constant . RED_PACKET_QUALIFICATIONS_KEY + loginUserId + "_" + dismantleRedPacketDTO. getPacketId ( ) ) ;
AssertUtils . isTrue ( ObjectUtil . notEqual ( value, dismantleRedPacketDTO. getSign ( ) ) , SIGN_ERROR ) ;
judge ( loginUserId, dismantleRedPacketDTO. getPacketId ( ) , redPackets, redPacketRecords) ;
if ( redPackets. getSendType ( ) . equals ( RedPacketSendTypeEnum . PRIVATE . getStatus ( ) ) ) {
AssertUtils . isTrue ( loginUserId. intValue ( ) == redPackets. getUserId ( ) , NOT_AVAILABLE ) ;
}
BigDecimal amount = calculate ( redPackets) ;
redPackets. setLeftPacket ( redPackets. getLeftPacket ( ) - 1 ) ;
redPackets. setLeftAmount ( redPackets. getLeftAmount ( ) . subtract ( amount) ) ;
if ( redPackets. getLeftPacket ( ) == 0 ) {
redPackets. setStatus ( RedPacketStatusEnum . WITHOUT . getStatus ( ) ) ;
}
RedPacketRecordsDO packetRecords = new RedPacketRecordsDO ( ) ;
packetRecords. setAmount ( amount) ;
packetRecords. setUserId ( loginUserId. intValue ( ) ) ;
packetRecords. setRedPacketId ( redPackets. getPacketId ( ) ) ;
packetRecords. setAvatar ( loginUser. getAvatar ( ) ) ;
packetRecords. setCreateTime ( new Date ( ) ) ;
packetRecords. setNickname ( loginUser. getNickname ( ) ) ;
packetRecords. setWatchwordContent ( dismantleRedPacketDTO. getWatchwordContent ( ) ) ;
RedPacketRecordsBO redPacketRecordsBO = new RedPacketRecordsBO ( ) ;
BeanUtils . copyProperties ( packetRecords, redPacketRecordsBO) ;
redPacketRecordsBO. setNickname ( redPackets. getNickname ( ) ) ;
Map < String , Object > map = new HashMap < > ( 3 ) ;
map. put ( "redPackets" , redPackets) ;
map. put ( "redPacketRecords" , redPacketRecordsBO) ;
rabbitTemplate. convertAndSend ( entryExchangeName, entryRoutingKey, map, correlationData -> {
correlationData. getMessageProperties ( ) . setHeader ( "tenant-id" , TenantContextHolder . getTenantId ( ) ) ;
correlationData. getMessageProperties ( ) . setHeader ( "user-id" , loginUserId) ;
return correlationData;
} ) ;
log. info ( "mq异步入账" ) ;
redPacketRedisDAO. set ( dismantleRedPacketDTO. getPacketId ( ) , redPackets) ;
log. info ( "红包记录状态更新【{}】,并写入redis" , redPackets) ;
redPacketRecordsRedisDAO. set ( dismantleRedPacketDTO. getPacketId ( ) , loginUserId. toString ( ) , packetRecords) ;
log. info ( "用户【{}】抢到的红包【{}】" , loginUser, packetRecords) ;
RedPacketInfoVO redPacketVO = buildRedPacketVO ( redPackets, packetRecords) ;
redPacketQualificationDAO. delete ( Constant . RED_PACKET_QUALIFICATIONS_KEY + loginUserId + "_" + dismantleRedPacketDTO. getPacketId ( ) ) ;
return redPacketVO;
} finally {
lock. unlock ( ) ;
}
}
@Override
public RedPacketInfoVO getPacketRecord ( String packetId) {
RedPacketInfoDO redPackets = redPacketRedisDAO. get ( packetId) ;
AssertUtils . isTrue ( ObjectUtil . isEmpty ( redPackets) , RECORD_ERROR ) ;
Long loginUserId = SecurityFrameworkUtils . getLoginUserId ( ) ;
RedPacketRecordsDO redPacketRecords = redPacketRecordsRedisDAO. get ( packetId, loginUserId. toString ( ) ) ;
RedPacketInfoVO redPacketVO = buildRedPacketVO ( redPackets, redPacketRecords) ;
log. info ( "红包记录【{}】" , redPacketVO) ;
return redPacketVO;
}
private BigDecimal calculate ( RedPacketInfoDO redPacketInfo) {
BigDecimal amount = BigDecimal . valueOf ( 0 ) ;
if ( redPacketInfo. getType ( ) . equals ( RedPacketTypeEnum . LUCKY . getType ( ) ) || redPacketInfo. getType ( ) . equals ( RedPacketTypeEnum . TEXT_WATCHWORD . getType ( ) ) || redPacketInfo. getType ( ) . equals ( RedPacketTypeEnum . VOICE_WATCHWORD . getType ( ) ) ) {
if ( redPacketInfo. getLeftPacket ( ) == 1 ) {
amount = redPacketInfo. getLeftAmount ( ) ;
} else {
BigDecimal result = redPacketInfo. getLeftAmount ( ) . divide ( BigDecimal . valueOf ( redPacketInfo. getLeftPacket ( ) ) , 0 , RoundingMode . UP ) ;
result = result. multiply ( BigDecimal . valueOf ( 2 ) ) ;
int limit = ( int ) ( result. doubleValue ( ) * 100 ) ;
amount = BigDecimal . valueOf ( RandomUtil . randomInt ( limit - 1 ) + 1 ) . divide ( BigDecimal . valueOf ( 100 ) , 0 , RoundingMode . UP ) ;
}
} else if ( redPacketInfo. getType ( ) . equals ( RedPacketTypeEnum . ORDINARY . getType ( ) ) ) {
amount = redPacketInfo. getAmountOne ( ) ;
}
return amount;
}
private RedPacketInfoVO buildRedPacketVO ( RedPacketInfoDO redPackets, RedPacketRecordsDO redPacketRecordsDO) {
RedPacketInfoVO redPacketVO = new RedPacketInfoVO ( ) ;
redPacketVO. setSendUserName ( redPackets. getNickname ( ) ) ;
redPacketVO. setAvatar ( redPackets. getAvatar ( ) ) ;
if ( null != redPacketRecordsDO) {
redPacketVO. setReceiveAmount ( redPacketRecordsDO. getAmount ( ) ) ;
}
redPacketVO. setTotalPacket ( redPackets. getTotalPacket ( ) ) ;
redPacketVO. setGetPacket ( redPackets. getTotalPacket ( ) - redPackets. getLeftPacket ( ) ) ;
redPacketVO. setTotalAmount ( redPackets. getTotalAmount ( ) ) ;
redPacketVO. setLeftAmount ( redPackets. getLeftAmount ( ) ) ;
redPacketVO. setBlessing ( redPackets. getBlessing ( ) ) ;
redPacketVO. setWatchwordContent ( redPackets. getWatchwordContent ( ) ) ;
Map < String , RedPacketRecordsDO > entries = redPacketRecordsRedisDAO. entries ( redPackets. getPacketId ( ) ) ;
List < RedPacketRecordsDO > redPacketRecordList = new ArrayList < > ( entries. values ( ) ) ;
if ( entries. size ( ) == 0 ) {
redPacketRecordList = redPacketRecordsMapper. getRedPacketRecordList ( redPackets. getPacketId ( ) ) ;
}
List < RedPacketRecordsVO > list = ObjectConvertUtil . convertInstance ( ) . objectConvert ( redPacketRecordList, RedPacketRecordsVO . class ) ;
redPacketVO. setRecordsList ( list) ;
return redPacketVO;
}
private void judge ( Long loginUserId, String packetId, RedPacketInfoDO redPackets, RedPacketRecordsDO redPacketRecords) {
AssertUtils . isTrue ( StrUtil . isBlank ( packetId) , PARAMETER_EXCEPTION ) ;
RedPacketInfoDO redPacketInfoDO = redPacketRedisDAO. get ( packetId) ;
if ( ObjectUtil . isNotNull ( redPacketInfoDO) ) {
BeanUtils . copyProperties ( redPacketInfoDO, redPackets) ;
}
AssertUtils . isTrue ( ObjectUtil . isNull ( redPacketInfoDO) , RECORD_ERROR ) ;
RedPacketRecordsDO redPacketRecordsDO = redPacketRecordsRedisDAO. get ( packetId, loginUserId. toString ( ) ) ;
if ( ObjectUtil . isNotNull ( redPacketRecordsDO) ) {
BeanUtils . copyProperties ( redPacketRecordsDO, redPacketRecords) ;
}
AssertUtils . isTrue ( ObjectUtil . isNotNull ( redPacketRecordsDO) , RECEIVED ) ;
AssertUtils . isTrue ( ObjectUtil . equals ( RedPacketStatusEnum . EXPIRED . getStatus ( ) , redPacketInfoDO. getStatus ( ) ) , EXPIRED ) ;
AssertUtils . isTrue ( RedPacketStatusEnum . WITHOUT . getStatus ( ) . equals ( redPacketInfoDO. getStatus ( ) ) || redPacketInfoDO. getLeftPacket ( ) < 1 , ROBBED ) ;
}
}
7.application.yaml
数据库配置省略了,你自己加上
spring :
rabbitmq :
host : 10.0.99.114
port : 5672
username : admin
password : admin
publisher-confirm-type : correlated
publisher-returns : true
virtual-host : /
listener :
simple :
acknowledge-mode : manual
template :
mandatory : true
redis :
host : 10.0.99.191
port : 6379
database : 8
password : 'password'
red_packet :
backtrack :
delayed-queue-name : red.packet.delayed.queue- test
delayed-exchange-name : red.packet.delayed.exchange- test
delayed-routing-key : red.packet.delayed.routing.key- test
dead-letter-queue-name : red.packet.dead.letter.delayed.queue- test
dead-letter-exchange-name : red.packet.dead.letter.delayed.exchange- test
dead-letter-routing-key : red.packet.dead.letter.delayed.routing.key- test
8.controller
package cn. inno. pala. module. packet. controller. app ;
import cn. inno. pala. framework. common. pojo. CommonResult ;
import cn. inno. pala. module. packet. controller. app. dto. DismantleRedPacketDTO ;
import cn. inno. pala. module. packet. controller. app. dto. RedPacketsDTO ;
import cn. inno. pala. module. packet. controller. app. vo. RedPacketInfoVO ;
import cn. inno. pala. module. packet. controller. app. vo. RedPacketQualificationsVO ;
import cn. inno. pala. module. packet. service. RedPacketInfoService ;
import io. swagger. annotations. Api ;
import io. swagger. annotations. ApiOperation ;
import org. springframework. web. bind. annotation. * ;
import javax. annotation. Resource ;
import javax. validation. Valid ;
import static cn. inno. pala. framework. common. pojo. CommonResult . success ;
@RestController
@Api ( tags = "用户 APP - 红包" )
@RequestMapping ( "/redPacket" )
public class RedPacketInfoController {
@Resource
private RedPacketInfoService redPacketInfoService;
@PostMapping ( "/send" )
@ApiOperation ( "发红包接口" )
public CommonResult < String > sendPacket ( @RequestBody @Valid RedPacketsDTO redPacketsDTO) {
return success ( redPacketInfoService. sendRedPacket ( redPacketsDTO) ) ;
}
@GetMapping ( "/grab/{packetId}" )
@ApiOperation ( "抢红包接口" )
public CommonResult < RedPacketQualificationsVO > grabPacket ( @PathVariable ( "packetId" ) String packetId) {
return success ( redPacketInfoService. grabPacket ( packetId) ) ;
}
@PostMapping ( "/rob" )
@ApiOperation ( "拆红包接口" )
public CommonResult < RedPacketInfoVO > getPacket ( @RequestBody DismantleRedPacketDTO dismantleRedPacketDTO) {
return success ( redPacketInfoService. getPacket ( dismantleRedPacketDTO) ) ;
}
@GetMapping ( "/get/{packetId}" )
@ApiOperation ( "红包记录接口" )
public CommonResult < RedPacketInfoVO > getPacketRecord ( @PathVariable ( "packetId" ) String packetId) {
return success ( redPacketInfoService. getPacketRecord ( packetId) ) ;
}
}
9.VO
RedPacketInfoVO
package cn. inno. pala. module. packet. controller. app. vo ;
import io. swagger. annotations. ApiModelProperty ;
import lombok. Data ;
import java. io. Serializable ;
import java. math. BigDecimal ;
import java. util. List ;
@Data
public class RedPacketInfoVO implements Serializable {
private static final long serialVersionUID = 1L ;
@ApiModelProperty ( value = "发红包的用户名" )
private String sendUserName;
@ApiModelProperty ( value = "头像" )
private String avatar;
@ApiModelProperty ( value = "当前登陆用户领取的红包金额" )
private BigDecimal receiveAmount;
@ApiModelProperty ( value = "红包总数量" )
private Integer totalPacket;
@ApiModelProperty ( value = "已领取的红包数量" )
private Integer getPacket;
@ApiModelProperty ( value = "红包总金额" )
private BigDecimal totalAmount;
@ApiModelProperty ( value = "剩余红包金额" )
private BigDecimal leftAmount;
@ApiModelProperty ( value = "口令内容" )
private String watchwordContent;
@ApiModelProperty ( value = "祝福语" )
private String blessing;
@ApiModelProperty ( value = "红包领取记录集合" )
private List < RedPacketRecordsVO > recordsList;
}
RedPacketQualificationsVO
package cn. inno. pala. module. packet. controller. app. vo ;
import io. swagger. annotations. ApiModelProperty ;
import lombok. Builder ;
import lombok. Data ;
import java. io. Serializable ;
@Data
@Builder
public class RedPacketQualificationsVO implements Serializable {
private static final long serialVersionUID = 1L ;
@ApiModelProperty ( value = "true 有资格抢" )
private Boolean flag;
@ApiModelProperty ( value = "拆红包签名" )
private String sign;
}
RedPacketRecordsVO
package cn. inno. pala. module. packet. controller. app. vo ;
import io. swagger. annotations. ApiModelProperty ;
import lombok. Data ;
import java. io. Serializable ;
import java. math. BigDecimal ;
import java. util. Date ;
@Data
public class RedPacketRecordsVO implements Serializable {
private static final long serialVersionUID = 1L ;
@ApiModelProperty ( value = "用户名" )
private String nickname;
@ApiModelProperty ( value = "红包金额" )
private BigDecimal amount;
@ApiModelProperty ( value = "抢红包时间" )
private Date createTime;
@ApiModelProperty ( value = "头像" )
private String avatar;
}
10.DTO
DismantleRedPacketDTO
package cn. inno. pala. module. packet. controller. app. dto ;
import io. swagger. annotations. ApiModelProperty ;
import lombok. Data ;
import javax. validation. constraints. NotBlank ;
import java. io. Serializable ;
@Data
public class DismantleRedPacketDTO implements Serializable {
@ApiModelProperty ( value = "红包id" , required = true )
@NotBlank ( message = "红包id不能为空" )
private String packetId;
@ApiModelProperty ( value = "口令内容" )
private String watchwordContent;
@ApiModelProperty ( value = "签名" )
private String sign;
}
RedPacketsDTO
package cn. inno. pala. module. packet. controller. app. dto ;
import io. swagger. annotations. ApiModelProperty ;
import lombok. AllArgsConstructor ;
import lombok. Data ;
import lombok. NoArgsConstructor ;
import org. hibernate. validator. constraints. Range ;
import javax. validation. constraints. NotBlank ;
import javax. validation. constraints. NotNull ;
import javax. validation. constraints. Pattern ;
import java. io. Serializable ;
import java. math. BigDecimal ;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RedPacketsDTO implements Serializable {
private static final long serialVersionUID = 1L ;
@ApiModelProperty ( value = "红包类型(0=拼手气红包,1=普通红包,2=文字口令红包,3=语音口令红包)" , required = true )
@NotBlank ( message = "红包类型不能为空" )
@Range ( min = 0 , max = 3 , message = "红包类型必须在规定内" )
private String type;
@ApiModelProperty ( value = "红包总金额" )
private BigDecimal totalAmount;
@ApiModelProperty ( value = "口令内容" )
private String watchwordContent;
@ApiModelProperty ( value = "红包个数" , required = true )
@NotNull ( message = "红包个数不能为空" )
@Pattern ( regexp = "-?[1-9]\\d*" , message = "红包个数不能为小数" )
private String totalPacket;
@ApiModelProperty ( value = "单个红包金额" )
private BigDecimal amountOne;
@ApiModelProperty ( value = "祝福语" )
private String blessing;
@ApiModelProperty ( value = "封面" )
private String cover;
@ApiModelProperty ( value = "0.私发,1.群发" )
@NotBlank ( message = "类型不能为空" )
@Range ( min = 0 , max = 1 , message = "类型必须在规定内" )
private String sendType;
@ApiModelProperty ( value = "私发的目标昵称,群发不需要传" )
private String nickname;
}
11.BO
RedPacketRecordsBO
package cn. inno. pala. module. packet. controller. app. bo ;
import lombok. Data ;
import lombok. experimental. Accessors ;
import java. io. Serializable ;
import java. math. BigDecimal ;
@Data
@Accessors ( chain = true )
public class RedPacketRecordsBO implements Serializable {
private static final long serialVersionUID = 1L ;
private Integer id;
private Integer userId;
private BigDecimal amount;
private String redPacketId;
private String nickname;
private String watchwordContent;
}
12.mp
RedPacketDelayMessage
package cn. inno. pala. module. packet. mp. message ;
import org. springframework. amqp. core. * ;
import org. springframework. beans. factory. annotation. Qualifier ;
import org. springframework. beans. factory. annotation. Value ;
import org. springframework. context. annotation. Bean ;
import org. springframework. context. annotation. Configuration ;
import java. util. HashMap ;
import java. util. Map ;
@Configuration
public class RedPacketDelayMessage {
@Value ( "${red-packet.backtrack.delayed-queue-name}" )
public String delayedQueueName;
@Value ( "${red-packet.backtrack.delayed-exchange-name}" )
public String delayedExchangeName;
@Value ( "${red-packet.backtrack.delayed-routing-key}" )
public String delayedRoutingKey;
@Value ( "${red-packet.backtrack.dead-letter-queue-name}" )
public String deadLetterQueueName;
@Value ( "${red-packet.backtrack.dead-letter-exchange-name}" )
public String deadLetterExchangeName;
@Value ( "${red-packet.backtrack.dead-letter-routing-key}" )
public String deadLetterRoutingKey;
@Bean
public Queue delayedQueue ( ) {
Map < String , Object > args = new HashMap < > ( 2 ) ;
args. put ( "x-dead-letter-exchange" , deadLetterExchangeName) ;
args. put ( "x-dead-letter-routing-key" , deadLetterRoutingKey) ;
return QueueBuilder . durable ( delayedQueueName) . withArguments ( args) . build ( ) ;
}
@Bean
public Queue delayedDeadLetterQueue ( ) {
return new Queue ( deadLetterQueueName) ;
}
@Bean
public CustomExchange delayedExchange ( ) {
Map < String , Object > args = new HashMap < > ( 1 ) ;
args. put ( "x-delayed-type" , "direct" ) ;
return new CustomExchange ( delayedExchangeName, "x-delayed-message" , true , false , args) ;
}
@Bean
public DirectExchange delayedDeadLetterExchange ( ) {
return new DirectExchange ( deadLetterExchangeName) ;
}
@Bean
public Binding bindingDelayedQueue ( @Qualifier ( "delayedQueue" ) Queue queue,
@Qualifier ( "delayedExchange" ) CustomExchange delayedExchange) {
return BindingBuilder . bind ( queue) . to ( delayedExchange) . with ( delayedRoutingKey) . noargs ( ) ;
}
@Bean
public Binding bindingDelayedDeadLetterQueue ( @Qualifier ( "delayedDeadLetterQueue" ) Queue queue,
@Qualifier ( "delayedDeadLetterExchange" ) DirectExchange exchange) {
return BindingBuilder . bind ( queue) . to ( exchange) . with ( deadLetterRoutingKey) ;
}
}
RedPacketEntryMessage
package cn. inno. pala. module. packet. mp. message ;
import org. springframework. amqp. core. * ;
import org. springframework. beans. factory. annotation. Qualifier ;
import org. springframework. beans. factory. annotation. Value ;
import org. springframework. context. annotation. Bean ;
import org. springframework. context. annotation. Configuration ;
import java. util. HashMap ;
import java. util. Map ;
@Configuration
public class RedPacketEntryMessage {
@Value ( "${red-packet.warehousing.entry-queue-name}" )
public String entryQueueName;
@Value ( "${red-packet.warehousing.entry-exchange-name}" )
public String entryExchangeName;
@Value ( "${red-packet.warehousing.entry-routing-key}" )
public String entryRoutingKey;
@Value ( "${red-packet.warehousing.dead-letter-queue-name}" )
public String deadLetterQueueName;
@Value ( "${red-packet.warehousing.dead-letter-exchange-name}" )
public String deadLetterExchangeName;
@Value ( "${red-packet.warehousing.dead-letter-routing-key}" )
public String deadLetterRoutingKey;
@Bean
public Queue entryQueue ( ) {
Map < String , Object > args = new HashMap < > ( 2 ) ;
args. put ( "x-dead-letter-exchange" , deadLetterExchangeName) ;
args. put ( "x-dead-letter-routing-key" , deadLetterRoutingKey) ;
return QueueBuilder . durable ( entryQueueName) . withArguments ( args) . build ( ) ;
}
@Bean
public Queue entryDeadLetterQueue ( ) {
return new Queue ( deadLetterQueueName) ;
}
@Bean
public DirectExchange entryExchange ( ) {
return new DirectExchange ( entryExchangeName, true , false ) ;
}
@Bean
public DirectExchange entryDeadLetterExchange ( ) {
return new DirectExchange ( deadLetterExchangeName) ;
}
@Bean
public Binding bindingEntryQueue ( @Qualifier ( "entryQueue" ) Queue queue,
@Qualifier ( "entryExchange" ) DirectExchange entryExchange) {
return BindingBuilder . bind ( queue) . to ( entryExchange) . with ( entryRoutingKey) ;
}
@Bean
public Binding bindingEntryDeadLetterQueue ( @Qualifier ( "entryDeadLetterQueue" ) Queue queue,
@Qualifier ( "entryDeadLetterExchange" ) DirectExchange exchange) {
return BindingBuilder . bind ( queue) . to ( exchange) . with ( deadLetterRoutingKey) ;
}
}
RedPacketDelayConsumer
package cn. inno. pala. module. packet. mp. consumer ;
import cn. hutool. core. util. ObjectUtil ;
import cn. inno. pala. framework. common. exception. ServiceException ;
import cn. inno. pala. framework. common. util. validation. AssertUtils ;
import cn. inno. pala. framework. tenant. core. context. TenantContextHolder ;
import cn. inno. pala. module. packet. dal. dataobject. RedPacketInfoDO ;
import cn. inno. pala. module. packet. dal. mysql. RedPacketInfoMapper ;
import cn. inno. pala. module. packet. dal. redis. packet. RedPacketRedisDAO ;
import cn. inno. pala. module. packet. enums. RedPacketStatusEnum ;
import cn. inno. pala. module. pay. enums. wallet. PalaWalletEnum ;
import cn. inno. pala. module. pay. wallet. ApiWalletService ;
import cn. inno. pala. module. pay. wallet. vo. ApiAddPalaCoinReqVO ;
import cn. inno. pala. module. pay. wallet. vo. ApiAddPalaCoinRespVO ;
import com. baomidou. mybatisplus. core. conditions. query. QueryWrapper ;
import com. rabbitmq. client. Channel ;
import lombok. extern. slf4j. Slf4j ;
import org. springframework. amqp. core. Message ;
import org. springframework. amqp. rabbit. annotation. RabbitListener ;
import org. springframework. stereotype. Component ;
import org. springframework. transaction. annotation. Transactional ;
import javax. annotation. Resource ;
import static cn. inno. pala. module. packet. enums. ErrorCodeConstants . ADD_ACCOUNT_ERROR ;
@Component
@Slf4j
public class RedPacketDelayConsumer {
@Resource
private RedPacketInfoMapper redPacketInfoMapper;
@Resource
private ApiWalletService apiWalletService;
@Resource
private RedPacketRedisDAO redPacketRedisDAO;
@RabbitListener ( queues = "#{delayedQueue.name}" )
@Transactional ( rollbackFor = Exception . class )
public void receiveDelayedQueue ( Message message, Channel channel) throws Exception {
try {
business ( message) ;
channel. basicAck ( message. getMessageProperties ( ) . getDeliveryTag ( ) , false ) ;
} catch ( Exception e) {
if ( e instanceof ServiceException ) {
channel. basicAck ( message. getMessageProperties ( ) . getDeliveryTag ( ) , false ) ;
return ;
}
channel. basicNack ( message. getMessageProperties ( ) . getDeliveryTag ( ) , false , false ) ;
e. printStackTrace ( ) ;
}
}
@RabbitListener ( queues = "#{delayedDeadLetterQueue.name}" )
@Transactional ( rollbackFor = Exception . class )
public void receiveDelayedDeadLetterQueue ( Message message, Channel channel) throws Exception {
try {
business ( message) ;
channel. basicAck ( message. getMessageProperties ( ) . getDeliveryTag ( ) , false ) ;
} catch ( Exception e) {
if ( e instanceof ServiceException ) {
channel. basicAck ( message. getMessageProperties ( ) . getDeliveryTag ( ) , false ) ;
return ;
}
channel. basicNack ( message. getMessageProperties ( ) . getDeliveryTag ( ) , false , false ) ;
e. printStackTrace ( ) ;
}
}
private void business ( Message message) {
String tenantId = message. getMessageProperties ( ) . getHeader ( "tenant-id" ) . toString ( ) ;
String userId = message. getMessageProperties ( ) . getHeader ( "user-id" ) . toString ( ) ;
TenantContextHolder . setTenantId ( Long . valueOf ( tenantId) ) ;
String redPacketId = new String ( message. getBody ( ) ) ;
RedPacketInfoDO redPacketInfo = redPacketInfoMapper. selectOne ( "packet_id" , redPacketId) ;
if ( ObjectUtil . isNotNull ( redPacketInfo) && redPacketInfo. getLeftPacket ( ) != 0 && redPacketInfo. getLeftAmount ( ) . intValue ( ) != 0 ) {
ApiAddPalaCoinReqVO palaCoinReq = new ApiAddPalaCoinReqVO ( )
. setUserId ( Long . valueOf ( redPacketInfo. getUserId ( ) ) )
. setContent ( "红包退款" )
. setBusiness ( PalaWalletEnum . ADD_RETURN_THE_RED_ENVELOPE . getStatus ( ) )
. setTargetId ( redPacketInfo. getPacketId ( ) )
. setOperationPalaCoin ( redPacketInfo. getLeftAmount ( ) ) ;
ApiAddPalaCoinRespVO apiAddPalaCoinResp = apiWalletService. addPalaCoin ( palaCoinReq) ;
log. info ( "退还给用户【】,金额【{}】,红包id【{}】" , redPacketInfo. getUserId ( ) , redPacketInfo. getLeftAmount ( ) , redPacketInfo. getPacketId ( ) ) ;
AssertUtils . isTrue ( ! apiAddPalaCoinResp. getState ( ) , ADD_ACCOUNT_ERROR ) ;
RedPacketInfoDO redPacketInfoDO = new RedPacketInfoDO ( ) ;
redPacketInfoDO. setStatus ( RedPacketStatusEnum . EXPIRED . getStatus ( ) ) ;
redPacketInfoDO. setUpdater ( userId) ;
redPacketInfoMapper. update ( redPacketInfoDO, new QueryWrapper < RedPacketInfoDO > ( ) . eq ( "packet_id" , redPacketInfo. getPacketId ( ) ) ) ;
log. info ( "修改数据库状态为已过期" ) ;
redPacketInfo. setStatus ( RedPacketStatusEnum . EXPIRED . getStatus ( ) ) ;
redPacketRedisDAO. set ( redPacketInfo. getPacketId ( ) , redPacketInfo) ;
log. info ( "修改redis状态为已过期" ) ;
}
}
}
RedPacketEntryConsumer
package cn. inno. pala. module. packet. mp. consumer ;
import cn. inno. pala. framework. common. exception. ServiceException ;
import cn. inno. pala. framework. common. util. validation. AssertUtils ;
import cn. inno. pala. framework. tenant. core. context. TenantContextHolder ;
import cn. inno. pala. module. packet. controller. app. bo. RedPacketRecordsBO ;
import cn. inno. pala. module. packet. dal. dataobject. RedPacketInfoDO ;
import cn. inno. pala. module. packet. dal. dataobject. RedPacketRecordsDO ;
import cn. inno. pala. module. packet. dal. mysql. RedPacketInfoMapper ;
import cn. inno. pala. module. packet. dal. mysql. RedPacketRecordsMapper ;
import cn. inno. pala. module. pay. enums. wallet. ConversionTypeEnum ;
import cn. inno. pala. module. pay. enums. wallet. PalaTypeEnum ;
import cn. inno. pala. module. pay. enums. wallet. PalaWalletDiamondsnEnum ;
import cn. inno. pala. module. pay. wallet. ApiWalletService ;
import cn. inno. pala. module. pay. wallet. vo. ApiDiamondsnRespVO ;
import cn. inno. pala. module. pay. wallet. vo. ApiSetDiamondsnReqVO ;
import com. baomidou. mybatisplus. core. conditions. query. QueryWrapper ;
import com. rabbitmq. client. Channel ;
import lombok. extern. slf4j. Slf4j ;
import org. springframework. amqp. core. Message ;
import org. springframework. amqp. rabbit. annotation. RabbitListener ;
import org. springframework. amqp. rabbit. core. RabbitTemplate ;
import org. springframework. stereotype. Component ;
import org. springframework. transaction. annotation. Transactional ;
import javax. annotation. Resource ;
import java. io. IOException ;
import java. util. Map ;
import static cn. inno. pala. module. packet. enums. ErrorCodeConstants . ADD_ACCOUNT_ERROR ;
@Component
@Slf4j
public class RedPacketEntryConsumer {
@Resource
private ApiWalletService apiWalletService;
@Resource
private RabbitTemplate rabbitTemplate;
@Resource
private RedPacketInfoMapper redPacketInfoMapper;
@Resource
private RedPacketRecordsMapper redPacketRecordsMapper;
@RabbitListener ( queues = "#{entryQueue.name}" )
@Transactional ( rollbackFor = Exception . class )
public void receiveEntryQueue ( Message message, Channel channel) throws IOException {
try {
business ( message) ;
channel. basicAck ( message. getMessageProperties ( ) . getDeliveryTag ( ) , false ) ;
} catch ( Exception e) {
if ( e instanceof ServiceException ) {
channel. basicAck ( message. getMessageProperties ( ) . getDeliveryTag ( ) , false ) ;
return ;
}
channel. basicNack ( message. getMessageProperties ( ) . getDeliveryTag ( ) , false , false ) ;
e. printStackTrace ( ) ;
}
}
@RabbitListener ( queues = "#{entryDeadLetterQueue.name}" )
@Transactional ( rollbackFor = Exception . class )
public void receiveEntryDeadLetterQueue ( Message message, Channel channel) throws Exception {
try {
business ( message) ;
channel. basicAck ( message. getMessageProperties ( ) . getDeliveryTag ( ) , false ) ;
} catch ( Exception e) {
if ( e instanceof ServiceException ) {
channel. basicAck ( message. getMessageProperties ( ) . getDeliveryTag ( ) , false ) ;
return ;
}
channel. basicNack ( message. getMessageProperties ( ) . getDeliveryTag ( ) , false , false ) ;
e. printStackTrace ( ) ;
}
}
private void business ( Message message) {
String tenantId = message. getMessageProperties ( ) . getHeader ( "tenant-id" ) . toString ( ) ;
String userId = message. getMessageProperties ( ) . getHeader ( "user-id" ) . toString ( ) ;
TenantContextHolder . setTenantId ( Long . valueOf ( tenantId) ) ;
Map < String , Object > map = ( Map < String , Object > ) rabbitTemplate. getMessageConverter ( ) . fromMessage ( message) ;
RedPacketInfoDO redPackets = ( RedPacketInfoDO ) map. get ( "redPackets" ) ;
redPacketInfoMapper. update ( redPackets, new QueryWrapper < RedPacketInfoDO > ( ) . eq ( "packet_id" , redPackets. getPacketId ( ) ) ) ;
log. info ( "修改数据库红包金额和个数【{}】" , redPackets) ;
RedPacketRecordsBO redPacketRecords = ( RedPacketRecordsBO ) map. get ( "redPacketRecords" ) ;
ApiSetDiamondsnReqVO apiSetDiamondsnReq = new ApiSetDiamondsnReqVO ( ) ;
apiSetDiamondsnReq. setUserId ( Long . valueOf ( userId) ) ;
apiSetDiamondsnReq. setContent ( "抢到昵称" + redPacketRecords. getNickname ( ) + "红包" ) ;
apiSetDiamondsnReq. setBusiness ( PalaWalletDiamondsnEnum . ADD_RECEIVED_A_RED_ENVELOPE . getStatus ( ) ) ;
apiSetDiamondsnReq. setTargetId ( redPacketRecords. getRedPacketId ( ) ) ;
apiSetDiamondsnReq. setConversionType ( ConversionTypeEnum . PARRA_CURRENCY . getStatus ( ) ) ;
apiSetDiamondsnReq. setType ( PalaTypeEnum . TYPE_ADD . getStatus ( ) ) ;
apiSetDiamondsnReq. setOperationQuota ( redPacketRecords. getAmount ( ) ) ;
ApiDiamondsnRespVO apiDiamondsnResp = apiWalletService. setDiamondsn ( apiSetDiamondsnReq) ;
AssertUtils . isTrue ( ! apiDiamondsnResp. getState ( ) , ADD_ACCOUNT_ERROR ) ;
log. info ( "抢到昵称【{}】 金额【{}】" , redPacketRecords. getNickname ( ) , redPacketRecords. getAmount ( ) ) ;
RedPacketRecordsDO redPacketRecordsDO = new RedPacketRecordsDO ( ) ;
redPacketRecordsDO. setAmount ( redPacketRecords. getAmount ( ) ) ;
redPacketRecordsDO. setRedPacketId ( redPacketRecords. getRedPacketId ( ) ) ;
redPacketRecordsDO. setUserId ( redPacketRecords. getUserId ( ) ) ;
redPacketRecordsDO. setUpdater ( userId) ;
redPacketRecordsDO. setCreator ( userId) ;
redPacketRecordsDO. setWatchwordContent ( redPacketRecords. getWatchwordContent ( ) ) ;
redPacketRecordsMapper. insert ( redPacketRecordsDO) ;
log. info ( "插入红包记录【{}】" , redPacketRecordsDO) ;
}
}