浏览代码

feat(business): 解析牌局详情 JSON 数据

- 新增 parseActionsToChineseList 方法解析动作列表
- 添加 parseBlindsInfo 方法解析盲注信息
- 实现 PlayerInfo 内部类存储玩家信息
- 优化 parseBoardCards 和 parseHoleCardsV2 方法
- 更新 HandHistoryVo 和 MessageReceiversBo 相关字段
fugui001 4 月之前
父节点
当前提交
c46bd2815c

+ 1 - 2
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/domain/bo/MessageReceiversBo.java

@@ -15,9 +15,8 @@ import java.util.Date;
  * @date 2025-07-10
  */
 @Data
-@EqualsAndHashCode(callSuper = true)
 @AutoMapper(target = MessageReceivers.class, reverseConvertGenerate = false)
-public class MessageReceiversBo extends BaseEntity {
+public class MessageReceiversBo {
 
     /**
      *

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

@@ -102,5 +102,10 @@ public class HandHistoryVo implements Serializable {
      */
     private String publicBrand;
 
+    /**
+     * 盲注信息
+     */
+    private String parseBlindsInfo;
+
 
 }

+ 2 - 2
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/service/impl/HandHistoryServiceImpl.java

@@ -170,9 +170,9 @@ public class HandHistoryServiceImpl implements IHandHistoryService {
                         String result = parseBoardCards.stream().collect(Collectors.joining(" "));
                         handHistoryVo1.setPublicBrand(result);
                     }
-
+                   /* String parseBlindsInfo = ActionParserUtils.parseBlindsInfo(handDetails);
+                    handHistoryVo1.setParseBlindsInfo(parseBlindsInfo);*/
                     handHistoryVoList.add(handHistoryVo1);
-
                 }
 
                 // 调用分页方法

+ 0 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/service/impl/MessageReceiversServiceImpl.java

