Parcourir la source

refactor(reward): 重构奖励发放逻辑

- 移除已删除奖励的处理逻辑
-优化排名信息的更新方式
- 新增消息通知功能
-改进奖励信息的获取和处理
- 优化 Redis 排行榜数据的更新
fugui001 il y a 4 mois
Parent
commit
ae5f731353

+ 0 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/controller/BlindStructuresController.java

@@ -6,7 +6,6 @@ import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.*;
 import cn.dev33.satoken.annotation.SaCheckPermission;
-import org.dromara.business.domain.vo.ItemsVo;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.validation.annotation.Validated;

+ 5 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/domain/RewardClaimsLog.java

@@ -74,5 +74,10 @@ public class RewardClaimsLog{
      */
     private Long isDelete;
 
+    /**
+     * 旧排名
+     */
+    private String oldRankJson;
+
 
 }

+ 57 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/domain/vo/MessagesTemplateVo.java

@@ -0,0 +1,57 @@
+package org.dromara.business.domain.vo;
+import cn.idev.excel.annotation.ExcelProperty;
+import lombok.Data;
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 消息主视图对象 messages
+ *
+ * @author Lion Li
+ * @date 2025-07-10
+ */
+@Data
+public class MessagesTemplateVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @ExcelProperty(value = "主键ID")
+    private Long id;
+
+    /**
+     * 模板唯一编码
+     */
+    @ExcelProperty(value = "模板唯一编码")
+    private String templateCode;
+
+    /**
+     * 标题模板
+     */
+    @ExcelProperty(value = "标题模板")
+    private String titleTemplate;
+
+    /**
+     * 内容模板
+     */
+    @ExcelProperty(value = "内容模板")
+    private String contentTemplate;
+
+    /**
+     * 发送方
+     */
+    @ExcelProperty(value = "发送方")
+    private String sendFrom;
+
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+
+}

+ 15 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/domain/vo/PlayerRankVo.java

@@ -0,0 +1,15 @@
+package org.dromara.business.domain.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class PlayerRankVo {
+
+    private Long id;
+    private String name;
+    private Long chips;
+    private Long rank;
+
+}

+ 1 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/domain/vo/RewardClaimsVo.java

@@ -92,4 +92,5 @@ public class RewardClaimsVo implements Serializable {
 
     List<RewardVo> rewardVoList;
 
+    private Boolean isDelete;
 }

+ 3 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/mapper/MessagesMapper.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.ibatis.annotations.Param;
 import org.dromara.business.domain.MessagesInfo;
+import org.dromara.business.domain.vo.MessagesTemplateVo;
 import org.dromara.business.domain.vo.MessagesVo;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 
@@ -39,5 +40,7 @@ public interface MessagesMapper extends BaseMapperPlus<MessagesInfo, MessagesVo>
     @InterceptorIgnore(tenantLine = "true")
     List<MessagesVo> selectMessagesList(@Param("ew") Wrapper<MessagesInfo> wrapper);
 
+    @InterceptorIgnore(tenantLine = "true")
+    MessagesTemplateVo selectMsgTemplateByCode(@Param("templateCode") String templateCode);
 
 }

+ 6 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/mapper/RewardClaimsLogMapper.java

@@ -8,6 +8,9 @@ import org.apache.ibatis.annotations.Param;
 import org.dromara.business.domain.RewardClaimsLog;
 import org.dromara.business.domain.vo.RewardClaimsLogVo;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+import java.util.List;
