Files
xiu_b/src/main/java/com/tailbet/service/impl/RedPacketServiceImpl.java
T
2026-08-05 13:48:48 +08:00

206 lines
6.6 KiB
Java

package com.tailbet.service.impl;
import com.tailbet.model.entity.RedPacket;
import com.tailbet.model.entity.RedPacketRecv;
import com.tailbet.model.entity.User;
import com.tailbet.mapper.RedPacketMapper;
import com.tailbet.mapper.RedPacketRecvMapper;
import com.tailbet.mapper.UserMapper;
import com.tailbet.mapper.GameRoundMapper;
import com.tailbet.service.IRedPacketService;
import com.tailbet.service.IPointsService;
import com.tailbet.service.IAuditService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
/**
* 红包Service实现
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class RedPacketServiceImpl implements IRedPacketService {
private final RedPacketMapper redPacketMapper;
private final RedPacketRecvMapper redPacketRecvMapper;
private final UserMapper userMapper;
private final GameRoundMapper gameRoundMapper;
private final IPointsService pointsService;
private final IAuditService auditService;
/**
* 发红包
*/
@Override
@Transactional
public RedPacket sendRp(Long userId, Long groupId, BigDecimal totalAmount,
Integer totalCount, Long roundId) {
User user = userMapper.selectById(userId);
if (user == null) {
throw new RuntimeException("用户不存在");
}
// 扣积分
boolean success = pointsService.subPoints(userId, totalAmount.longValue(),
IPointsService.TYPE_RP_SEND, null, null, "发送红包");
if (!success) {
throw new RuntimeException("余额不足");
}
// 创建红包
RedPacket rp = new RedPacket();
rp.setUserId(userId);
rp.setGroupId(groupId);
rp.setRoundId(roundId);
rp.setTotalAmount(totalAmount);
rp.setTotalCount(totalCount);
rp.setRemainAmount(totalAmount);
rp.setRemainCount(totalCount);
rp.setStatus(STATUS_ONGOING);
rp.setCreateTime(LocalDateTime.now());
// 判断红包类型
if (roundId != null) {
rp.setType(TYPE_GAME);
} else {
rp.setType(TYPE_RANDOM);
}
redPacketMapper.insert(rp);
// 审计日志
auditService.log(groupId, roundId, userId, "RP_SEND",
String.format("{\"rpId\":%d,\"amount\":%s,\"count\":%d}",
rp.getId(), totalAmount, totalCount));
return rp;
}
/**
* 领红包
*/
@Override
@Transactional
public RedPacketRecv receiveRp(Long rpId, Long userId) {
RedPacket rp = redPacketMapper.selectById(rpId);
if (rp == null) {
throw new RuntimeException("红包不存在");
}
if (rp.getStatus() != STATUS_ONGOING) {
throw new RuntimeException("红包已领完");
}
// 检查是否已领取
RedPacketRecv existing = redPacketRecvMapper.selectOne(new LambdaQueryWrapper<RedPacketRecv>()
.eq(RedPacketRecv::getRpId, rpId)
.eq(RedPacketRecv::getUserId, userId));
if (existing != null) {
throw new RuntimeException("已领取过该红包");
}
// 计算领取金额
BigDecimal amount = calculateReceiveAmount(rp);
// 更新红包
rp.setRemainAmount(rp.getRemainAmount().subtract(amount));
rp.setRemainCount(rp.getRemainCount() - 1);
if (rp.getRemainCount() <= 0) {
rp.setStatus(STATUS_FINISHED);
}
redPacketMapper.updateById(rp);
// 加积分
pointsService.addPoints(userId, amount.longValue(),
IPointsService.TYPE_RP_RECV, String.valueOf(rpId), null, "领取红包");
// 记录领取
RedPacketRecv recv = new RedPacketRecv();
recv.setRpId(rpId);
recv.setUserId(userId);
recv.setAmount(amount);
recv.setCreateTime(LocalDateTime.now());
redPacketRecvMapper.insert(recv);
// 审计日志
auditService.log(rp.getGroupId(), rp.getRoundId(), userId, "RP_RECV",
String.format("{\"rpId\":%d,\"amount\":%s}", rpId, amount));
return recv;
}
/**
* 计算领取金额
*/
@Override
public BigDecimal calculateReceiveAmount(RedPacket rp) {
if (rp.getRemainCount() <= 0) {
return BigDecimal.ZERO;
}
// 如果是游戏红包且有目标尾数,则需要控制手气王
// 简化实现:随机分配
Random random = new Random();
if (rp.getRemainCount() == 1) {
// 最后一个,拿剩余全部
return rp.getRemainAmount();
}
// 随机金额,最小0.01
double min = 0.01;
double max = rp.getRemainAmount().divide(BigDecimal.valueOf(rp.getRemainCount()), 2, RoundingMode.HALF_UP).doubleValue() * 2;
double amount = min + (max - min) * random.nextDouble();
return BigDecimal.valueOf(amount).setScale(2, RoundingMode.HALF_UP);
}
/**
* 获取红包详情
*/
@Override
public RedPacket getById(Long rpId) {
return redPacketMapper.selectById(rpId);
}
/**
* 获取红包领取列表
*/
@Override
public List<RedPacketRecv> getReceives(Long rpId) {
return redPacketRecvMapper.selectList(new LambdaQueryWrapper<RedPacketRecv>()
.eq(RedPacketRecv::getRpId, rpId)
.orderByAsc(RedPacketRecv::getCreateTime));
}
/**
* 查询红包记录
*/
@Override
public Page<RedPacket> getRecords(Long userId, Integer pageNum, Integer pageSize) {
Page<RedPacket> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<RedPacket> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(RedPacket::getUserId, userId)
.orderByDesc(RedPacket::getCreateTime);
return redPacketMapper.selectPage(page, wrapper);
}
/**
* 查找进行中的游戏红包
*/
@Override
public RedPacket getOngoingGameRp(Long groupId) {
return redPacketMapper.selectOne(new LambdaQueryWrapper<RedPacket>()
.eq(RedPacket::getGroupId, groupId)
.eq(RedPacket::getType, TYPE_GAME)
.eq(RedPacket::getStatus, STATUS_ONGOING)
.isNotNull(RedPacket::getRoundId));
}
}