@@ -94,7 +94,6 @@ public class MessageReceiversServiceImpl implements IMessageReceiversService {
     }
 
     private LambdaQueryWrapper<MessageReceivers> buildQueryWrapper(MessageReceiversBo bo) {
-        Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<MessageReceivers> lqw = Wrappers.lambdaQuery();
         lqw.orderByAsc(MessageReceivers::getId);
         lqw.eq(bo.getTitle() != null, MessageReceivers::getTitle, bo.getTitle());

+ 168 - 14
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/utils/ActionParserUtils.java

@@ -3,14 +3,10 @@ package org.dromara.business.utils;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 public class ActionParserUtils {
 
-
     // 阶段映射
     private static final Map<String, String> STAGE_MAP = Map.of(
         "PREFLOP", "翻牌前",
@@ -25,30 +21,63 @@ public class ActionParserUtils {
         "CHECK", "过牌",
         "CALL", "跟注",
         "RAISE", "加注",
-        "ALL_IN", "全压"
+        "ALL_IN", "全下",
+        "BET", "下注"
     );
 
     /**
      * 将 JSON 中的 actions 解析为中文描述的列表
+     * 包括盲注、动作、摊牌信息(含底牌解析)
      */
     public static List<String> parseActionsToChineseList(String json) throws Exception {
         ObjectMapper mapper = new ObjectMapper();
         JsonNode rootNode = mapper.readTree(json);
 
-        // 构建 playerId -> name 映射
-        Map<Integer, String> playerMap = new HashMap<>();
+        // 提取盲注金额
+        JsonNode blindsNode = rootNode.get("blinds");
+        int sbAmount = blindsNode != null ? blindsNode.get("sb").asInt() : 0;
+        int bbAmount = blindsNode != null ? blindsNode.get("bb").asInt() : 0;
+        int anteAmount = blindsNode != null ? blindsNode.get("ante").asInt() : 0;
+
+        // 提取盲注座位号
+        int smallBlindSeat = rootNode.has("smallBlindSeat") ? rootNode.get("smallBlindSeat").asInt() : -1;
+        int bigBlindSeat = rootNode.has("bigBlindSeat") ? rootNode.get("bigBlindSeat").asInt() : -1;
+
+        // 构建 playerId -> 玩家信息(含姓名、座位、标签)
+        Map<Long, PlayerInfo> playerInfoMap = new HashMap<>();
+        String sbPlayerName = "未知";
+        String bbPlayerName = "未知";
+
         JsonNode playersNode = rootNode.get("players");
         if (playersNode != null && playersNode.isArray()) {
             for (JsonNode playerNode : playersNode) {
-                int id = playerNode.get("id").asInt();
+                long id = playerNode.get("id").asLong();
                 String name = playerNode.get("name").asText();
-                playerMap.put(id, name);
+                int seat = playerNode.get("seat").asInt();
+
+                List<String> tags = new ArrayList<>();
+                if (seat == smallBlindSeat) {
+                    tags.add("小盲");
+                    sbPlayerName = name;
+                }
+                if (seat == bigBlindSeat) {
+                    tags.add("大盲");
+                    bbPlayerName = name;
+                }
+
+                playerInfoMap.put(id, new PlayerInfo(name, seat, tags));
             }
         }
 
         List<String> chineseActions = new ArrayList<>();
-        JsonNode actionsNode = rootNode.get("actions");
 
+        // ✅ STEP 1: 插入盲注系统提示
+        chineseActions.add("[系统] ANTE:" + anteAmount);
+        chineseActions.add("[系统] 小盲(" + sbPlayerName + "):" + sbAmount);
+        chineseActions.add("[系统] 大盲(" + bbPlayerName + "):" + bbAmount);
+
+        // ✅ STEP 2: 添加原始动作
+        JsonNode actionsNode = rootNode.get("actions");
         if (actionsNode != null && actionsNode.isArray()) {
             for (JsonNode actionNode : actionsNode) {
                 String stageKey = actionNode.get("stage").asText();
@@ -57,21 +86,146 @@ public class ActionParserUtils {
                 String actionKey = actionNode.get("action").asText();
                 String action = ACTION_MAP.getOrDefault(actionKey, "未知操作");
 
-                int playerId = actionNode.get("playerId").asInt();
+                long playerId = actionNode.get("playerId").asLong();
                 int addAmount = actionNode.get("addAmount").asInt();
                 int seatNumber = actionNode.get("seatNumber").asInt();
-                String playerName = playerMap.getOrDefault(playerId, "未知玩家");
+
+                PlayerInfo playerInfo = playerInfoMap.get(playerId);
+                if (playerInfo == null) {
+                    playerInfo = new PlayerInfo("未知玩家", seatNumber, Collections.emptyList());
+                }
+
+                // 构建带标签的玩家名称
+                String playerNameWithTags = playerInfo.name;
+                if (!playerInfo.tags.isEmpty()) {
+                    playerNameWithTags += "(" + String.join(" + ", playerInfo.tags) + ")";
+                }
+
+                // 特殊处理:大盲在翻牌前免费过牌
+                boolean isPreflop = "PREFLOP".equals(stageKey);
+                boolean isBigBlind = playerInfo.tags.contains("大盲");
+                boolean isCallOrCheck = "CALL".equals(actionKey) || "CHECK".equals(actionKey);
+                boolean isBigBlindFreeAction = isPreflop && isBigBlind && (addAmount == 0 || addAmount == bbAmount);
+
+                String actionDetail = action;
+                if (isBigBlindFreeAction && "CALL".equals(actionKey)) {
+                    actionDetail = "跟注(大盲免费过牌)";
+                }
 
                 String desc = String.format("[%s] 玩家 %s(座位%d)%s,加注金额为 %d",
-                    stage, playerName, seatNumber, action, addAmount);
+                    stage, playerNameWithTags, seatNumber, actionDetail, addAmount);
 
                 chineseActions.add(desc);
             }
         }
 
+        // ✅ STEP 3: 添加 showdown 摊牌信息(使用 PokerCardParserUtils.parseHoleCardsV2)
+        JsonNode showdownNode = rootNode.get("showdown");
+        if (showdownNode != null && showdownNode.isArray() && showdownNode.size() > 0) {
+            Map<Long, String> holeCardsMap = new HashMap<>();
+            Map<Long, Integer> winAmountMap = new HashMap<>();
+
+            for (JsonNode sd : showdownNode) {
+                long playerId = -1;
+
+                // 优先使用 id
+                if (sd.has("id")) {
+                    playerId = sd.get("id").asLong();
+                } else {
+                    // 回退:通过 seatId 匹配
+                    int seatId = sd.get("seatId").asInt();
+                    for (Map.Entry<Long, PlayerInfo> entry : playerInfoMap.entrySet()) {
+                        if (entry.getValue().seat == seatId) {
+                            playerId = entry.getKey();
+                            break;
+                        }
+                    }
+                }
+
+                String holeCardsRaw = sd.has("holeCards") ? sd.get("holeCards").asText().trim() : "";
+                // ✅ 调用你已有的工具方法解析底牌
+                List<String> holeCardsList = PokerCardParserUtils.parseHoleCardsV2(holeCardsRaw);
+                String holeCardsFormatted = String.join(", ", holeCardsList);
+
+                int winAmount = sd.has("winAmount") ? sd.get("winAmount").asInt() : 0;
+
+                holeCardsMap.put(playerId, holeCardsFormatted);
+                if (winAmount > 0) {
+                    winAmountMap.put(playerId, winAmount);
+                }
+            }
+
+            // 生成摊牌描述
+            for (Map.Entry<Long, String> entry : holeCardsMap.entrySet()) {
+                long playerId = entry.getKey();
+                String formattedHoleCards = entry.getValue();
+                PlayerInfo playerInfo = playerInfoMap.get(playerId);
+                if (playerInfo == null) continue;
+
+                String playerNameWithTags = playerInfo.name;
+                if (!playerInfo.tags.isEmpty()) {
+                    playerNameWithTags += "(" + String.join(" + ", playerInfo.tags) + ")";
+                }
+
+                int winAmount = winAmountMap.getOrDefault(playerId, 0);
+                String winText = winAmount > 0 ? ",+ " + winAmount : "";
+
+                String showdownDesc = String.format("[摊牌] 玩家 %s(座位%d)摊牌,底牌:%s%s",
+                    playerNameWithTags, playerInfo.seat, formattedHoleCards, winText);
+
+                chineseActions.add(showdownDesc);
+            }
+        }
+
         return chineseActions;
     }
 
+    // 内部类:存储玩家信息(名称、座位、标签)
+    private static class PlayerInfo {
+        String name;
+        int seat;
+        List<String> tags;
+
+        public PlayerInfo(String name, int seat, List<String> tags) {
+            this.name = name;
+            this.seat = seat;
+            this.tags = new ArrayList<>(tags);
+        }
+    }
+
+    /**
+     * 解析盲注信息字符串:ante + 小盲(玩家) + 大盲(玩家)
+     */
+    public static String parseBlindsInfo(String json) throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        JsonNode rootNode = mapper.readTree(json);
+
+        JsonNode blindsNode = rootNode.get("blinds");
+        if (blindsNode == null) return "未知盲注信息";
+
+        int anteAmount = blindsNode.get("ante").asInt();
+        int sbAmount = blindsNode.get("sb").asInt();
+        int bbAmount = blindsNode.get("bb").asInt();
+
+        int smallBlindSeat = rootNode.get("smallBlindSeat").asInt();
+        int bigBlindSeat = rootNode.get("bigBlindSeat").asInt();
+
+        String sbPlayerName = "未知";
+        String bbPlayerName = "未知";
+
+        JsonNode playersNode = rootNode.get("players");
+        if (playersNode != null && playersNode.isArray()) {
+            for (JsonNode player : playersNode) {
+                int seat = player.get("seat").asInt();
+                String name = player.get("name").asText();
+                if (seat == smallBlindSeat) sbPlayerName = name;
+                if (seat == bigBlindSeat) bbPlayerName = name;
+            }
+        }
+
+        return String.format("ANTE(%d) + 小盲(%s) %d + 大盲(%s) %d",
+            anteAmount, sbPlayerName, sbAmount, bbPlayerName, bbAmount);
+    }
 
 
 }

文件差异内容过多而无法显示
+ 121 - 9
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/business/utils/PokerCardParserUtils.java


部分文件因为文件数量过多而无法显示