+
 /**
  * 被移除排名用户参赛获奖日志Mapper接口
  *
@@ -32,5 +35,8 @@ public interface RewardClaimsLogMapper extends BaseMapperPlus<RewardClaimsLog, R
     @InterceptorIgnore(tenantLine = "true")
     RewardClaimsLogVo selectRewardClaimsLogById(Long id);
 
+    @InterceptorIgnore(tenantLine = "true")
+    List<RewardClaimsLogVo> selectRewardClaimsLogInfoByCondition(@Param("tournamentId") Long tournamentId);
+
 
 }

+ 258 - 100
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/service/impl/RewardClaimsServiceImpl.java

@@ -3,16 +3,14 @@ package org.dromara.business.service.impl;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import org.dromara.business.domain.PlayerItems;
-import org.dromara.business.domain.PlayersItemsLog;
-import org.dromara.business.domain.RewardClaims;
-import org.dromara.business.domain.RewardClaimsLog;
+import org.dromara.business.domain.*;
 import org.dromara.business.domain.bo.RewardClaimsBo;
 import org.dromara.business.domain.enums.RewardStatusEnum;
 import org.dromara.business.domain.vo.*;
 import org.dromara.business.mapper.*;
 import org.dromara.business.service.IItemsService;
 import org.dromara.business.service.IRewardClaimsService;
+import org.dromara.business.service.ITournamentsService;
 import org.dromara.business.service.IUserService;
 import org.dromara.business.utils.ItemOperationLock;
 import org.dromara.business.utils.RedisKeys;
@@ -35,7 +33,6 @@ import org.springframework.stereotype.Service;
 
 import java.time.Duration;
 import java.util.*;
-import java.util.stream.Collectors;
 
 /**
  * 用户参赛Service业务层处理
@@ -65,19 +62,19 @@ public class RewardClaimsServiceImpl implements IRewardClaimsService {
 
     private final ItemsMapper itemsMapper;
 
-
     private final MessageReceiversMapper messageReceiversMapper;
 
     private final UserComplaintsMapper userComplaintsMapper;
 
     private final RewardClaimsLogMapper rewardClaimsLogMapper;
 
-    private final PrizeDistributionsMapper prizeDistributionsMapper;
-
-    private final PrizeDistributionItemsMapper prizeDistributionItemsMapper;
+    private final MessagesMapper messagesMapper;
 
     private final IUserService iUserService;
 
+    private final ITournamentsService tournamentsService;
+
+
 
     @Autowired
     RedisUtil redisUtil;
@@ -217,47 +214,24 @@ public class RewardClaimsServiceImpl implements IRewardClaimsService {
             //获奖名单信息  只要有认领了 审核通过后就不能删除了   都是未领取才能删除
             List<RewardClaimsVo> rewardClaimsVosList = baseMapper.selectByCriteria(bo.getTournamentId());
             boolean hasClaimed = rewardClaimsVosList.stream()
-                .anyMatch(vo -> vo.getClaimed() != null && vo.getClaimed() == 1);
+                .anyMatch(vo -> vo.getClaimed() != null && (vo.getClaimed() == 1 || vo.getClaimed() == 2));
             if(hasClaimed){
                 return false;
             }
 
+            //本场比赛所有的排名原始信息
+            Long tournamentId=bo.getTournamentId();
+            String leaderboardJson = redisUtil.get(RedisKeys.tournamentLeaderboard(tournamentId));
+            if(StringUtils.isEmpty(leaderboardJson)){
+                return false;
+            }
 
-            // 执行删除操作 baseMapper.deleteByCriteria(bo.getId(), bo.getTournamentId())
+            // 执行删除操作
             int rowsAffected = baseMapper.deleteByCriteria(bo.getId(), bo.getTournamentId());
 
             // 判断删除是否成功
             boolean isDeleted = rowsAffected > 0;
             if (isDeleted) {
-                //本次奖品信息
-                List<RewardVo> itemsPrizeList=new ArrayList<>();
-                //排名
-                List<PrizeDistributionsVo> prizeDistributionsVos = prizeDistributionsMapper.selectByTournamentId(bo.getTournamentId());
-                if(prizeDistributionsVos!=null){
-                    // 提取并按排名正序排序后,取出 id 列表
-                    List<Long> ids = prizeDistributionsVos.stream()
-                        .sorted(Comparator.comparing(PrizeDistributionsVo::getPaiming))
-                        .map(PrizeDistributionsVo::getId)
-                        .collect(Collectors.toList());
-                    for (Long id2 : ids) {
-                        //奖品
-                        PrizeDistributionItemsVo prizeDistributionItemsVos = prizeDistributionItemsMapper.selectByPrizeDistributionId(id2);
-                        RewardVo rewardVo = new RewardVo();
-                        rewardVo.setItemId(prizeDistributionItemsVos.getItemId());
-                        rewardVo.setItemName(prizeDistributionItemsVos.getItemsName());
-                        rewardVo.setQuantity(prizeDistributionItemsVos.getQuantity());
-                        itemsPrizeList.add(rewardVo);
-                    }
-                }
-                String itemsPrizeListString = "";
-                if(itemsPrizeList.size()>0){
-                    try {
-                        itemsPrizeListString = objectMapper.writeValueAsString(itemsPrizeList);
-                    } catch (JsonProcessingException e) {
-                        e.printStackTrace();
-                    }
-                    System.out.println(itemsPrizeListString);
-                }
 
                 //获奖名单信息
                 for (RewardClaimsVo claimsVo : rewardClaimsVosList) {
@@ -266,46 +240,253 @@ public class RewardClaimsServiceImpl implements IRewardClaimsService {
 
                     // 复制同名同类型的属性(自动忽略不匹配的字段)
                     BeanUtils.copyProperties(claimsVo, rewardClaimsLog);
-                    // 插入日志记录
-                    rewardClaimsLogMapper.insertRewardClaimsLog(rewardClaimsLog);
+                    rewardClaimsLog.setOldRankJson(leaderboardJson);
+                    if(!claimsVo.getIsDelete()){
+                        // 插入日志记录
+                        rewardClaimsLogMapper.insertRewardClaimsLog(rewardClaimsLog);
+                    }
+                }
+
+                // todo 得到删除人是第几名 rewardClaimsVo
+                Long claimsVoRank = rewardClaimsVo.getRank();
+                // 存储更新后的列表
+                List<RewardClaimsVo> updatedList = new ArrayList<>();
+
+                // 先按 rank 排序(确保顺序正确)
+                rewardClaimsVosList.sort(Comparator.comparing(RewardClaimsVo::getRank));
+
+
+                //拿出这次排名中 最后一位 去redis中取出这人信息
+                Optional<RewardClaimsVo> lastPlace = rewardClaimsVosList.stream()
+                    .max(Comparator.comparing(RewardClaimsVo::getRank));
+                //本场赛事最后一名
+                Long finalRank;
+                if (lastPlace.isPresent()) {
+                    RewardClaimsVo lastPlayer = lastPlace.get();
+                    finalRank=lastPlayer.getRank();
+                    System.out.println("最后一名 rank: " + lastPlayer.getRank());
+                    System.out.println("奖励: " + lastPlayer.getRewardJson());
+                    // 可以获取其他信息,如 playerId 等
+                } else {
+                    finalRank = null;
                 }
-                //删除原来的获奖信息列表
+
+                //把排名信息根据移除的人都向前推送一位
                 for (RewardClaimsVo claimsVo : rewardClaimsVosList) {
-                    baseMapper.deleteById(claimsVo.getId());
+                    Long voRank = claimsVo.getRank();
+
+                    if (voRank.equals(claimsVoRank)) {
+                        // 跳过要删除的玩家
+                        continue;
+                    }
+
+                    // 创建新对象或直接修改(建议 new 对象避免污染原数据)
+                    RewardClaimsVo updatedVo = new RewardClaimsVo();
+                    updatedVo.setId(claimsVo.getId());
+
+                    // 名次前移:所有 rank > 删除名次的,都 -1
+                    updatedVo.setRank(voRank > claimsVoRank ? voRank - 1 : voRank);
+
+                    // 关键:奖励信息也前移
+                    // 意思是:原来第3名的人,现在变成第2名,应获得原第2名的奖励
+                    // 所以我们设置:新排名 = 原排名 - 1 对应的奖励
+                    if (voRank > claimsVoRank) {
+                        // 名次前移,奖励也用前一名的奖励
+                        updatedVo.setRewardJson(getRewardByOriginalRank(voRank - 1, rewardClaimsVosList));
+                    } else {
+                        // rank < 删除名次:不受影响,奖励不变
+                        updatedVo.setRewardJson(claimsVo.getRewardJson());
+                    }
+                    updatedList.add(updatedVo);
+                    RewardClaims update = MapstructUtils.convert(updatedVo, RewardClaims.class);
+                    update.setIsDelete(false);
+                    update.setClaimed(0L);
+                    update.setRewardJson(updatedVo.getRewardJson());
+                    update.setPhone(claimsVo.getPhone());
+                    update.setRank(updatedVo.getRank());
+                    update.setPlayerId(claimsVo.getPlayerId());
+                    update.setPlayerName(claimsVo.getPlayerName());
+                    update.setTournamentId(claimsVo.getTournamentId());
+                    update.setUpdatedAt(new Date());
+                    //更新以此类推后的排名信息
+                    baseMapper.updateById(update);
+              }
+
+                //本次奖品信息 都去日志表reward_claims_log中拿到历史获奖信息和排名
+                // 所有奖品集合信息
+                List<HashMap<Long, Object>> rewardJsonList = new ArrayList<>();
+                List<RewardClaimsLogVo> rewardClaimsLogVoList = rewardClaimsLogMapper.selectRewardClaimsLogInfoByCondition(bo.getTournamentId());
+                for (RewardClaimsLogVo rewardClaimsLogVo : rewardClaimsLogVoList) {
+                     HashMap<Long, Object> rewardJsonMap = new HashMap<>();
+                     String rewardJson = rewardClaimsLogVo.getRewardJson();
+                     Long rewardClaimsLogVoRank = rewardClaimsLogVo.getRank();
+                     rewardJsonMap.put(rewardClaimsLogVoRank, rewardJson);
+                     rewardJsonList.add(rewardJsonMap);
                 }
 
-                //排名列表信息
+                //redis原始排名列表信息
                 //todo 删除成功之后进行替补  1:后面的往前推送  增加记录表  记录源数据来源  2:修改列表排名数据
-                Long tournamentId=bo.getTournamentId();
-                String leaderboardJson = redisUtil.get(RedisKeys.tournamentLeaderboard(tournamentId));
                 ObjectMapper objectMapper = new ObjectMapper();
-
-                List<PlayerVo> players = null;
-                players = objectMapper.readValue(leaderboardJson,
+                List<PlayerVo> players = objectMapper.readValue(leaderboardJson,
                     objectMapper.getTypeFactory().constructCollectionType(List.class, PlayerVo.class));
 
-                // 3. 从 players 中取出前 claimedCount 名选手(防止越界)
-                List<PlayerVo> topPlayers = new ArrayList<>();
-                if (players != null && !players.isEmpty()) {
-                    int endIndex = Math.min(rewardClaimsVosList.size(), players.size()); // 防止索引越界
-                    topPlayers = players.subList(0, endIndex);
+                // 2. 按 rank 排序(确保顺序)
+                players.sort(Comparator.comparing(PlayerVo::getRank));
+
+                if(players.size()==1){
+                    Iterator<PlayerVo> iterator = players.iterator();
+                    while (iterator.hasNext()) {
+                        PlayerVo player = iterator.next();
+                        if (Objects.equals(player.getId(), rewardClaimsVo.getPlayerId())) {
+                            // ✅ 现在 rankVoList 包含了更新后的完整排行榜(除了被删除的)
+                            //只有一个排行  删除之后元数据也删除
+                            redisUtil.delete(RedisKeys.tournamentLeaderboard(tournamentId));
+                        }
+                    }
                 }
-                for (PlayerVo topPlayer : topPlayers) {
+
+
+
+                // 原始排名+1  如果只有两个人 移除第一名 第二名变第一名   这边就拿出
+                PlayerVo topPlayer = players.stream()
+                    .filter(p -> Long.valueOf(finalRank+1).equals(p.getRank()))
+                    .findAny()
+                    .orElse(null);
+                if(topPlayer!=null){
+                    //把这一名的玩家信息  存入领取奖励的表中  并且发送消息给对应用户
                     UserVo userVo = iUserService.queryById(topPlayer.getId());
                     RewardClaims rewardClaims=new RewardClaims();
                     rewardClaims.setTournamentId(tournamentId);
                     rewardClaims.setPlayerId(topPlayer.getId());
-                    rewardClaims.setRank(topPlayer.getRank());
-                    rewardClaims.setPlayerName(userVo.getNickName());
+                    //原始排名-1
+                    rewardClaims.setRank(topPlayer.getRank()-1L);
                     rewardClaims.setClaimed(0L);
                     if(userVo!=null){
+                        rewardClaims.setPlayerName(userVo.getNickName());
                         rewardClaims.setPhone(userVo.getPhone());
                     }
                     rewardClaims.setIsDelete(false);
-                    rewardClaims.setRewardJson(itemsPrizeListString);
+                    //奖励信息去对应map中拿原始奖励信息
+                    // 所有排名 对应的奖励信息
+                    String rewardJson=null;
+                    for (HashMap<Long, Object> rewardJsonMap : rewardJsonList) {
+                        for (Map.Entry<Long, Object> entry : rewardJsonMap.entrySet()) {
+                            Long rank = entry.getKey();
+                            if(Objects.equals(rewardClaims.getRank(), rank)){
+                                rewardJson = (String) entry.getValue();
+                                rewardClaims.setRewardJson(rewardJson);
+                                break;
+                            }
+                        }
+                    }
                     baseMapper.insert(rewardClaims);
+                    //todo 发送通知
+                    //查询赛事基本信息 名称
+                    TournamentsVo tournamentsVo = tournamentsService.queryById(tournamentId);
+                    //查询消息模版信息
+                    MessagesTemplateVo msgTemplateByCode = messagesMapper.selectMsgTemplateByCode("MATCH_WIN");
+                    //解析奖励信息
+                    List<Map<String, Object>> listReward = objectMapper.readValue(
+                        rewardJson,
+                        new TypeReference<List<Map<String, Object>>>() {}
+                    );
+
+                    List<String> rewardItems = new ArrayList<>();
+
+                    for (Map<String, Object> item : listReward) {
+                        Integer itemId = (Integer) item.get("itemId");
+                        Integer quantity = (Integer) item.get("quantity");
+
+                        if (itemId != null && quantity != null) {
+                            ItemsVo itemsVo = itemsMapper.selectVoByIdInfo(itemId.longValue());
+                            if (itemsVo != null) {
+                                String itemName = itemsVo.getName();
+                                rewardItems.add(itemName + "×" + quantity);
+                            }
+                        }
+                    }
+
+                    // 用逗号连接,自动处理:0个、1个、多个的情况
+                    String rewardContent = String.join(", ", rewardItems);
+
+
+                    Map<String, String> params = new HashMap<>();
+                    params.put("matchName", tournamentsVo.getName());
+                    params.put("finalRank", String.valueOf(rewardClaims.getRank()));
+                    params.put("rewardContent", rewardContent);
+                    params.put("phone", null);
+
+                    String content = renderTemplate(msgTemplateByCode.getContentTemplate(), params);
+                    String titles=renderTemplate(msgTemplateByCode.getTitleTemplate(), params);
+
+                    //增加消息到对应表 发送人 接收人
+                    MessageReceivers messageReceivers=new MessageReceivers();
+                    messageReceivers.setTitle(titles);
+                    messageReceivers.setTargetType("SINGLE");
+                    messageReceivers.setContent(content);
+                    messageReceivers.setRewardId(rewardClaims.getId());
+                    messageReceivers.setTemplateCode(msgTemplateByCode.getTemplateCode());
+                    messageReceivers.setMessageType("REWARD");
+                    messageReceivers.setSendTime(new Date());
+                    messageReceivers.setCreatedAt(new Date());
+                    messageReceivers.setSendName(msgTemplateByCode.getSendFrom());
+                    messageReceivers.setUserId(userVo.getId());
+                    messageReceiversMapper.insertMessageReceiver(messageReceivers);
+                }
+
+                //todo 在原始排名信息中redis 里面也要删除对应信息
+                //被移除的玩家id信息
+                Long removePlayerId = rewardClaimsVo.getPlayerId();
+                //移除排名信息 和 更新排名最新信息
+                //都是redis中的原始排名
+                Iterator<PlayerVo> iterator = players.iterator();
+                Long removedRank = null;
+
+                // Step 1: 移除指定玩家,并记录其原排名
+                while (iterator.hasNext()) {
+                    PlayerVo player = iterator.next();
+                    if (Objects.equals(player.getId(), removePlayerId)) {
+                        removedRank = player.getRank(); // 记录被移除玩家的 rank
+                        iterator.remove();
+                        System.out.println("已移除玩家: " + player.getName() + ", 原排名: " + removedRank);
+                        break;
+                    }
+                }
+
+                // Step 2: 如果成功移除,则更新所有排名 > removedRank 的玩家
+                if (removedRank != null) {
+                    for (PlayerVo player : players) {
+                        if (player.getRank() > removedRank) {
+                            player.setRank(player.getRank() - 1); // 排名前移
+                        }
+                    }
+                    System.out.println("后续玩家排名已前移");
+                } else {
+                    System.out.println("未找到要移除的玩家");
                 }
 
+               // Step 3: 将更新后的【所有剩余玩家】转换为 PlayerRankVo 列表(用于返回或写回 Redis)
+                List<PlayerRankVo> rankVoList = new ArrayList<>();
+                for (PlayerVo player : players) {
+                    PlayerRankVo vo = new PlayerRankVo();
+                    // 注意:是 copyProperties(目标, 源) 还是 (源, 目标)?取决于 BeanUtils 实现
+                    // 常见的是 BeanUtils.copyProperties(目标对象, 源对象)
+                    org.springframework.beans.BeanUtils.copyProperties(player, vo); // Spring 版本
+                    // 或 Apache Commons: BeanUtils.copyProperties(vo, player);
+                    UserVo userVo = iUserService.queryById(player.getId());
+                    if(userVo!=null){
+                        vo.setName(userVo.getNickName());
+                    }
+                    rankVoList.add(vo);
+                }
+
+                // ✅ 现在 rankVoList 包含了更新后的完整排行榜(除了被删除的)
+                //调整完之后 进行删除排名key  在重新set进去
+                redisUtil.delete(RedisKeys.tournamentLeaderboard(tournamentId));
+
+                //重新设置进去
+                redisUtil.set(RedisKeys.tournamentLeaderboard(tournamentId), rankVoList);
+
                 logger.info("Reward claim successfully deleted. ID: {}, Tournament ID: {}", bo.getId(), bo.getTournamentId());
             } else {
                 logger.warn("Failed to delete reward claim. ID: {}, Tournament ID: {}", bo.getId(), bo.getTournamentId());
@@ -319,52 +500,28 @@ public class RewardClaimsServiceImpl implements IRewardClaimsService {
         }
     }
 
-        /*//移除领奖排名
-        int rewardClaimed = baseMapper.delUserRewardClaimed(bo.getId());
-        //如果是已经领取的就进行退还操作
-        if(rewardClaimsVo.getClaimed()==1){
-            String rewardJson = rewardClaimsVo.getRewardJson();
-            try {
-                List<Map<String, Object>> list = objectMapper.readValue(
-                    rewardJson,
-                    new TypeReference<List<Map<String, Object>>>() {}
-                );
-                for (Map<String, Object> item : list) {
-                    Integer itemId = (Integer) item.get("itemId");
-                    Integer quantity = (Integer) item.get("quantity");
-
-                    if (itemId == null || quantity == null) {
-                        throw new IllegalArgumentException("道具ID或数量不能为空");
-                    }
-
-                    Long playerId = bo.getPlayerId();
 
-                    PlayerItems playerItem = new PlayerItems();
-                    playerItem.setPlayerId(playerId);
-                    playerItem.setItemId(itemId.longValue());
-                    //本次需要扣减的数量
-                    playerItem.setQuantity(quantity.longValue());
 
-                    // 查询玩家当前道具信息
-                    PlayerItemsVo playerItemsVos = playerItemsMapper.selectPlayerItems(playerItem);
-                    //当前的数量
-                    Long playerItemsVosQuantity = playerItemsVos.getQuantity();
-                    //剩下的数量
-                    long finalQuantity = playerItemsVosQuantity - quantity.longValue();
-                    //更新
-                    playerItem.setQuantity(finalQuantity);
-                    playerItemsMapper.updatePlayerItems(playerItem);
-                }
-                //退还之后,下一位顶上
-                //TODO 奖励如何发放 还是按json的吗   需要记录日志到道具日志列表   替补归为第一,第二名奖励如果发了全部撤回 重新发第一名的给他  查询这场赛事的排名列表
 
+    private String getRewardByOriginalRank(Long targetRank, List<RewardClaimsVo> list) {
+        return list.stream()
+            .filter(vo -> vo.getRank().equals(targetRank))
+            .map(RewardClaimsVo::getRewardJson)
+            .findFirst()
+            .orElse(""); // 默认奖励(可自定义)
+    }
 
 
+    public static String renderTemplate(String template, Map<String, String> placeholders) {
+        String result = template;
+        for (Map.Entry<String, String> entry : placeholders.entrySet()) {
+            String key = "[" + entry.getKey() + "]";  // 如 [matchName]
+            String value = entry.getValue() != null ? entry.getValue() : "";
+            result = result.replace(key, value);
+        }
+        return result;
+    }
 
-            }catch (Exception e){
-                e.printStackTrace();
-            }
-        }*/
     /**
      * 审核并发放奖励
      */
