206 lines
6.6 KiB
Java
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));
|
|
}
|
|
}
|