@@ -472,6 +629,7 @@ public class RewardClaimsServiceImpl implements IRewardClaimsService {
                         // TODO app收到消息  用户提交领取状态   审核  发放      修改消息状态表的领取状态
                         userMessageStatusMapper.updateUserMessageClaimed(bo.getId());
 
+
                         // TODO app收到消息  用户提交领取状态   审核  发放
                         // 6. 更新用户消息状态
                     /*int messageUpdateCount = userMessageStatusMapper.updateUserMessageClaimed(bo.getId());
@@ -483,7 +641,7 @@ public class RewardClaimsServiceImpl implements IRewardClaimsService {
                 } finally {
                     ItemOperationLock.releaseLock(bo.getPlayerId()); // 确保释放锁
                 }
-
+                redisUtil.publish("channel:item_updates", rewardClaimsVo.getPlayerId());
                 return Boolean.TRUE;
             }else{
                 throw new RuntimeException("该奖励已处理,请勿重复操作");

+ 5 - 0
ruoyi-modules/ruoyi-system/src/main/resources/mapper/business/MessagesMapper.xml

@@ -97,5 +97,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
 
 
+    <select id="selectMsgTemplateByCode" resultType="org.dromara.business.domain.vo.MessagesTemplateVo">
+       select * from  message_template where template_code=#{templateCode} limit 1
+    </select>
+
+
 
 </mapper>

+ 11 - 0
ruoyi-modules/ruoyi-system/src/main/resources/mapper/business/RewardClaimsLogMapper.xml

@@ -22,6 +22,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         ${ew.customSqlSegment}
     </select>
 
+    <!-- 条件查询(支持字段为空判断) -->
+    <select id="selectRewardClaimsLogInfoByCondition" resultType="org.dromara.business.domain.vo.RewardClaimsLogVo">
+        SELECT *
+        FROM reward_claims_log
+        where  1=1
+        <if test="tournamentId != null">and tournament_id = #{tournamentId}</if>
+        order by 'rank' asc
+    </select>
+
     <!-- 更新语句:只更新非空字段 -->
     <update id="updateRewardClaimsLogById">
         UPDATE reward_claims_log
@@ -60,6 +69,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="rewardJson != null and rewardJson != ''">reward_json,</if>
             <if test="claimed != null">claimed,</if>
             <if test="isDelete != null">is_delete,</if>
+            <if test="oldRankJson != null">old_rank_json,</if>
             created_at,
             updated_at
         </trim>
@@ -72,6 +82,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="rewardJson != null and rewardJson != ''">#{rewardJson},</if>
             <if test="claimed != null">#{claimed},</if>
             <if test="isDelete != null">#{isDelete},</if>
+            <if test="oldRankJson != null">#{oldRankJson},</if>
             NOW(), NOW()
         </trim>
     </insert>

+ 4 - 6
ruoyi-modules/ruoyi-system/src/main/resources/mapper/business/RewardClaimsMapper.xml

@@ -58,7 +58,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 player_id = #{playerId},
             </if>
             <if test="rank != null">
-                rank = #{rank},
+                `rank` = #{rank},
             </if>
             <if test="playerName != null and playerName != ''">
                 player_name = #{playerName},
@@ -67,7 +67,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 phone = #{phone},
             </if>
             <if test="rewardJson != null">
-                reward_json = #{rewardJson, typeHandler=org.apache.ibatis.type.JdbcType.VARCHAR},
+                reward_json = #{rewardJson},
             </if>
             <if test="claimed != null">
                 claimed = #{claimed},
@@ -87,10 +87,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <!-- 条件查询 -->
     <select id="selectByCriteria" resultType="org.dromara.business.domain.vo.RewardClaimsVo">
         SELECT *
-        FROM reward_claims
-        <where>
-            <if test="tournamentId != null">AND tournament_id = #{tournamentId}</if>
-        </where>
+        FROM reward_claims where 1=1 AND is_delete=0
+         <if test="tournamentId != null">AND tournament_id = #{tournamentId}</if>
     </select>
 
     <!-- 根据主键删除 -->