package com.xy.xyaicpzs.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xy.xyaicpzs.domain.entity.*;
import com.xy.xyaicpzs.mapper.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

import com.xy.xyaicpzs.domain.vo.BallHitRateVO;
import com.xy.xyaicpzs.domain.vo.PrizeEstimateVO;
import com.xy.xyaicpzs.domain.vo.RedBallHitRateVO;
import com.xy.xyaicpzs.domain.vo.BallAnalysisResultVO;
import com.xy.xyaicpzs.domain.vo.FollowBallAnalysisResultVO;
import com.xy.xyaicpzs.domain.vo.BlueBallAnalysisResultVO;

/**
 * 球号分析服务类
 * 实现复杂的球号分析算法
 */
@Slf4j
@Service
public class BallAnalysisService {

    /**
     * 球号和系数的对应关系
     */
    public static class BallWithCoefficient {
        private Integer ballNumber;
        private Double coefficient;
        private Integer masterBallNumber; // 主球号，用于标识来源
        
        public BallWithCoefficient(Integer ballNumber, Double coefficient, Integer masterBallNumber) {
            this.ballNumber = ballNumber;
            this.coefficient = coefficient;
            this.masterBallNumber = masterBallNumber;
        }
        
        public Integer getBallNumber() { return ballNumber; }
        public Double getCoefficient() { return coefficient; }
        public Integer getMasterBallNumber() { return masterBallNumber; }
        
        @Override
        public String toString() {
            return String.format("球号:%d,系数:%.4f,主球:%d", ballNumber, coefficient, masterBallNumber);
        }
    }

    @Autowired
    private T3Mapper t3Mapper;

    @Autowired
    private T4Mapper t4Mapper;

    @Autowired
    private HistoryTopMapper historyTopMapper;

    @Autowired
    private HistoryTop100Mapper historyTop100Mapper;

    @Autowired
    private T7Mapper t7Mapper;

    @Autowired
    private T5Mapper t5Mapper;

    @Autowired
    private T6Mapper t6Mapper;

    @Autowired
    private T8Mapper t8Mapper;

    @Autowired
    private BlueHistoryTop100Mapper blueHistoryTop100Mapper;

    @Autowired
    private BlueHistoryTopMapper blueHistoryTopMapper;

    @Autowired
    private PredictRecordMapper predictRecordMapper;

    @Autowired
    private LotteryDrawsService lotteryDrawsService;

    /**
     * 辅助：查询给定主球在T7表中指定从球集合的面系数，返回BallWithCoefficient列表
     */
    private List<BallWithCoefficient> getT7CoefficientsFor(Integer masterBallNumber, List<Integer> slaveBallNumbers) {
        if (slaveBallNumbers == null || slaveBallNumbers.isEmpty()) {
            return new ArrayList<>();
        }
        QueryWrapper<T7> qw = new QueryWrapper<>();
        qw.eq("masterBallNumber", masterBallNumber)
          .in("slaveBallNumber", slaveBallNumbers);
        List<T7> rows = t7Mapper.selectList(qw);
        // 用球号->系数映射，避免顺序问题
        Map<Integer, Double> num2Coeff = rows.stream()
            .collect(Collectors.toMap(T7::getSlaveBallNumber, T7::getFaceCoefficient, (a,b)->a));
        List<BallWithCoefficient> result = new ArrayList<>();
        for (Integer num : slaveBallNumbers) {
            Double coeff = num2Coeff.get(num);
            if (coeff != null) {
                result.add(new BallWithCoefficient(num, coeff, masterBallNumber));
            }
        }
        return result;
    }

    /**
     * 辅助：查询给定主球在T8表中指定从球集合的面系数，返回BallWithCoefficient列表
     */
    private List<BallWithCoefficient> getT8CoefficientsFor(Integer masterBallNumber, List<Integer> slaveBallNumbers) {
        if (slaveBallNumbers == null || slaveBallNumbers.isEmpty()) {
            return new ArrayList<>();
        }
        QueryWrapper<T8> qw = new QueryWrapper<>();
        qw.eq("masterBallNumber", masterBallNumber)
          .in("slaveBallNumber", slaveBallNumbers);
        List<T8> rows = t8Mapper.selectList(qw);
        Map<Integer, Double> num2Coeff = rows.stream()
            .collect(Collectors.toMap(T8::getSlaveBallNumber, T8::getFaceCoefficient, (a,b)->a));
        List<BallWithCoefficient> result = new ArrayList<>();
        for (Integer num : slaveBallNumbers) {
            Double coeff = num2Coeff.get(num);
            if (coeff != null) {
                result.add(new BallWithCoefficient(num, coeff, masterBallNumber));
            }
        }
        return result;
    }

    /**
     * 球号分析算法主方法
     * @param level 高位/中位/低位标识 (H/M/L)
     * @param redBalls 6个红球号码
     * @param blueBall 1个蓝球号码
     * @return 分析结果：出现频率最高的前11位数字
     */
    public List<Integer> analyzeBalls(String level, List<Integer> redBalls, Integer blueBall) {
        log.info("开始球号分析算法，级别：{}，红球：{}，蓝球：{}", level, redBalls, blueBall);
        
        // 验证输入参数
        if (redBalls == null || redBalls.size() != 6) {
            throw new IllegalArgumentException("红球数量必须为6个");
        }
        if (blueBall == null) {
            throw new IllegalArgumentException("蓝球不能为空");
        }
        if (!Arrays.asList("H", "M", "L").contains(level)) {
            throw new IllegalArgumentException("级别必须为H、M或L");
        }

        List<Integer> allNumbers = new ArrayList<>();
        // 第一步：记录球号和系数的对应关系
        List<BallWithCoefficient> ballsWithCoefficients = new ArrayList<>();

        // 第一步：处理6个红球，每个红球获取17个数字
        log.info("第一步：处理6个红球，使用{}级别算法", level);
        for (int i = 0; i < redBalls.size(); i++) {
            Integer redBall = redBalls.get(i);
            log.info("处理第{}个红球：{}", i + 1, redBall);
            
            List<BallWithCoefficient> ballsWithCoeffs = getTop17FromT3WithCoefficients(redBall, level);
            ballsWithCoefficients.addAll(ballsWithCoeffs);
            
            List<Integer> ballNumbers = ballsWithCoeffs.stream()
                    .map(BallWithCoefficient::getBallNumber)
                    .collect(Collectors.toList());
            allNumbers.addAll(ballNumbers);
            
            log.info("红球{}获取到{}个数字：{}", redBall, ballNumbers.size(), ballNumbers);
        }
        
        // 记录第一步的球号和系数统计
        log.info("=== 第一步球号和系数统计（共{}个） ===", ballsWithCoefficients.size());
        for (int i = 0; i < ballsWithCoefficients.size(); i++) {
            BallWithCoefficient ballWithCoeff = ballsWithCoefficients.get(i);
            log.info("第{}个：{}", i + 1, ballWithCoeff.toString());
        }
        log.info("=== 第一步统计结束 ===");

        // 第二步：从history_top获取前3个球号
        log.info("第二步：从history_top获取前3个球号");
        List<Integer> top3Numbers = getTop3FromHistoryTop();
        allNumbers.addAll(top3Numbers);
        log.info("从history_top获取到{}个数字：{}", top3Numbers.size(), top3Numbers);

        // 第三步：用蓝球从t4表获取17个数字
        log.info("第三步：用蓝球{}从t4表获取17个数字，使用{}级别算法", blueBall, level);
        List<Integer> blueNumbers = getTop17FromT4(blueBall, level);
        allNumbers.addAll(blueNumbers);
        log.info("蓝球{}获取到{}个数字：{}", blueBall, blueNumbers.size(), blueNumbers);

        log.info("总共收集到{}个数字", allNumbers.size());

        // 第四步：统计频率并获取前11个
        List<Integer> result = getTop11ByFrequency(allNumbers, ballsWithCoefficients);
        
        log.info("球号分析算法完成，结果：{}", result);
        return result;
    }

    /**
     * 球号分析算法（包含筛选过程说明）
     * @param level 高位/中位/低位标识 (H/M/L)
     * @param redBalls 6个红球号码
     * @param blueBall 蓝球号码
     * @return 分析结果：出现频率最高的前11位数字及筛选过程说明
     */
    public BallAnalysisResultVO analyzeBallsWithProcess(String level, List<Integer> redBalls, Integer blueBall) {
        log.info("开始球号分析算法（含过程说明），级别：{}，红球：{}，蓝球：{}", level, redBalls, blueBall);
        
        // 验证输入参数
        if (redBalls == null || redBalls.size() != 6) {
            throw new IllegalArgumentException("红球数量必须为6个");
        }
        if (blueBall == null) {
            throw new IllegalArgumentException("蓝球不能为空");
        }
        if (!Arrays.asList("H", "M", "L").contains(level)) {
            throw new IllegalArgumentException("级别必须为H、M或L");
        }

        List<Integer> allNumbers = new ArrayList<>();
        // 第一步：记录球号和系数的对应关系
        List<BallWithCoefficient> ballsWithCoefficients = new ArrayList<>();

        // 第一步：处理6个红球，每个红球获取17个数字
        log.info("第一步：处理6个红球，使用{}级别算法", level);
        for (int i = 0; i < redBalls.size(); i++) {
            Integer redBall = redBalls.get(i);
            log.info("处理第{}个红球：{}", i + 1, redBall);
            
            List<BallWithCoefficient> ballsWithCoeffs = getTop17FromT3WithCoefficients(redBall, level);
            ballsWithCoefficients.addAll(ballsWithCoeffs);
            
            List<Integer> ballNumbers = ballsWithCoeffs.stream()
                    .map(BallWithCoefficient::getBallNumber)
                    .collect(Collectors.toList());
            allNumbers.addAll(ballNumbers);
            
            log.info("红球{}获取到{}个数字：{}", redBall, ballNumbers.size(), ballNumbers);
        }
        
        // 记录第一步的球号和系数统计
        log.info("=== 第一步球号和系数统计（共{}个） ===", ballsWithCoefficients.size());
        for (int i = 0; i < ballsWithCoefficients.size(); i++) {
            BallWithCoefficient ballWithCoeff = ballsWithCoefficients.get(i);
            log.info("第{}个：{}", i + 1, ballWithCoeff.toString());
        }
        log.info("=== 第一步统计结束 ===");

        // 第二步：从history_top获取前3个球号
        log.info("第二步：从history_top获取前3个球号");
        List<Integer> top3Numbers = getTop3FromHistoryTop();
        allNumbers.addAll(top3Numbers);
        log.info("从history_top获取到{}个数字：{}", top3Numbers.size(), top3Numbers);

        // 第三步：用蓝球从t4表获取17个数字
        log.info("第三步：用蓝球{}从t4表获取17个数字，使用{}级别算法", blueBall, level);
        List<Integer> blueNumbers = getTop17FromT4(blueBall, level);
        allNumbers.addAll(blueNumbers);
        log.info("蓝球{}获取到{}个数字：{}", blueBall, blueNumbers.size(), blueNumbers);

        log.info("总共收集到{}个数字", allNumbers.size());

        // 第四步：统计频率并获取前11个（包含过程说明）
        BallAnalysisResultVO result = getTop11ByFrequencyWithProcess(allNumbers, ballsWithCoefficients);
        
        log.info("球号分析算法完成，结果：{}", result.getResult());
        return result;
    }

    /**
     * 从T3表获取指定主球的17个从球号及其系数
     * 根据不同级别使用不同的选择策略
     * @param masterBallNumber 主球号
     * @param level 级别：H-高位，M-中位，L-低位
     * @return 球号和系数的对应关系列表
     */
    private List<BallWithCoefficient> getTop17FromT3WithCoefficients(Integer masterBallNumber, String level) {
        log.debug("从T3表查询主球{}的线系数数据（含系数），级别：{}", masterBallNumber, level);
        
        // 查询指定主球的所有数据，按线系数降序排列
        QueryWrapper<T3> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("masterBallNumber", masterBallNumber)
                   .orderByDesc("lineCoefficient")
                   .orderByAsc("slaveBallNumber"); // 线系数相同时按从球号升序
        
        List<T3> t3List = t3Mapper.selectList(queryWrapper);
        
        if (t3List.isEmpty()) {
            log.warn("T3表中主球{}没有数据", masterBallNumber);
            return new ArrayList<>();
        }

        List<BallWithCoefficient> result = new ArrayList<>();
        
        switch (level) {
            case "H":
                // 高位：取前17个（按线系数从大到小排列）
                result = getHighLevelBallsWithCoefficients(t3List, masterBallNumber);
                break;
            case "M":
                // 中位：取线系数平均值向上9个球（含），向下8个球，共17个
                result = getMiddleLevelBallsWithCoefficients(t3List, masterBallNumber);
                break;
            case "L":
                // 低位：取最小值向上17个球，含最小值
                result = getLowLevelBallsWithCoefficients(t3List, masterBallNumber);
                break;
        }

        log.debug("T3表主球{}{}级别最终选择的{}个从球（含系数）：{}", masterBallNumber, level, result.size(), result);
        return result;
    }

    /**
     * 从T3表获取指定主球的17个从球号
     * 根据不同级别使用不同的选择策略
     * @param masterBallNumber 主球号
     * @param level 级别：H-高位，M-中位，L-低位
     */
    private List<Integer> getTop17FromT3(Integer masterBallNumber, String level) {
        log.debug("从T3表查询主球{}的线系数数据，级别：{}", masterBallNumber, level);
        
        // 查询指定主球的所有数据，按线系数降序排列
        QueryWrapper<T3> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("masterBallNumber", masterBallNumber)
                   .orderByDesc("lineCoefficient")
                   .orderByAsc("slaveBallNumber"); // 线系数相同时按从球号升序
        
        List<T3> t3List = t3Mapper.selectList(queryWrapper);
        
        if (t3List.isEmpty()) {
            log.warn("T3表中主球{}没有数据", masterBallNumber);
            return new ArrayList<>();
        }

        List<Integer> result = new ArrayList<>();
        
        switch (level) {
            case "H":
                // 高位：取前17个（按线系数从大到小排列）
                result = getHighLevelBalls(t3List);
                break;
            case "M":
                // 中位：取线系数平均值向上9个球（含），向下8个球，共17个
                result = getMiddleLevelBalls(t3List);
                break;
            case "L":
                // 低位：取最小值向上17个球，含最小值
                result = getLowLevelBalls(t3List);
                break;
        }

        log.debug("T3表主球{}{}级别最终选择的{}个从球：{}", masterBallNumber, level, result.size(), result);
        return result;
    }

    /**
     * 从T4表获取指定主球的17个从球号
     * 根据不同级别使用不同的选择策略
     * @param masterBallNumber 主球号
     * @param level 级别：H-高位，M-中位，L-低位
     */
    private List<Integer> getTop17FromT4(Integer masterBallNumber, String level) {
        log.debug("从T4表查询主球{}的线系数数据，级别：{}", masterBallNumber, level);
        
        // 查询指定主球的所有数据，按线系数降序排列
        QueryWrapper<T4> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("masterBallNumber", masterBallNumber)
                   .orderByDesc("lineCoefficient")
                   .orderByAsc("slaveBallNumber"); // 线系数相同时按从球号升序
        
        List<T4> t4List = t4Mapper.selectList(queryWrapper);
        
        if (t4List.isEmpty()) {
            log.warn("T4表中主球{}没有数据", masterBallNumber);
            return new ArrayList<>();
        }

        List<Integer> result = new ArrayList<>();
        
        switch (level) {
            case "H":
                // 高位：取前17个（按线系数从大到小排列）
                result = getHighLevelBallsFromT4(t4List);
                break;
            case "M":
                // 中位：取线系数平均值向上9个球（含），向下8个球，共17个
                result = getMiddleLevelBallsFromT4(t4List);
                break;
            case "L":
                // 低位：取最小值向上17个球，含最小值
                result = getLowLevelBallsFromT4(t4List);
                break;
        }

        log.debug("T4表主球{}{}级别最终选择的{}个从球：{}", masterBallNumber, level, result.size(), result);
        return result;
    }

    /**
     * 高位算法：从T3表取前17个（按线系数从大到小排列）
     */
    private List<Integer> getHighLevelBalls(List<T3> t3List) {
        if (t3List.size() < 17) {
            log.warn("T3表数据不足17条，实际{}条", t3List.size());
            return t3List.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 获取前16个
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < 16; i++) {
            result.add(t3List.get(i).getSlaveBallNumber());
        }

        // 处理第17个位置：检查是否有相同的线系数
        Double targetCoefficient = t3List.get(16).getLineCoefficient();
        List<T3> candidatesFor17th = new ArrayList<>();
        
        // 找出所有线系数等于第17个位置线系数的记录
        for (int i = 16; i < t3List.size(); i++) {
            if (t3List.get(i).getLineCoefficient().equals(targetCoefficient)) {
                candidatesFor17th.add(t3List.get(i));
            } else {
                break; // 线系数不同，停止查找
            }
        }

        if (candidatesFor17th.size() == 1) {
            // 只有一个候选，直接添加
            result.add(candidatesFor17th.get(0).getSlaveBallNumber());
        } else {
            // 有多个候选，通过history_top_100表的点系数排序
            log.debug("第17位有{}个相同线系数的候选：{}", candidatesFor17th.size(), 
                candidatesFor17th.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList()));
            
            Integer bestBall = selectBestBallFromHistoryTop100(
                candidatesFor17th.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList())
            );
            result.add(bestBall);
        }

        return result;
    }

    /**
     * 高位算法：从T3表取前17个（按线系数从大到小排列），包含系数信息
     */
    private List<BallWithCoefficient> getHighLevelBallsWithCoefficients(List<T3> t3List, Integer masterBallNumber) {
        if (t3List.size() < 17) {
            log.warn("T3表数据不足17条，实际{}条", t3List.size());
            return t3List.stream()
                    .map(t3 -> new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber))
                    .collect(Collectors.toList());
        }

        // 获取前17个
        List<BallWithCoefficient> result = new ArrayList<>();
        for (int i = 0; i < 17 && i < t3List.size(); i++) {
            T3 t3 = t3List.get(i);
            result.add(new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber));
        }

        // 处理第17个位置（系数最小的球号）的毛边情况
        if (result.size() == 17) {
            int lastIndex = 16; // 第17个位置的索引
            Double lastCoefficient = result.get(lastIndex).getCoefficient();
            
            // 查找第17个位置向下相同系数值的球号
            List<T3> candidatesFor17th = new ArrayList<>();
            
            // 添加第17个位置本身
            candidatesFor17th.add(t3List.get(16));
            
            // 向下查找相同系数值的球号
            for (int i = 17; i < t3List.size(); i++) {
                if (t3List.get(i).getLineCoefficient().equals(lastCoefficient)) {
                    candidatesFor17th.add(t3List.get(i));
                } else {
                    break; // 系数不同就停止查找
                }
            }
            
            if (candidatesFor17th.size() > 1) {
                log.debug("第17个位置及其向下有{}个相同线系数的候选：{}", candidatesFor17th.size(),
                    candidatesFor17th.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList()));
                
                Integer bestBall = selectBestBallWithFallback(
                    candidatesFor17th.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList())
                );
                
                // 找到对应的T3对象并替换第17个位置
                T3 selectedT3 = candidatesFor17th.stream()
                        .filter(t3 -> t3.getSlaveBallNumber().equals(bestBall))
                        .findFirst()
                        .orElse(candidatesFor17th.get(0));
                result.set(lastIndex, new BallWithCoefficient(selectedT3.getSlaveBallNumber(), selectedT3.getLineCoefficient(), masterBallNumber));
                
                log.debug("第17个位置毛边处理完成，最终选择：{}", bestBall);
            }
        }

        return result;
    }

    /**
     * 中位算法：从T3表取线系数平均值向上8个球，向下8个球，共17个
     */
    private List<Integer> getMiddleLevelBalls(List<T3> t3List) {
        if (t3List.size() < 17) {
            log.warn("T3表数据不足17条，实际{}条", t3List.size());
            return t3List.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 计算线系数平均值
        double avgCoefficient = t3List.stream()
                .mapToDouble(T3::getLineCoefficient)
                .average()
                .orElse(0.0);
        
        log.debug("T3表线系数平均值：{}", avgCoefficient);

        // 找到大于平均值且最接近的位置（如果有多个相同值，选择最后一个）
        int avgIndex = -1;
        double minDiff = Double.MAX_VALUE;
        for (int i = 0; i < t3List.size(); i++) {
            double coefficient = t3List.get(i).getLineCoefficient();
            // 只考虑大于平均值的值
            if (coefficient > avgCoefficient) {
                double diff = coefficient - avgCoefficient;
                if (diff <= minDiff) { // 使用 <= 来选择最后一个相同值
                minDiff = diff;
                avgIndex = i;
            }
        }
        }

        // 如果没有找到大于平均值的，则取最大值
        if (avgIndex == -1) {
            avgIndex = 0; // T3表按线系数降序排列，第一个就是最大值
            log.debug("未找到大于平均值的位置，使用最大值位置：{}，线系数：{}", avgIndex, t3List.get(avgIndex).getLineCoefficient());
        } else {
            log.debug("找到大于平均值且最接近的位置：{}，线系数：{}", avgIndex, t3List.get(avgIndex).getLineCoefficient());
        }

        // 向上8个，向下8个，共17个
        int startIndex = Math.max(0, avgIndex - 8);
        int endIndex = Math.min(t3List.size() - 1, avgIndex + 8);
        
        // 确保总共17个数字
        while (endIndex - startIndex + 1 < 17 && (startIndex > 0 || endIndex < t3List.size() - 1)) {
            if (startIndex > 0) {
                startIndex--;
            }
            if (endIndex < t3List.size() - 1 && endIndex - startIndex + 1 < 17) {
                endIndex++;
            }
        }

        List<Integer> result = new ArrayList<>();
        for (int i = startIndex; i <= endIndex && result.size() < 17; i++) {
            result.add(t3List.get(i).getSlaveBallNumber());
        }

        // 处理边界位置的相同线系数情况
        result = handleT3BoundaryConflicts(t3List, result, startIndex, endIndex);

        log.debug("中位算法选择范围：[{}, {}]，共{}个球", startIndex, endIndex, result.size());
        return result;
    }

    /**
     * 处理T3表边界位置的线系数冲突
     */
    private List<Integer> handleT3BoundaryConflicts(List<T3> t3List, List<Integer> candidates, int startIndex, int endIndex) {
        List<Integer> result = new ArrayList<>(candidates);
        if (result.size() >= 17) {
            // 检查第1个位置（最大线系数）的冲突
            Double firstCoefficient = t3List.get(startIndex).getLineCoefficient();
            List<T3> candidatesForFirst = new ArrayList<>();
            candidatesForFirst.add(t3List.get(startIndex)); // 边界本身
            for (int i = 0; i < startIndex; i++) {
                if (t3List.get(i).getLineCoefficient().equals(firstCoefficient)) {
                    candidatesForFirst.add(t3List.get(i));
                }
            }
            if (candidatesForFirst.size() > 1) {
                log.debug("第一位及其上面有{}个相同线系数的候选：{}", candidatesForFirst.size(),
                    candidatesForFirst.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList()));
                Integer bestBall = selectBestBallWithFallback(
                    candidatesForFirst.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList())
                );
                result.set(0, bestBall);
            }
            // 检查最后一个位置（最小线系数）的冲突
            Double lastCoefficient = t3List.get(endIndex).getLineCoefficient();
            List<T3> candidatesForLast = new ArrayList<>();
            candidatesForLast.add(t3List.get(endIndex)); // 边界本身
            for (int i = endIndex + 1; i < t3List.size(); i++) {
                if (t3List.get(i).getLineCoefficient().equals(lastCoefficient)) {
                    candidatesForLast.add(t3List.get(i));
                }
            }
            if (candidatesForLast.size() > 1) {
                log.debug("最后一位及其下面有{}个相同线系数的候选：{}", candidatesForLast.size(),
                    candidatesForLast.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList()));
                Integer bestBall = selectBestBallWithFallback(
                    candidatesForLast.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList())
                );
                result.set(result.size() - 1, bestBall);
            }
        }
        return result;
    }

    /**
     * 中位算法：从T3表取线系数平均值向上8个球，向下8个球，共17个，包含系数信息
     */
    private List<BallWithCoefficient> getMiddleLevelBallsWithCoefficients(List<T3> t3List, Integer masterBallNumber) {
        if (t3List.size() < 17) {
            log.warn("T3表数据不足17条，实际{}条", t3List.size());
            return t3List.stream()
                    .map(t3 -> new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber))
                    .collect(Collectors.toList());
        }

        // 计算线系数平均值
        double avgCoefficient = t3List.stream()
                .mapToDouble(T3::getLineCoefficient)
                .average()
                .orElse(0.0);
        
        log.debug("T3表线系数平均值：{}", avgCoefficient);

        // 找到大于平均值且最接近的位置（如果有多个相同值，选择最后一个）
        int avgIndex = -1;
        double minDiff = Double.MAX_VALUE;
        for (int i = 0; i < t3List.size(); i++) {
            double coefficient = t3List.get(i).getLineCoefficient();
            // 只考虑大于平均值的值
            if (coefficient > avgCoefficient) {
                double diff = coefficient - avgCoefficient;
                if (diff <= minDiff) { // 使用 <= 来选择最后一个相同值
                    minDiff = diff;
                    avgIndex = i;
                }
            }
        }

        // 如果没有找到大于平均值的，则取最大值
        if (avgIndex == -1) {
            avgIndex = 0; // T3表按线系数降序排列，第一个就是最大值
            log.debug("未找到大于平均值的位置，使用最大值位置：{}，线系数：{}", avgIndex, t3List.get(avgIndex).getLineCoefficient());
        } else {
            log.debug("找到大于平均值且最接近的位置：{}，线系数：{}", avgIndex, t3List.get(avgIndex).getLineCoefficient());
        }

        // 向上8个，向下8个，共17个
        int startIndex = Math.max(0, avgIndex - 8);
        int endIndex = Math.min(t3List.size() - 1, avgIndex + 8);
        
        // 确保总共17个数字
        while (endIndex - startIndex + 1 < 17 && (startIndex > 0 || endIndex < t3List.size() - 1)) {
            if (startIndex > 0) {
                startIndex--;
            }
            if (endIndex < t3List.size() - 1 && endIndex - startIndex + 1 < 17) {
                endIndex++;
            }
        }

        List<BallWithCoefficient> result = new ArrayList<>();
        for (int i = startIndex; i <= endIndex && result.size() < 17; i++) {
            T3 t3 = t3List.get(i);
            result.add(new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber));
        }

        // 处理边界毛边情况
        if (result.size() >= 8) {
            // 处理向上8个球号的第8个位置（对应区间起点 startIndex，即 result[0]）的毛边情况
            int upBoundaryIndex = 0; // 向上第8个球号在 result 中的索引
            if (upBoundaryIndex < result.size()) {
                Double upBoundaryCoefficient = result.get(upBoundaryIndex).getCoefficient();
                
                // 查找向上还有没有相同系数值的球号
                List<T3> upCandidates = new ArrayList<>();
                int actualUpIndex = startIndex + upBoundaryIndex; // 即 startIndex
                
                // 添加边界球号本身
                upCandidates.add(t3List.get(actualUpIndex));
                
                // 向上查找相同系数值的球号（在 startIndex 之上）
                for (int i = actualUpIndex - 1; i >= 0; i--) {
                    if (t3List.get(i).getLineCoefficient().equals(upBoundaryCoefficient)) {
                        upCandidates.add(t3List.get(i));
                    } else {
                        break; // 系数不同就停止查找
                    }
                }
                
                if (upCandidates.size() > 1) {
                    log.debug("向上第8个球号（startIndex）及其上面有{}个相同线系数的候选：{}", upCandidates.size(),
                        upCandidates.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList()));
                    
                    Integer bestBall = selectBestBallWithFallback(
                        upCandidates.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList())
                    );
                    
                    // 找到对应的T3对象并替换 result[0]
                    T3 selectedT3 = upCandidates.stream()
                            .filter(t3 -> t3.getSlaveBallNumber().equals(bestBall))
                            .findFirst()
                            .orElse(upCandidates.get(0));
                    result.set(upBoundaryIndex, new BallWithCoefficient(selectedT3.getSlaveBallNumber(), selectedT3.getLineCoefficient(), masterBallNumber));
                    
                    log.debug("向上第8个球号毛边处理完成，最终选择：{}", bestBall);
                }
            }
            
            // 处理向下8个球号的第8个位置（对应区间终点 endIndex，即 result[last]）的毛边情况
            int downBoundaryIndex = Math.min(16, result.size() - 1); // 向下第8个球号在 result 中的索引
            if (downBoundaryIndex >= 8 && downBoundaryIndex < result.size()) {
                Double downBoundaryCoefficient = result.get(downBoundaryIndex).getCoefficient();
                
                // 查找向下还有没有相同系数值的球号
                List<T3> downCandidates = new ArrayList<>();
                int actualDownIndex = startIndex + downBoundaryIndex; // 即 endIndex
                
                // 添加边界球号本身
                downCandidates.add(t3List.get(actualDownIndex));
                
                // 向下查找相同系数值的球号（在 endIndex 之下）
                for (int i = actualDownIndex + 1; i < t3List.size(); i++) {
                    if (t3List.get(i).getLineCoefficient().equals(downBoundaryCoefficient)) {
                        downCandidates.add(t3List.get(i));
                    } else {
                        break; // 系数不同就停止查找
                    }
                }
                
                if (downCandidates.size() > 1) {
                    log.debug("向下第8个球号（endIndex）及其下面有{}个相同线系数的候选：{}", downCandidates.size(),
                        downCandidates.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList()));
                    
                    Integer bestBall = selectBestBallWithFallback(
                        downCandidates.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList())
                    );
                    
                    // 找到对应的T3对象并替换 result[last]
                    T3 selectedT3 = downCandidates.stream()
                            .filter(t3 -> t3.getSlaveBallNumber().equals(bestBall))
                            .findFirst()
                            .orElse(downCandidates.get(0));
                    result.set(downBoundaryIndex, new BallWithCoefficient(selectedT3.getSlaveBallNumber(), selectedT3.getLineCoefficient(), masterBallNumber));
                    
                    log.debug("向下第8个球号毛边处理完成，最终选择：{}", bestBall);
                }
            }
        }

        log.debug("中位算法选择范围：[{}, {}]，共{}个球", startIndex, endIndex, result.size());
        return result;
    }

    /**
     * 低位算法：从T3表取最小值向上17个球，含最小值
     */
    private List<Integer> getLowLevelBalls(List<T3> t3List) {
        if (t3List.size() < 17) {
            log.warn("T3表数据不足17条，实际{}条", t3List.size());
            return t3List.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 从最后17个开始（最小的线系数）
        int startIndex = Math.max(0, t3List.size() - 17);
        List<Integer> candidates = new ArrayList<>();
        
        for (int i = startIndex; i < t3List.size(); i++) {
            candidates.add(t3List.get(i).getSlaveBallNumber());
        }

        // 处理第一个位置（最大线系数）的相同值情况
        if (candidates.size() >= 17) {
            Double firstCoefficient = t3List.get(startIndex).getLineCoefficient();
            List<T3> candidatesForFirst = new ArrayList<>();
            
            // 找出所有线系数等于第一个位置线系数的记录
            for (int i = 0; i < t3List.size(); i++) {
                if (t3List.get(i).getLineCoefficient().equals(firstCoefficient)) {
                    candidatesForFirst.add(t3List.get(i));
                }
            }

            if (candidatesForFirst.size() > 1) {
                // 有多个候选，通过带回退的多级筛选
                log.debug("第一位有{}个相同线系数的候选：{}", candidatesForFirst.size(), 
                    candidatesForFirst.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList()));
                
                Integer bestBall = selectBestBallWithFallback(
                    candidatesForFirst.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList())
                );
                
                // 替换第一个位置
                candidates.set(0, bestBall);
            }
        }

        log.debug("低位算法选择范围：[{}, {}]，共{}个球", startIndex, t3List.size() - 1, candidates.size());
        return candidates.subList(0, Math.min(17, candidates.size()));
    }

    /**
     * 低位算法：从T3表取最小值向上17个球，含最小值，包含系数信息
     */
    private List<BallWithCoefficient> getLowLevelBallsWithCoefficients(List<T3> t3List, Integer masterBallNumber) {
        if (t3List.size() < 17) {
            log.warn("T3表数据不足17条，实际{}条", t3List.size());
            return t3List.stream()
                    .map(t3 -> new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber))
                    .collect(Collectors.toList());
        }

        // 从最后17个开始（最小的线系数）
        int startIndex = Math.max(0, t3List.size() - 17);
        List<BallWithCoefficient> candidates = new ArrayList<>();
        
        for (int i = startIndex; i < t3List.size(); i++) {
            T3 t3 = t3List.get(i);
            candidates.add(new BallWithCoefficient(t3.getSlaveBallNumber(), t3.getLineCoefficient(), masterBallNumber));
        }

        // 处理第17个位置（系数最大的球号）的毛边情况
        if (candidates.size() == 17) {
            int lastIndex = 0; // 第17个位置的索引
            Double lastCoefficient = candidates.get(lastIndex).getCoefficient();
            int actualLastIndex = startIndex + lastIndex;
            
            // 查找第17个位置向上相同系数值的球号
            List<T3> candidatesFor17th = new ArrayList<>();
            
            // 添加第17个位置本身
            candidatesFor17th.add(t3List.get(actualLastIndex));
            
            // 向上查找相同系数值的球号
            for (int i = actualLastIndex - 1; i >= 0; i--) {
                if (t3List.get(i).getLineCoefficient().equals(lastCoefficient)) {
                    candidatesFor17th.add(t3List.get(i));
                } else {
                    break; // 系数不同就停止查找
                }
            }
            
            if (candidatesFor17th.size() > 1) {
                log.debug("第17个位置及其向上有{}个相同线系数的候选：{}", candidatesFor17th.size(),
                    candidatesFor17th.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList()));
                
                Integer bestBall = selectBestBallFromHistoryTop100(
                    candidatesFor17th.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList())
                );
                
                // 找到对应的T3对象并替换第17个位置
                T3 selectedT3 = candidatesFor17th.stream()
                        .filter(t3 -> t3.getSlaveBallNumber().equals(bestBall))
                        .findFirst()
                        .orElse(candidatesFor17th.get(0));
                candidates.set(lastIndex, new BallWithCoefficient(selectedT3.getSlaveBallNumber(), selectedT3.getLineCoefficient(), masterBallNumber));
                
                log.debug("第17个位置毛边处理完成，最终选择：{}", bestBall);
            }
        }

        log.debug("低位算法选择范围：[{}, {}]，共{}个球", startIndex, t3List.size() - 1, candidates.size());
        return candidates.subList(0, Math.min(17, candidates.size()));
    }

    /**
     * 高位算法：从T4表取前17个（按线系数从大到小排列）
     */
    private List<Integer> getHighLevelBallsFromT4(List<T4> t4List) {
        if (t4List.size() < 17) {
            log.warn("T4表数据不足17条，实际{}条", t4List.size());
            return t4List.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 获取前16个
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < 16; i++) {
            result.add(t4List.get(i).getSlaveBallNumber());
        }

        // 处理第17个位置：检查是否有相同的线系数
        Double targetCoefficient = t4List.get(16).getLineCoefficient();
        List<T4> candidatesFor17th = new ArrayList<>();
        
        // 找出所有线系数等于第17个位置线系数的记录
        for (int i = 16; i < t4List.size(); i++) {
            if (t4List.get(i).getLineCoefficient().equals(targetCoefficient)) {
                candidatesFor17th.add(t4List.get(i));
            } else {
                break; // 线系数不同，停止查找
            }
        }

        if (candidatesFor17th.size() == 1) {
            // 只有一个候选，直接添加
            result.add(candidatesFor17th.get(0).getSlaveBallNumber());
        } else {
            // 有多个候选，通过history_top_100表的点系数排序
            log.debug("第17位有{}个相同线系数的候选：{}", candidatesFor17th.size(), 
                candidatesFor17th.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList()));
            
            Integer bestBall = selectBestBallFromHistoryTop100(
                candidatesFor17th.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList())
            );
            result.add(bestBall);
        }

        return result;
    }

    /**
     * 中位算法：从T4表取线系数平均值向上8个球，向下8个球，共17个
     */
    private List<Integer> getMiddleLevelBallsFromT4(List<T4> t4List) {
        if (t4List.size() < 17) {
            log.warn("T4表数据不足17条，实际{}条", t4List.size());
            return t4List.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 计算线系数平均值
        double avgCoefficient = t4List.stream()
                .mapToDouble(T4::getLineCoefficient)
                .average()
                .orElse(0.0);
        
        log.debug("T4表线系数平均值：{}", avgCoefficient);

        // 找到大于平均值且最接近的位置（如果有多个相同值，选择最后一个）
        int avgIndex = -1;
        double minDiff = Double.MAX_VALUE;
        for (int i = 0; i < t4List.size(); i++) {
            double coefficient = t4List.get(i).getLineCoefficient();
            // 只考虑大于平均值的值
            if (coefficient > avgCoefficient) {
                double diff = coefficient - avgCoefficient;
                if (diff <= minDiff) { // 使用 <= 来选择最后一个相同值
                minDiff = diff;
                avgIndex = i;
            }
        }
        }

        // 如果没有找到大于平均值的，则取最大值
        if (avgIndex == -1) {
            avgIndex = 0; // T4表按线系数降序排列，第一个就是最大值
            log.debug("未找到大于平均值的位置，使用最大值位置：{}，线系数：{}", avgIndex, t4List.get(avgIndex).getLineCoefficient());
        } else {
            log.debug("找到大于平均值且最接近的位置：{}，线系数：{}", avgIndex, t4List.get(avgIndex).getLineCoefficient());
        }

        // 向上8个，向下8个，共17个
        int startIndex = Math.max(0, avgIndex - 8);
        int endIndex = Math.min(t4List.size() - 1, avgIndex + 8);
        
        // 确保总共17个数字
        while (endIndex - startIndex + 1 < 17 && (startIndex > 0 || endIndex < t4List.size() - 1)) {
            if (startIndex > 0) {
                startIndex--;
            }
            if (endIndex < t4List.size() - 1 && endIndex - startIndex + 1 < 17) {
                endIndex++;
            }
        }

        List<Integer> result = new ArrayList<>();
        for (int i = startIndex; i <= endIndex && result.size() < 17; i++) {
            result.add(t4List.get(i).getSlaveBallNumber());
        }

        // 处理边界位置的相同线系数情况
        result = handleT4BoundaryConflicts(t4List, result, startIndex, endIndex);

        log.debug("中位算法选择范围：[{}, {}]，共{}个球", startIndex, endIndex, result.size());
        return result;
    }

    /**
     * 处理T4表边界位置的线系数冲突
     */
    private List<Integer> handleT4BoundaryConflicts(List<T4> t4List, List<Integer> candidates, 
                                                   int startIndex, int endIndex) {
        List<Integer> result = new ArrayList<>(candidates);
        
        if (result.size() >= 17) {
            // 检查第1个位置（最大线系数）的冲突
            Double firstCoefficient = t4List.get(startIndex).getLineCoefficient();
            List<T4> candidatesForFirst = new ArrayList<>();
            
            // 先包含第一个位置本身
            candidatesForFirst.add(t4List.get(startIndex));

            // 找出第一个位置上面（如果存在）的所有相同线系数的记录
            for (int i = 0; i < startIndex; i++) {
                if (t4List.get(i).getLineCoefficient().equals(firstCoefficient)) {
                    candidatesForFirst.add(t4List.get(i));
                }
            }

            if (candidatesForFirst.size() > 1) {
                // 有多个候选，通过带回退的多级筛选
                log.debug("第一位上面及自身有{}个相同线系数的候选：{}", candidatesForFirst.size(), 
                    candidatesForFirst.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList()));
                
                Integer bestBall = selectBestBallWithFallback(
                    candidatesForFirst.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList())
                );
                
                // 替换第一个位置
                result.set(0, bestBall);
            }

            // 检查最后一个位置（最小线系数）的冲突
            Double lastCoefficient = t4List.get(endIndex).getLineCoefficient();
            List<T4> candidatesForLast = new ArrayList<>();
            
            // 先包含最后一个位置本身
            candidatesForLast.add(t4List.get(endIndex));

            // 找出最后一个位置下面（如果存在）的所有相同线系数的记录
            for (int i = endIndex + 1; i < t4List.size(); i++) {
                if (t4List.get(i).getLineCoefficient().equals(lastCoefficient)) {
                    candidatesForLast.add(t4List.get(i));
                }
            }

            if (candidatesForLast.size() > 1) {
                // 有多个候选，通过带回退的多级筛选
                log.debug("最后一位下面及自身有{}个相同线系数的候选：{}", candidatesForLast.size(), 
                    candidatesForLast.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList()));
                
                Integer bestBall = selectBestBallWithFallback(
                    candidatesForLast.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList())
                );
                
                // 替换最后一个位置
                result.set(result.size() - 1, bestBall);
            }
        }

        return result;
    }

    /**
     * 低位算法：从T4表取最小值向上17个球，含最小值，共17个球号
     */
    private List<Integer> getLowLevelBallsFromT4(List<T4> t4List) {
        if (t4List.size() < 17) {
            log.warn("T4表数据不足17条，实际{}条", t4List.size());
            return t4List.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 从最后17个开始（最小的线系数）
        int startIndex = Math.max(0, t4List.size() - 17);
        List<Integer> candidates = new ArrayList<>();
        
        for (int i = startIndex; i < t4List.size(); i++) {
            candidates.add(t4List.get(i).getSlaveBallNumber());
        }

        // 处理第17个位置（系数最大的球号）的毛边情况
        if (candidates.size() == 17) {
            int lastIndex = 0; // 第17个位置的索引
            Double lastCoefficient = t4List.get(lastIndex).getLineCoefficient();
            int actualLastIndex =  lastIndex;
            
            // 查找第17个位置向上相同系数值的球号
            List<T4> candidatesFor17th = new ArrayList<>();
            
            // 添加第17个位置本身
            candidatesFor17th.add(t4List.get(actualLastIndex));
            
            // 向上查找相同系数值的球号
            for (int i = actualLastIndex - 1; i >= 0; i--) {
                if (t4List.get(i).getLineCoefficient().equals(lastCoefficient)) {
                    candidatesFor17th.add(t4List.get(i));
                } else {
                    break; // 系数不同就停止查找
                }
            }
            
            if (candidatesFor17th.size() > 1) {
                log.debug("第17个位置及其向上有{}个相同线系数的候选：{}", candidatesFor17th.size(),
                    candidatesFor17th.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList()));
                
                Integer bestBall = selectBestBallWithFallback(
                    candidatesFor17th.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList())
                );
                
                // 替换第17个位置的球号
                candidates.set(lastIndex, bestBall);
                
                log.debug("第17个位置毛边处理完成，最终选择：{}", bestBall);
            }
        }

        log.debug("T4低位算法选择范围：[{}, {}]，共{}个球", startIndex, t4List.size() - 1, candidates.size());
        return candidates.subList(0, Math.min(17, candidates.size()));
    }

    /**
     * 处理T4表边界位置的线系数冲突（10个球版本）
     */
    private List<Integer> handleT4BoundaryConflictsFor10(List<T4> t4List, List<Integer> candidates, 
                                                        int startIndex, int endIndex) {
        List<Integer> result = new ArrayList<>(candidates);
        
        if (result.size() >= 10) {
            // 检查第1个位置（最大线系数）的冲突
            Double firstCoefficient = t4List.get(startIndex).getLineCoefficient();
            List<T4> candidatesForFirst = new ArrayList<>();
            
            // 找出第一个位置上面（如果存在）的所有相同线系数的记录
            for (int i = 0; i < startIndex; i++) {
                if (t4List.get(i).getLineCoefficient().equals(firstCoefficient)) {
                    candidatesForFirst.add(t4List.get(i));
                }
            }

            if (candidatesForFirst.size() > 0) {
                log.debug("第1位上面有{}个相同线系数的候选：{}", candidatesForFirst.size(), 
                    candidatesForFirst.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList()));
                
                Integer bestBall = selectBestBallWithFallback(
                    candidatesForFirst.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList())
                );
                result.set(0, bestBall);
            }

            // 检查第10个位置（最小线系数）的冲突
            Double lastCoefficient = t4List.get(endIndex).getLineCoefficient();
            List<T4> candidatesForLast = new ArrayList<>();
            
            // 找出第10个位置下面（如果存在）的所有相同线系数的记录
            for (int i = endIndex + 1; i < t4List.size(); i++) {
                if (t4List.get(i).getLineCoefficient().equals(lastCoefficient)) {
                    candidatesForLast.add(t4List.get(i));
                }
            }

            if (candidatesForLast.size() > 0) {
                log.debug("第10位下面有{}个相同线系数的候选：{}", candidatesForLast.size(), 
                    candidatesForLast.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList()));
                
                Integer bestBall = selectBestBallWithFallback(
                    candidatesForLast.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList())
                );
                result.set(result.size() - 1, bestBall);
            }
        }

        return result.subList(0, Math.min(10, result.size()));
    }

    /**
     * 从history_top表获取前3个球号（按点系数排行）
     * 如果点系数相同，通过history_top_100表比较
     */
    private List<Integer> getTop3FromHistoryTop() {
        log.debug("从history_top表获取前3个球号");
        
        // 查询前3个排行的数据
        QueryWrapper<HistoryTop> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc("no").last("LIMIT 3");
        
        List<HistoryTop> topList = historyTopMapper.selectList(queryWrapper);
        
        if (topList.size() < 3) {
            log.warn("history_top表数据不足3条，实际{}条", topList.size());
            return topList.stream().map(HistoryTop::getBallNumber).collect(Collectors.toList());
        }

        List<Integer> result = new ArrayList<>();
        
        // 处理每个位置，检查是否有相同点系数的情况
        for (int i = 0; i < 3; i++) {
            HistoryTop current = topList.get(i);
            
            // 查找所有具有相同排行的记录
            QueryWrapper<HistoryTop> sameRankQuery = new QueryWrapper<>();
            sameRankQuery.eq("no", current.getNo());
            List<HistoryTop> sameRankList = historyTopMapper.selectList(sameRankQuery);
            
            if (sameRankList.size() == 1) {
                // 只有一个，直接添加
                result.add(current.getBallNumber());
            } else {
                // 有多个相同排行，通过history_top_100表比较点系数
                log.debug("排行{}有{}个相同的球号：{}", current.getNo(), sameRankList.size(),
                    sameRankList.stream().map(HistoryTop::getBallNumber).collect(Collectors.toList()));
                
                Integer bestBall = selectBestBallWithFallback(
                    sameRankList.stream().map(HistoryTop::getBallNumber).collect(Collectors.toList())
                );
                result.add(bestBall);
            }
        }

        log.debug("history_top表最终选择的3个球号：{}", result);
        return result;
    }

    /**
     * 从候选球号中选择history_top_100表中点系数最大的球号
     */
    private Integer selectBestBallFromHistoryTop100(List<Integer> candidates) {
        if (candidates.isEmpty()) {
            throw new IllegalArgumentException("候选球号列表不能为空");
        }
        
        if (candidates.size() == 1) {
            return candidates.get(0);
        }

        log.debug("从history_top_100表中比较候选球号：{}", candidates);
        
        // 查询这些球号在history_top_100表中的点系数
        QueryWrapper<HistoryTop100> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("ballNumber", candidates)
                   .orderByDesc("pointCoefficient")
                   .orderByAsc("ballNumber"); // 点系数相同时按球号升序
        
        List<HistoryTop100> top100List = historyTop100Mapper.selectList(queryWrapper);
        
        if (top100List.isEmpty()) {
            log.warn("候选球号{}在history_top_100表中未找到数据，返回第一个", candidates);
            return candidates.get(0);
        }

        Integer bestBall = top100List.get(0).getBallNumber();
        log.debug("从候选{}中选择最佳球号：{}（点系数：{}）", candidates, bestBall, top100List.get(0).getPointCoefficient());
        
        return bestBall;
    }

    /**
     * 在候选球号中进行多级回退选择：
     * 1) 先用 history_top_100 的点系数最高者；
     * 2) 若全部不在 history_top_100，则用 history_top 的点系数最高者；
     * 3) 若仍无法区分，则随机选择一个。
     */
    private Integer selectBestBallWithFallback(List<Integer> candidates) {
        if (candidates == null || candidates.isEmpty()) {
            throw new IllegalArgumentException("候选球号列表不能为空");
        }

        if (candidates.size() == 1) {
            return candidates.get(0);
        }

        // 优先：history_top_100
        QueryWrapper<HistoryTop100> top100Query = new QueryWrapper<>();
        top100Query.in("ballNumber", candidates)
                   .orderByDesc("pointCoefficient")
                   .orderByAsc("ballNumber");
        List<HistoryTop100> top100List = historyTop100Mapper.selectList(top100Query);
        if (top100List != null && !top100List.isEmpty()) {
            return top100List.get(0).getBallNumber();
        }

        // 其次：history_top
        QueryWrapper<HistoryTop> topQuery = new QueryWrapper<>();
        topQuery.in("ballNumber", candidates)
                .orderByDesc("pointCoefficient")
                .orderByAsc("ballNumber");
        List<HistoryTop> topList = historyTopMapper.selectList(topQuery);
        if (topList != null && !topList.isEmpty()) {
            return topList.get(0).getBallNumber();
        }

        // 最后：随机
        Collections.shuffle(candidates);
        return candidates.get(0);
    }

    /**
     * 统计数字出现频率，返回频率最高的前11个数字
     * 如果频次相同的球号超过11个，使用多层筛选：
     * 1. ballNumbersWithCoefficients系数和筛选
     * 2. history_top_100表排名筛选
     * 3. history_top表点系数筛选
     * 4. 随机选择
     */
    private List<Integer> getTop11ByFrequency(List<Integer> allNumbers, List<BallWithCoefficient> ballsWithCoefficients) {
        log.debug("统计{}个数字的出现频率", allNumbers.size());
        
        // 统计频率
        Map<Integer, Integer> frequencyMap = new HashMap<>();
        for (Integer number : allNumbers) {
            frequencyMap.put(number, frequencyMap.getOrDefault(number, 0) + 1);
        }

        log.debug("数字频率统计：{}", frequencyMap);

        // 打印所有球号出现频率的详细信息
        log.info("=== 所有球号出现频率统计 ===");
        List<Map.Entry<Integer, Integer>> sortedEntries = frequencyMap.entrySet().stream()
                .sorted(Map.Entry.<Integer, Integer>comparingByValue().reversed()
                        .thenComparing(Map.Entry.comparingByKey()))
                .collect(Collectors.toList());
        
        for (int i = 0; i < sortedEntries.size(); i++) {
            Map.Entry<Integer, Integer> entry = sortedEntries.get(i);
            log.info("第{}位：数字{}，出现{}次", i + 1, entry.getKey(), entry.getValue());
        }
        log.info("=== 频率统计结束 ===");

        // 按频率分组
        Map<Integer, List<Integer>> frequencyGroups = new TreeMap<>(Collections.reverseOrder());
        for (Map.Entry<Integer, Integer> entry : frequencyMap.entrySet()) {
            Integer frequency = entry.getValue();
            Integer ballNumber = entry.getKey();
            
            frequencyGroups.computeIfAbsent(frequency, k -> new ArrayList<>()).add(ballNumber);
        }

        log.debug("按频率分组：{}", frequencyGroups);

        List<Integer> result = new ArrayList<>();
        
        // 按频率从高到低处理
        for (Map.Entry<Integer, List<Integer>> group : frequencyGroups.entrySet()) {
            Integer frequency = group.getKey();
            List<Integer> balls = group.getValue();
            
            // 对同频率的球号按数字升序排序
            Collections.sort(balls);
            
            log.info("频率{}的球号：{}", frequency, balls);
            
            // 检查加入这组球号后是否会超过11个
            if (result.size() + balls.size() <= 11) {
                // 不会超过11个，直接添加所有球号
                result.addAll(balls);
                log.info("直接添加{}个球号，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过11个，需要从这组球号中选择部分
                int remainingSlots = 11 - result.size();
                log.info("需要从{}个频率相同的球号中选择{}个，开始多层筛选", balls.size(), remainingSlots);
                
                List<Integer> selectedBalls = selectBallsByMultiLevelFiltering(balls, remainingSlots, ballsWithCoefficients);
                result.addAll(selectedBalls);
                
                log.info("多层筛选完成，最终选择：{}", selectedBalls);
                break; // 已经达到11个，结束
            }
            
            // 如果已经有11个，结束
            if (result.size() >= 11) {
                break;
            }
        }

        log.info("频率统计最终结果（共{}个）：{}", result.size(), result);
        
        // 打印详细的频率信息
        for (int i = 0; i < result.size(); i++) {
            Integer ballNumber = result.get(i);
            Integer frequency = frequencyMap.get(ballNumber);
            log.info("第{}位：数字{}，出现{}次", i + 1, ballNumber, frequency);
        }

        return result;
    }

    /**
     * 统计数字出现频率，返回频率最高的前11个数字（包含筛选过程说明）
     * 如果频次相同的球号超过11个，使用多层筛选：
     * 1. ballNumbersWithCoefficients系数和筛选
     * 2. history_top_100表排名筛选
     * 3. history_top表点系数筛选
     * 4. 随机选择
     */
    private BallAnalysisResultVO getTop11ByFrequencyWithProcess(List<Integer> allNumbers, List<BallWithCoefficient> ballsWithCoefficients) {
        log.debug("统计{}个数字的出现频率", allNumbers.size());
        StringBuilder processDescription = new StringBuilder();
        
        // 统计频率
        Map<Integer, Integer> frequencyMap = new HashMap<>();
        for (Integer number : allNumbers) {
            frequencyMap.put(number, frequencyMap.getOrDefault(number, 0) + 1);
        }

        log.debug("数字频率统计：{}", frequencyMap);

        // 按频率分组
        Map<Integer, List<Integer>> frequencyGroups = new TreeMap<>(Collections.reverseOrder());
        for (Map.Entry<Integer, Integer> entry : frequencyMap.entrySet()) {
            Integer frequency = entry.getValue();
            Integer ballNumber = entry.getKey();
            
            frequencyGroups.computeIfAbsent(frequency, k -> new ArrayList<>()).add(ballNumber);
        }

        List<Integer> result = new ArrayList<>();
        boolean needMultiLevelFiltering = false;
        List<Integer> multiLevelCandidates = new ArrayList<>();
        List<Integer> directlySelectedBalls = new ArrayList<>();
        List<Integer> secondaryFilteredBalls = new ArrayList<>();
        
        // 统计所有参与筛选的高频球号（用于显示）
        Map<Integer, List<Integer>> allCandidateFrequencyGroups = new LinkedHashMap<>();
        int highestFrequency = frequencyGroups.keySet().iterator().next();
        
        // 收集所有可能参与筛选的球号（不只是最高频率，而是前几个高频率组）
        int frequencyThreshold = Math.max(1, highestFrequency - 2); // 显示最高频率、次高频率、第三高频率等
        for (Map.Entry<Integer, List<Integer>> group : frequencyGroups.entrySet()) {
            Integer frequency = group.getKey();
            List<Integer> balls = group.getValue();
            Collections.sort(balls);
            
            // 只收集较高频率的球号用于显示
            if (frequency >= frequencyThreshold) {
                allCandidateFrequencyGroups.put(frequency, new ArrayList<>(balls));
            }
        }
        
        // 按频率从高到低处理，进行实际筛选
        for (Map.Entry<Integer, List<Integer>> group : frequencyGroups.entrySet()) {
            List<Integer> balls = group.getValue();
            Collections.sort(balls);
            
            // 检查加入这组球号后是否会超过11个
            if (result.size() + balls.size() <= 11) {
                // 不会超过11个，直接添加所有球号
                result.addAll(balls);
                directlySelectedBalls.addAll(balls);
                log.info("直接添加{}个球号，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过11个，需要从这组球号中选择部分
                int remainingSlots = 11 - result.size();
                log.info("需要从{}个频率相同的球号中选择{}个，开始多层筛选", balls.size(), remainingSlots);
                
                needMultiLevelFiltering = true;
                multiLevelCandidates = new ArrayList<>(balls);
                
                // 使用新的筛选方法，返回详细的筛选步骤信息
                MultiLevelFilteringResult filteringResult = performMultiLevelFilteringWithDetails(balls, remainingSlots, ballsWithCoefficients);
                result.addAll(filteringResult.getSelectedBalls());
                secondaryFilteredBalls = new ArrayList<>(filteringResult.getSelectedBalls());
                
                // 记录筛选步骤信息
                processDescription.append("筛选步骤：").append(filteringResult.getStepsDescription());
                if (!filteringResult.getDetailedStepsInfo().isEmpty()) {
                    processDescription.append(" ").append(filteringResult.getDetailedStepsInfo());
                }
                
                log.info("多层筛选完成，最终选择：{}", filteringResult.getSelectedBalls());
                break; // 已经达到11个，结束
            }
            
            // 如果已经有11个，结束
            if (result.size() >= 11) {
                break;
            }
        }

        // 构建详细的筛选过程说明
        StringBuilder detailedDescription = new StringBuilder();
        
        if (allCandidateFrequencyGroups.isEmpty()) {
            detailedDescription.append("所有球号出现频率相同，");
        } else {
            detailedDescription.append("参与筛选的候选球号按频率分布为")
                    .append(formatBallNumbersWithFrequency(allCandidateFrequencyGroups))
                    .append("；");
        }
        
        if (!needMultiLevelFiltering) {
            detailedDescription.append("直接按频率筛选出前11个球号：")
                    .append(formatBallNumbersComplete(result))
                    .append("。筛选步骤：通过频率筛选确定所有球号，无需进行T3系数和筛选、百期排位、历史排位。");
        } else {
            detailedDescription.append("无法直接筛选出前11个");
            
            if (!directlySelectedBalls.isEmpty()) {
                detailedDescription.append("，其中")
                        .append(formatBallNumbers(directlySelectedBalls))
                        .append("直接入选");
            }
            
            detailedDescription.append("，")
                    .append(formatBallNumbers(multiLevelCandidates))
                    .append("需要进行二次筛选，最终筛选出")
                    .append(formatBallNumbers(secondaryFilteredBalls))
                    .append("，组成前11个球号：")
                    .append(formatBallNumbersComplete(result))
                    .append("。")
                    .append(processDescription.toString());
        }

        log.info("频率统计最终结果（共{}个）：{}", result.size(), result);
        
        return BallAnalysisResultVO.builder()
                .result(result)
                .filteringProcess(detailedDescription.toString())
                .build();
    }

    /**
     * 多层筛选：ballNumbersWithCoefficients系数和筛选 -> history_top_100表排名 -> history_top表点系数 -> 随机选择
     */
    private List<Integer> selectBallsByMultiLevelFiltering(List<Integer> candidateBalls, int selectCount, List<BallWithCoefficient> ballsWithCoefficients) {
        log.debug("开始多层筛选，候选球号：{}，需要选择：{}个", candidateBalls, selectCount);
        
        if (candidateBalls.size() <= selectCount) {
            return new ArrayList<>(candidateBalls);
        }

        List<Integer> currentCandidates = new ArrayList<>(candidateBalls);

        // 第一层：ballNumbersWithCoefficients系数和筛选
        log.debug("=== 第一层：ballNumbersWithCoefficients系数和筛选 ===");
        List<Integer> ballNumbersFiltered = selectBallsByBallNumbersWithCoefficients(currentCandidates, selectCount, ballsWithCoefficients);
        log.debug("ballNumbersWithCoefficients系数和筛选结果：{}", ballNumbersFiltered);
        
        // 如果ballNumbersWithCoefficients筛选后的结果数量等于selectCount，说明没有相同系数和的情况，直接返回
        if (ballNumbersFiltered.size() == selectCount) {
            log.debug("ballNumbersWithCoefficients系数和筛选完成，选择：{}", ballNumbersFiltered);
            return ballNumbersFiltered;
        }
        
        // 如果ballNumbersWithCoefficients筛选后的结果数量超过selectCount，说明有系数和相同的情况，继续下一层筛选
        currentCandidates = ballNumbersFiltered;
        log.debug("ballNumbersWithCoefficients筛选后剩余候选：{}", currentCandidates);

        // 第二层：history_top_100表排名筛选
        log.debug("=== 第二层：history_top_100表排名筛选 ===");
        List<Integer> historyTop100Filtered = selectBallsByHistoryTop100Ranking(currentCandidates, selectCount);
        log.debug("history_top_100表排名筛选结果：{}", historyTop100Filtered);
        
        if (historyTop100Filtered.size() == selectCount) {
            log.debug("history_top_100表排名筛选完成，选择：{}", historyTop100Filtered);
            return historyTop100Filtered;
        }
        currentCandidates = historyTop100Filtered;
        log.debug("history_top_100表筛选后剩余候选：{}", currentCandidates);

        // 第三层：history_top表点系数筛选
        log.debug("=== 第三层：history_top表点系数筛选 ===");
        List<Integer> historyTopFiltered = selectBallsByHistoryTopPointCoefficient(currentCandidates, selectCount);
        log.debug("history_top表点系数筛选结果：{}", historyTopFiltered);
        
        if (historyTopFiltered.size() == selectCount) {
            log.debug("history_top表点系数筛选完成，选择：{}", historyTopFiltered);
            return historyTopFiltered;
        }
        currentCandidates = historyTopFiltered;
        log.debug("history_top表筛选后剩余候选：{}", currentCandidates);

        // 第四层：随机选择
        log.debug("=== 第四层：随机选择 ===");
        List<Integer> randomSelected = selectBallsRandomly(currentCandidates, selectCount);
        log.debug("随机筛选完成，选择：{}", randomSelected);
        return randomSelected;
    }

    /**
     * 多层筛选（包含过程说明）：ballNumbersWithCoefficients系数和筛选 -> history_top_100表排名 -> history_top表点系数 -> 随机选择
     */
    private List<Integer> selectBallsByMultiLevelFilteringWithProcess(List<Integer> candidateBalls, int selectCount, 
            List<BallWithCoefficient> ballsWithCoefficients, StringBuilder processDescription) {
        log.debug("开始多层筛选（含过程说明），候选球号：{}，需要选择：{}个", candidateBalls, selectCount);
        
        if (candidateBalls.size() <= selectCount) {
            return new ArrayList<>(candidateBalls);
        }

        List<Integer> currentCandidates = new ArrayList<>(candidateBalls);
        List<String> filteringSteps = new ArrayList<>();

        // 第一层：ballNumbersWithCoefficients系数和筛选
        log.debug("=== 第一层：ballNumbersWithCoefficients系数和筛选 ===");
        List<Integer> ballNumbersFiltered = selectBallsByBallNumbersWithCoefficients(currentCandidates, selectCount, ballsWithCoefficients);
        log.debug("ballNumbersWithCoefficients系数和筛选结果：{}", ballNumbersFiltered);
        
        if (ballNumbersFiltered.size() < currentCandidates.size()) {
            filteringSteps.add("T3系数和筛选");
        }
        
        // 如果ballNumbersWithCoefficients筛选后的结果数量等于selectCount，说明没有相同系数和的情况，直接返回
        if (ballNumbersFiltered.size() == selectCount) {
            log.debug("ballNumbersWithCoefficients系数和筛选完成，选择：{}", ballNumbersFiltered);
            return ballNumbersFiltered;
        }
        
        // 如果ballNumbersWithCoefficients筛选后的结果数量超过selectCount，说明有系数和相同的情况，继续下一层筛选
        currentCandidates = ballNumbersFiltered;
        log.debug("ballNumbersWithCoefficients筛选后剩余候选：{}", currentCandidates);

        // 第二层：history_top_100表排名筛选
        log.debug("=== 第二层：history_top_100表排名筛选 ===");
        List<Integer> historyTop100Filtered = selectBallsByHistoryTop100Ranking(currentCandidates, selectCount);
        log.debug("history_top_100表排名筛选结果：{}", historyTop100Filtered);
        
        if (historyTop100Filtered.size() < currentCandidates.size()) {
            filteringSteps.add("history_top_100表排名筛选");
        }
        
        if (historyTop100Filtered.size() == selectCount) {
            log.debug("history_top_100表排名筛选完成，选择：{}", historyTop100Filtered);
            return historyTop100Filtered;
        }
        currentCandidates = historyTop100Filtered;
        log.debug("history_top_100表筛选后剩余候选：{}", currentCandidates);

        // 第三层：history_top表点系数筛选
        log.debug("=== 第三层：history_top表点系数筛选 ===");
        List<Integer> historyTopFiltered = selectBallsByHistoryTopPointCoefficient(currentCandidates, selectCount);
        log.debug("history_top表点系数筛选结果：{}", historyTopFiltered);
        
        if (historyTopFiltered.size() < currentCandidates.size()) {
            filteringSteps.add("history_top表点系数筛选");
        }
        
        if (historyTopFiltered.size() == selectCount) {
            log.debug("history_top表点系数筛选完成，选择：{}", historyTopFiltered);
            return historyTopFiltered;
        }
        currentCandidates = historyTopFiltered;
        log.debug("history_top表筛选后剩余候选：{}", currentCandidates);

        // 第四层：随机选择
        log.debug("=== 第四层：随机选择 ===");
        List<Integer> randomSelected = selectBallsRandomly(currentCandidates, selectCount);
        log.debug("随机筛选完成，选择：{}", randomSelected);
        
        if (currentCandidates.size() > selectCount) {
            filteringSteps.add("随机选择");
        }
        
        return randomSelected;
    }

    /**
     * 筛选详情结果内部类
     */
    private static class FilteringDetailResult {
        private List<Integer> filteredBalls;
        private String detailInfo;
        
        public FilteringDetailResult(List<Integer> filteredBalls, String detailInfo) {
            this.filteredBalls = filteredBalls;
            this.detailInfo = detailInfo;
        }
        
        public List<Integer> getFilteredBalls() {
            return filteredBalls;
        }
        
        public String getDetailInfo() {
            return detailInfo;
        }
    }

    /**
     * 多层筛选结果内部类
     */
    private static class MultiLevelFilteringResult {
        private List<Integer> selectedBalls;
        private String stepsDescription;
        private String detailedStepsInfo;
        
        public MultiLevelFilteringResult(List<Integer> selectedBalls, String stepsDescription) {
            this.selectedBalls = selectedBalls;
            this.stepsDescription = stepsDescription;
            this.detailedStepsInfo = "";
        }
        
        public MultiLevelFilteringResult(List<Integer> selectedBalls, String stepsDescription, String detailedStepsInfo) {
            this.selectedBalls = selectedBalls;
            this.stepsDescription = stepsDescription;
            this.detailedStepsInfo = detailedStepsInfo;
        }
        
        public List<Integer> getSelectedBalls() {
            return selectedBalls;
        }
        
        public String getStepsDescription() {
            return stepsDescription;
        }
        
        public String getDetailedStepsInfo() {
            return detailedStepsInfo;
        }
    }

    /**
     * 跟随球号多级筛选结果内部类
     */
    private static class FollowBallMultiLevelFilteringResult {
        private List<Integer> selectedBalls;
        private String stepsDescription;
        private String detailedStepsInfo;
        
        public FollowBallMultiLevelFilteringResult(List<Integer> selectedBalls, String stepsDescription) {
            this.selectedBalls = selectedBalls;
            this.stepsDescription = stepsDescription;
            this.detailedStepsInfo = "";
        }
        
        public FollowBallMultiLevelFilteringResult(List<Integer> selectedBalls, String stepsDescription, String detailedStepsInfo) {
            this.selectedBalls = selectedBalls;
            this.stepsDescription = stepsDescription;
            this.detailedStepsInfo = detailedStepsInfo;
        }
        
        public List<Integer> getSelectedBalls() {
            return selectedBalls;
        }
        
        public String getStepsDescription() {
            return stepsDescription;
        }
        
        public String getDetailedStepsInfo() {
            return detailedStepsInfo;
        }
    }

    /**
     * 跟随球号多级筛选（带详细过程说明）
     * @param candidateBalls 候选球号
     * @param selectCount 需要选择的数量
     * @param step1BallsWithCoefficients 第一步T7面系数数据
     * @return 筛选结果和详细说明
     */
    private FollowBallMultiLevelFilteringResult performFollowBallMultiLevelFilteringWithDetails(List<Integer> candidateBalls, int selectCount, List<BallWithCoefficient> step1BallsWithCoefficients) {
        List<String> completedSteps = new ArrayList<>();
        List<String> skippedSteps = new ArrayList<>();
        
        // 构建 step1 球号->面系数 映射（找不到则为0）
        Map<Integer, Double> num2Coeff = new HashMap<>();
        if (step1BallsWithCoefficients != null) {
            for (BallWithCoefficient bwc : step1BallsWithCoefficients) {
                num2Coeff.put(bwc.getBallNumber(), bwc.getCoefficient());
            }
        }

        // 先按面系数降序，再按球号升序
        List<Integer> sortedByT7 = new ArrayList<>(candidateBalls);
        sortedByT7.sort((a, b) -> {
            double ca = num2Coeff.getOrDefault(a, 0.0);
            double cb = num2Coeff.getOrDefault(b, 0.0);
            if (Double.compare(cb, ca) != 0) {
                return Double.compare(cb, ca);
            }
            return Integer.compare(a, b);
        });

        // 记录所有候选球号的T7面系数详情
        StringBuilder t7Details = new StringBuilder();
        Map<Double, List<Integer>> t7Groups = new TreeMap<>(Collections.reverseOrder());
        for (Integer ball : candidateBalls) {
            double coeff = num2Coeff.getOrDefault(ball, 0.0);
            t7Groups.computeIfAbsent(coeff, k -> new ArrayList<>()).add(ball);
        }
        for (Map.Entry<Double, List<Integer>> entry : t7Groups.entrySet()) {
            List<Integer> balls = entry.getValue();
            Collections.sort(balls);
            if (t7Details.length() > 0) t7Details.append("，");
            t7Details.append(formatBallNumbers(balls)).append("（T7面系数").append(String.format("%.2f", entry.getKey())).append("）");
        }

        // 如果正好能取齐，直接取
        if (sortedByT7.size() <= selectCount) {
            completedSteps.add("通过T7面系数筛选确定");
            skippedSteps.add("百期排位");
            skippedSteps.add("历史排位");
            skippedSteps.add("随机选择");
            
            String stepsDesc = buildFollowBallStepsDescription(completedSteps, skippedSteps);
            String detailInfo = "T7面系数筛选：" + formatBallNumbers(candidateBalls) + "参与筛选，" + 
                              formatBallNumbers(sortedByT7) + "通过T7面系数筛选出来。" +
                              "T7面系数详情：" + t7Details.toString() + "。";
            
            return new FollowBallMultiLevelFilteringResult(sortedByT7, stepsDesc, detailInfo);
        }

        // 检查是否存在边界相同面系数
        double thresholdCoeff = num2Coeff.getOrDefault(sortedByT7.get(selectCount - 1), 0.0);
        
        // 统计与阈值系数相同的球号数量
        long countAtThreshold = sortedByT7.stream()
                .mapToDouble(bn -> num2Coeff.getOrDefault(bn, 0.0))
                .filter(c -> Double.compare(c, thresholdCoeff) == 0)
                .count();
        
        // 如果阈值系数只有1个球号，说明没有边界重复，直接取前selectCount个
        if (countAtThreshold == 1) {
            List<Integer> directSelected = sortedByT7.subList(0, selectCount);
            completedSteps.add("通过T7面系数筛选确定");
            skippedSteps.add("百期排位");
            skippedSteps.add("历史排位");
            skippedSteps.add("随机选择");
            
            String stepsDesc = buildFollowBallStepsDescription(completedSteps, skippedSteps);
            String detailInfo = "T7面系数筛选：" + formatBallNumbers(candidateBalls) + "参与筛选，" +
                              formatBallNumbers(directSelected) + "通过T7面系数筛选出来。" +
                              "T7面系数详情：" + t7Details.toString() + "。";
            
            return new FollowBallMultiLevelFilteringResult(directSelected, stepsDesc, detailInfo);
        }
        
        // 存在边界相同面系数：拆分高于阈值与阈值相等的集合
        List<Integer> higherThanThreshold = new ArrayList<>();
        List<Integer> equalThresholdGroup = new ArrayList<>();
        for (Integer bn : sortedByT7) {
            double c = num2Coeff.getOrDefault(bn, 0.0);
            if (c > thresholdCoeff) {
                higherThanThreshold.add(bn);
            } else if (Double.compare(c, thresholdCoeff) == 0) {
                equalThresholdGroup.add(bn);
            }
        }

        List<Integer> result = new ArrayList<>(higherThanThreshold);
        int stillNeed = selectCount - higherThanThreshold.size();
        
        if (stillNeed > 0) {
            // 需要进一步筛选
            FollowBallTop100HistoryFilteringResult furtherResult = selectByTop100ThenHistoryTopWithDetails(equalThresholdGroup, stillNeed);
            result.addAll(furtherResult.getSelectedBalls());
            
            completedSteps.add("通过T7面系数和百期排位、历史排位筛选确定");
            
            StringBuilder allDetails = new StringBuilder();
            allDetails.append("T7面系数筛选：").append(formatBallNumbers(candidateBalls)).append("参与筛选，");
            allDetails.append(formatBallNumbers(higherThanThreshold)).append("通过T7面系数直接筛选出来，");
            allDetails.append(formatBallNumbers(equalThresholdGroup)).append("因T7面系数相同需要进入百期排位、历史排位筛选。");
            allDetails.append("T7面系数详情：").append(t7Details.toString()).append("。");
            if (!furtherResult.getDetailedInfo().isEmpty()) {
                allDetails.append(" ").append(furtherResult.getDetailedInfo());
            }
            
            String stepsDesc = buildFollowBallStepsDescription(completedSteps, skippedSteps);
            return new FollowBallMultiLevelFilteringResult(result, stepsDesc, allDetails.toString());
        } else {
            completedSteps.add("通过T7面系数筛选确定");
            skippedSteps.add("百期排位");
            skippedSteps.add("历史排位");
            skippedSteps.add("随机选择");
            
            String stepsDesc = buildFollowBallStepsDescription(completedSteps, skippedSteps);
            String detailInfo = "T7面系数筛选：" + formatBallNumbers(candidateBalls) + "参与筛选，" +
                              formatBallNumbers(result) + "通过T7面系数筛选出来。" +
                              "T7面系数详情：" + t7Details.toString() + "。";
            
            return new FollowBallMultiLevelFilteringResult(result, stepsDesc, detailInfo);
        }
    }

    /**
     * 跟随球号百期排位和历史排位筛选结果
     */
    private static class FollowBallTop100HistoryFilteringResult {
        private List<Integer> selectedBalls;
        private String detailedInfo;
        
        public FollowBallTop100HistoryFilteringResult(List<Integer> selectedBalls, String detailedInfo) {
            this.selectedBalls = selectedBalls;
            this.detailedInfo = detailedInfo;
        }
        
        public List<Integer> getSelectedBalls() {
            return selectedBalls;
        }
        
        public String getDetailedInfo() {
            return detailedInfo;
        }
    }

    /**
     * 蓝球多级筛选结果内部类
     */
    private static class BlueBallMultiLevelFilteringResult {
        private List<Integer> selectedBalls;
        private String stepsDescription;
        private String detailedStepsInfo;
        
        public BlueBallMultiLevelFilteringResult(List<Integer> selectedBalls, String stepsDescription) {
            this.selectedBalls = selectedBalls;
            this.stepsDescription = stepsDescription;
            this.detailedStepsInfo = "";
        }
        
        public BlueBallMultiLevelFilteringResult(List<Integer> selectedBalls, String stepsDescription, String detailedStepsInfo) {
            this.selectedBalls = selectedBalls;
            this.stepsDescription = stepsDescription;
            this.detailedStepsInfo = detailedStepsInfo;
        }
        
        public List<Integer> getSelectedBalls() {
            return selectedBalls;
        }
        
        public String getStepsDescription() {
            return stepsDescription;
        }
        
        public String getDetailedStepsInfo() {
            return detailedStepsInfo;
        }
    }

    /**
     * 通过百期排位和历史排位进行筛选（带详细信息）
     */
    private FollowBallTop100HistoryFilteringResult selectByTop100ThenHistoryTopWithDetails(List<Integer> candidateBalls, int needCount) {
        if (candidateBalls == null || candidateBalls.isEmpty() || needCount <= 0) {
            return new FollowBallTop100HistoryFilteringResult(new ArrayList<>(), "");
        }

        StringBuilder detailedInfo = new StringBuilder();
        
        // 在同T7系数的候选中，先用100期逐组加入
        QueryWrapper<HistoryTop100> query = new QueryWrapper<>();
        query.in("ballNumber", candidateBalls)
             .orderByDesc("pointCoefficient")
             .orderByAsc("ballNumber");
        List<HistoryTop100> top100List = historyTop100Mapper.selectList(query);

        List<Integer> selectedByTop100 = new ArrayList<>();
        List<Integer> undecidedGroup = new ArrayList<>();
        StringBuilder top100Details = new StringBuilder();

        if (top100List != null && !top100List.isEmpty()) {
            // 记录所有球号的百期排位详情
            Map<Double, List<Integer>> top100Groups = new TreeMap<>(Collections.reverseOrder());
            for (HistoryTop100 item : top100List) {
                top100Groups.computeIfAbsent(item.getPointCoefficient(), k -> new ArrayList<>()).add(item.getBallNumber());
            }
            for (Map.Entry<Double, List<Integer>> entry : top100Groups.entrySet()) {
                List<Integer> balls = entry.getValue();
                Collections.sort(balls);
                if (top100Details.length() > 0) top100Details.append("，");
                top100Details.append(formatBallNumbers(balls)).append("（百期排位").append(String.format("%.2f", entry.getKey())).append("）");
            }
            
            // 记录不在百期表中的球号
            List<Integer> notInTop100 = new ArrayList<>(candidateBalls);
            List<Integer> inTop100 = top100List.stream().map(HistoryTop100::getBallNumber).collect(Collectors.toList());
            notInTop100.removeAll(inTop100);
            if (!notInTop100.isEmpty()) {
                Collections.sort(notInTop100);
                if (top100Details.length() > 0) top100Details.append("，");
                top100Details.append(formatBallNumbers(notInTop100)).append("（无百期排位）");
            }

            // 分组筛选
            Map<Double, List<Integer>> groups = new LinkedHashMap<>();
            for (HistoryTop100 item : top100List) {
                groups.computeIfAbsent(item.getPointCoefficient(), k -> new ArrayList<>())
                      .add(item.getBallNumber());
            }
            for (Map.Entry<Double, List<Integer>> entry : groups.entrySet()) {
                List<Integer> groupBalls = entry.getValue();
                if (selectedByTop100.size() + groupBalls.size() <= needCount) {
                    selectedByTop100.addAll(groupBalls);
                } else {
                    undecidedGroup.addAll(groupBalls);
                    break;
                }
                if (selectedByTop100.size() == needCount) {
                    break;
                }
            }
        }

        if (selectedByTop100.size() >= needCount) {
            detailedInfo.append("百期排位筛选：").append(formatBallNumbers(candidateBalls)).append("参与筛选，");
            detailedInfo.append(formatBallNumbers(selectedByTop100.subList(0, needCount))).append("通过百期排位筛选出来。");
            detailedInfo.append("百期排位详情：").append(top100Details.toString()).append("。");
            return new FollowBallTop100HistoryFilteringResult(selectedByTop100.subList(0, needCount), detailedInfo.toString());
        }

        // 还需要历史排位筛选
        int stillNeed = needCount - selectedByTop100.size();
        List<Integer> forHistoryTop = !undecidedGroup.isEmpty() ? new ArrayList<>(undecidedGroup) : 
                                     (top100List == null || top100List.isEmpty() ? new ArrayList<>(candidateBalls) : new ArrayList<>());

        List<Integer> selectedByHistoryTop = new ArrayList<>();
        StringBuilder historyDetails = new StringBuilder();
        
        if (!forHistoryTop.isEmpty()) {
            QueryWrapper<HistoryTop> historyQuery = new QueryWrapper<>();
            historyQuery.in("ballNumber", forHistoryTop)
                       .orderByDesc("pointCoefficient")
                       .orderByAsc("ballNumber");
            List<HistoryTop> historyTopList = historyTopMapper.selectList(historyQuery);

            if (historyTopList != null && !historyTopList.isEmpty()) {
                // 记录所有球号的历史排位详情
                Map<Double, List<Integer>> historyGroups = new TreeMap<>(Collections.reverseOrder());
                for (HistoryTop item : historyTopList) {
                    historyGroups.computeIfAbsent(item.getPointCoefficient(), k -> new ArrayList<>()).add(item.getBallNumber());
                }
                for (Map.Entry<Double, List<Integer>> entry : historyGroups.entrySet()) {
                    List<Integer> balls = entry.getValue();
                    Collections.sort(balls);
                    if (historyDetails.length() > 0) historyDetails.append("，");
                    historyDetails.append(formatBallNumbers(balls)).append("（历史点系数").append(String.format("%.2f", entry.getKey())).append("）");
                }

                // 记录不在历史表中的球号
                List<Integer> notInHistory = new ArrayList<>(forHistoryTop);
                List<Integer> inHistory = historyTopList.stream().map(HistoryTop::getBallNumber).collect(Collectors.toList());
                notInHistory.removeAll(inHistory);
                if (!notInHistory.isEmpty()) {
                    Collections.sort(notInHistory);
                    if (historyDetails.length() > 0) historyDetails.append("，");
                    historyDetails.append(formatBallNumbers(notInHistory)).append("（无历史数据）");
                }

                // 按点系数筛选
                selectedByHistoryTop.addAll(historyTopList.stream()
                    .limit(stillNeed)
                    .map(HistoryTop::getBallNumber)
                    .collect(Collectors.toList()));
            }
        }

        List<Integer> finalResult = new ArrayList<>(selectedByTop100);
        finalResult.addAll(selectedByHistoryTop);

        // 构建详细信息
        detailedInfo.append("百期排位筛选：").append(formatBallNumbers(candidateBalls)).append("参与筛选，");
        if (!selectedByTop100.isEmpty()) {
            detailedInfo.append(formatBallNumbers(selectedByTop100)).append("通过百期排位筛选出来，");
        }
        if (!forHistoryTop.isEmpty()) {
            detailedInfo.append(formatBallNumbers(forHistoryTop)).append("因百期排位不足或相同需要进入历史排位筛选。");
        }
        if (top100Details.length() > 0) {
            detailedInfo.append("百期排位详情：").append(top100Details.toString()).append("。");
        }
        
        if (!forHistoryTop.isEmpty() && !selectedByHistoryTop.isEmpty()) {
            detailedInfo.append(" 历史排位筛选：").append(formatBallNumbers(forHistoryTop)).append("参与筛选，");
            detailedInfo.append(formatBallNumbers(selectedByHistoryTop)).append("通过历史排位筛选出来。");
        }
        if (historyDetails.length() > 0) {
            detailedInfo.append(" 历史排位详情：").append(historyDetails.toString()).append("。");
        }

        return new FollowBallTop100HistoryFilteringResult(finalResult, detailedInfo.toString());
    }

    /**
     * 构建跟随球号筛选步骤描述
     */
    private String buildFollowBallStepsDescription(List<String> completedSteps, List<String> skippedSteps) {
        StringBuilder sb = new StringBuilder();
        if (!completedSteps.isEmpty()) {
            if (completedSteps.size() == 1) {
                sb.append("通过频率筛选确定部分球号，通过").append(completedSteps.get(0)).append("确定剩余球号");
            } else {
                sb.append("通过频率筛选确定部分球号，经过");
                for (int i = 0; i < completedSteps.size(); i++) {
                    if (i > 0) sb.append("、");
                    sb.append(completedSteps.get(i));
                }
                sb.append("最终确定剩余球号");
            }
        }
        if (!skippedSteps.isEmpty()) {
            if (sb.length() > 0) sb.append("，");
            sb.append("无需进行").append(String.join("、", skippedSteps));
        }
        return sb.append("。").toString();
    }

    /**
     * 蓝球多级筛选（带详细过程说明）
     * @param candidateBalls 候选蓝球号码
     * @param selectCount 需要选择的数量
     * @param step2BlueT8Coefficients 第二步T8面系数数据
     * @return 筛选结果和详细说明
     */
    private BlueBallMultiLevelFilteringResult performBlueBallMultiLevelFilteringWithDetails(List<Integer> candidateBalls, int selectCount, List<BallWithCoefficient> step2BlueT8Coefficients) {
        List<String> completedSteps = new ArrayList<>();
        List<String> skippedSteps = new ArrayList<>();
        
        // 统计同频候选在step2BlueT8Coefficients中的面系数和（找不到视为0）
        Map<Integer, Double> sumMap = new HashMap<>();
        StringBuilder t8Details = new StringBuilder();
        Map<Double, List<Integer>> t8Groups = new TreeMap<>(Collections.reverseOrder());
        
        for (Integer ball : candidateBalls) {
            double sum = 0.0;
            if (step2BlueT8Coefficients != null) {
                for (BallWithCoefficient bwc : step2BlueT8Coefficients) {
                    if (ball.equals(bwc.getBallNumber())) {
                        sum += bwc.getCoefficient();
                    }
                }
            }
            sumMap.put(ball, sum);
            t8Groups.computeIfAbsent(sum, k -> new ArrayList<>()).add(ball);
        }
        
        // 记录所有候选球号的T8面系数和详情
        for (Map.Entry<Double, List<Integer>> entry : t8Groups.entrySet()) {
            List<Integer> balls = entry.getValue();
            Collections.sort(balls);
            if (t8Details.length() > 0) t8Details.append("，");
            t8Details.append(formatBallNumbers(balls)).append("（T8面系数和").append(String.format("%.2f", entry.getKey())).append("）");
        }

        // 按"系数和降序、球号升序"排序，取前selectCount个
        List<Integer> sortedCandidates = new ArrayList<>(candidateBalls);
        sortedCandidates.sort((a, b) -> {
            int cmp = Double.compare(sumMap.getOrDefault(b, 0.0), sumMap.getOrDefault(a, 0.0));
            if (cmp != 0) return cmp;
            return Integer.compare(a, b);
        });

        List<Integer> result = sortedCandidates.subList(0, Math.min(selectCount, sortedCandidates.size()));
        
        completedSteps.add("通过T8面系数和筛选确定");
        skippedSteps.add("百期排位");
        skippedSteps.add("历史排位");
        
        String stepsDesc = buildBlueBallStepsDescription(completedSteps, skippedSteps);
        String detailInfo = "T8面系数和筛选：" + formatBallNumbers(candidateBalls) + "参与筛选，" +
                          formatBallNumbers(result) + "通过T8面系数和筛选出来。" +
                          "T8面系数和详情：" + t8Details.toString() + "。";
        
        return new BlueBallMultiLevelFilteringResult(result, stepsDesc, detailInfo);
    }

    /**
     * 构建蓝球筛选步骤描述
     */
    private String buildBlueBallStepsDescription(List<String> completedSteps, List<String> skippedSteps) {
        StringBuilder sb = new StringBuilder();
        if (!completedSteps.isEmpty()) {
            if (completedSteps.size() == 1) {
                sb.append("通过频率筛选确定部分球号，通过").append(completedSteps.get(0)).append("确定剩余球号");
            } else {
                sb.append("通过频率筛选确定部分球号，经过");
                for (int i = 0; i < completedSteps.size(); i++) {
                    if (i > 0) sb.append("、");
                    sb.append(completedSteps.get(i));
                }
                sb.append("最终确定剩余球号");
            }
        }
        if (!skippedSteps.isEmpty()) {
            if (sb.length() > 0) sb.append("，");
            sb.append("无需进行").append(String.join("、", skippedSteps));
        }
        return sb.append("。").toString();
    }

    /**
     * 格式化球号列表为字符串
     */
    private String formatBallNumbers(List<Integer> ballNumbers) {
        if (ballNumbers == null || ballNumbers.isEmpty()) {
            return "";
        }
        
        List<Integer> sortedBalls = new ArrayList<>(ballNumbers);
        Collections.sort(sortedBalls);
        
        if (sortedBalls.size() <= 10) {
            return sortedBalls.toString().replace("[", "").replace("]", "");
        } else {
            // 如果球号太多，只显示前几个和后几个
            List<Integer> displayBalls = new ArrayList<>();
            displayBalls.addAll(sortedBalls.subList(0, 5));
            displayBalls.addAll(sortedBalls.subList(sortedBalls.size() - 3, sortedBalls.size()));
            return displayBalls.subList(0, 5).toString().replace("[", "").replace("]", "") + 
                   "等" + sortedBalls.size() + "个";
        }
    }

    /**
     * 格式化球号列表为字符串（完整显示，不使用"等"字简化）
     */
    private String formatBallNumbersComplete(List<Integer> ballNumbers) {
        if (ballNumbers == null || ballNumbers.isEmpty()) {
            return "";
        }
        
        List<Integer> sortedBalls = new ArrayList<>(ballNumbers);
        Collections.sort(sortedBalls);
        
        return sortedBalls.toString().replace("[", "").replace("]", "");
    }

    /**
     * 格式化带频率的球号列表为字符串
     * @param frequencyGroups 按频率分组的球号，key为频率，value为球号列表
     */
    private String formatBallNumbersWithFrequency(Map<Integer, List<Integer>> frequencyGroups) {
        if (frequencyGroups == null || frequencyGroups.isEmpty()) {
            return "";
        }
        
        StringBuilder result = new StringBuilder();
        List<String> parts = new ArrayList<>();
        
        // 按频率从高到低排序
        List<Map.Entry<Integer, List<Integer>>> sortedEntries = frequencyGroups.entrySet().stream()
                .sorted(Map.Entry.<Integer, List<Integer>>comparingByKey().reversed())
                .collect(Collectors.toList());
        
        for (Map.Entry<Integer, List<Integer>> entry : sortedEntries) {
            Integer frequency = entry.getKey();
            List<Integer> balls = entry.getValue();
            
            // 对球号排序
            Collections.sort(balls);
            
            // 始终显示所有球号，不使用省略
            String ballsStr = balls.toString().replace("[", "").replace("]", "");
            parts.add(ballsStr + "（出现" + frequency + "次）");
        }
        
        // 组合所有部分，显示完整的频率分布信息
        for (int i = 0; i < parts.size(); i++) {
            if (i > 0) {
                result.append("，");
            }
            result.append(parts.get(i));
        }
        
        return result.toString();
    }

    /**
     * 执行多层筛选并返回详细的步骤信息
     * @param candidateBalls 候选球号列表
     * @param selectCount 需要选择的数量
     * @param ballsWithCoefficients 系数数据
     * @return 筛选结果和步骤描述
     */
    private MultiLevelFilteringResult performMultiLevelFilteringWithDetails(List<Integer> candidateBalls, 
            int selectCount, List<BallWithCoefficient> ballsWithCoefficients) {
        log.debug("开始详细多层筛选，候选球号：{}，需要选择：{}个", candidateBalls, selectCount);
        
        if (candidateBalls.size() <= selectCount) {
            return new MultiLevelFilteringResult(new ArrayList<>(candidateBalls), "直接选择，无需进一步筛选。");
        }

        List<Integer> currentCandidates = new ArrayList<>(candidateBalls);
        List<String> completedSteps = new ArrayList<>();
        List<String> skippedSteps = new ArrayList<>();
        StringBuilder detailedInfo = new StringBuilder();

        // 第一层：ballNumbersWithCoefficients系数和筛选
        log.debug("=== 第一层：T3系数和筛选 ===");
        FilteringDetailResult t3Result = selectBallsByBallNumbersWithCoefficientsWithDetails(currentCandidates, selectCount, ballsWithCoefficients);
        List<Integer> ballNumbersFiltered = t3Result.getFilteredBalls();
        log.debug("T3系数和筛选结果：{}", ballNumbersFiltered);
        
        if (ballNumbersFiltered.size() < currentCandidates.size()) {
            completedSteps.add("T3系数和筛选");
            if (!t3Result.getDetailInfo().isEmpty()) {
                detailedInfo.append("T3系数和详情：").append(t3Result.getDetailInfo()).append("。");
            }
        }
        
        if (ballNumbersFiltered.size() == selectCount) {
            log.debug("T3系数和筛选完成，选择：{}", ballNumbersFiltered);
            skippedSteps.add("百期排位");
            skippedSteps.add("历史排位");
            return new MultiLevelFilteringResult(ballNumbersFiltered, buildStepsDescription(completedSteps, skippedSteps), detailedInfo.toString());
        }
        
        currentCandidates = ballNumbersFiltered;

        // 第二层：history_top_100表排名筛选（百期排位）
        log.debug("=== 第二层：百期排位筛选 ===");
        FilteringDetailResult top100Result = selectBallsByHistoryTop100RankingWithDetails(currentCandidates, selectCount);
        List<Integer> historyTop100Filtered = top100Result.getFilteredBalls();
        log.debug("百期排位筛选结果：{}", historyTop100Filtered);
        
        if (historyTop100Filtered.size() < currentCandidates.size()) {
            completedSteps.add("百期排位");
            if (!top100Result.getDetailInfo().isEmpty()) {
                if (detailedInfo.length() > 0) detailedInfo.append(" ");
                detailedInfo.append("百期排位详情：").append(top100Result.getDetailInfo()).append("。");
            }
        }
        
        if (historyTop100Filtered.size() == selectCount) {
            log.debug("百期排位筛选完成，选择：{}", historyTop100Filtered);
            skippedSteps.add("历史排位");
            return new MultiLevelFilteringResult(historyTop100Filtered, buildStepsDescription(completedSteps, skippedSteps), detailedInfo.toString());
        }
        currentCandidates = historyTop100Filtered;

        // 第三层：history_top表点系数筛选（历史排位）
        log.debug("=== 第三层：历史排位筛选 ===");
        FilteringDetailResult historyTopResult = selectBallsByHistoryTopPointCoefficientWithDetails(currentCandidates, selectCount);
        List<Integer> historyTopFiltered = historyTopResult.getFilteredBalls();
        log.debug("历史排位筛选结果：{}", historyTopFiltered);
        
        if (historyTopFiltered.size() < currentCandidates.size()) {
            completedSteps.add("历史排位");
            if (!historyTopResult.getDetailInfo().isEmpty()) {
                if (detailedInfo.length() > 0) detailedInfo.append(" ");
                detailedInfo.append("历史排位详情：").append(historyTopResult.getDetailInfo()).append("。");
            }
        }
        
        if (historyTopFiltered.size() == selectCount) {
            log.debug("历史排位筛选完成，选择：{}", historyTopFiltered);
            return new MultiLevelFilteringResult(historyTopFiltered, buildStepsDescription(completedSteps, skippedSteps), detailedInfo.toString());
        }
        currentCandidates = historyTopFiltered;

        // 第四层：随机选择
        log.debug("=== 第四层：随机选择 ===");
        List<Integer> randomSelected = selectBallsRandomly(currentCandidates, selectCount);
        log.debug("随机筛选完成，选择：{}", randomSelected);
        
        if (currentCandidates.size() > selectCount) {
            completedSteps.add("随机选择");
        }
        
        return new MultiLevelFilteringResult(randomSelected, buildStepsDescription(completedSteps, skippedSteps), detailedInfo.toString());
    }

    /**
     * 构建筛选步骤描述
     * @param completedSteps 已完成的步骤
     * @param skippedSteps 跳过的步骤
     * @return 步骤描述字符串
     */
    private String buildStepsDescription(List<String> completedSteps, List<String> skippedSteps) {
        StringBuilder description = new StringBuilder();
        
        if (!completedSteps.isEmpty()) {
            if (completedSteps.size() == 1) {
                description.append("通过频率筛选确定部分球号，通过").append(completedSteps.get(0)).append("确定剩余球号");
            } else {
                description.append("通过频率筛选确定部分球号，经过");
                for (int i = 0; i < completedSteps.size(); i++) {
                    if (i > 0) description.append("、");
                    description.append(completedSteps.get(i));
                }
                description.append("最终确定剩余球号");
            }
        }
        
        if (!skippedSteps.isEmpty()) {
            if (description.length() > 0) {
                description.append("，无需进行");
            } else {
                description.append("无需进行");
            }
            for (int i = 0; i < skippedSteps.size(); i++) {
                if (i > 0) description.append("、");
                description.append(skippedSteps.get(i));
            }
        }
        
        description.append("。");
        return description.toString();
    }

    /**
     * 根据ballNumbersWithCoefficients系数和进行筛选
     * 当系数和相同时，返回所有相同系数和的球号，让下一层筛选来处理
     */
    private List<Integer> selectBallsByBallNumbersWithCoefficients(List<Integer> candidateBalls, int selectCount, List<BallWithCoefficient> ballsWithCoefficients) {
        log.debug("使用ballNumbersWithCoefficients系数和筛选{}个候选球号，需要选择{}个", candidateBalls.size(), selectCount);
        
        if (candidateBalls.size() <= selectCount) {
            return new ArrayList<>(candidateBalls);
        }

        // 计算每个球号的系数和
        Map<Integer, Double> ballCoefficientSum = new HashMap<>();
        for (Integer ballNumber : candidateBalls) {
            double coefficientSum = calculateBallNumbersWithCoefficientsSum(ballNumber, ballsWithCoefficients);
            ballCoefficientSum.put(ballNumber, coefficientSum);
            log.debug("球号{}的ballNumbersWithCoefficients系数和：{}", ballNumber, coefficientSum);
        }

        // 按系数和分组（从高到低排序）
        Map<Double, List<Integer>> coefficientGroups = new TreeMap<>(Collections.reverseOrder());
        for (Integer ballNumber : candidateBalls) {
            double coefficientSum = ballCoefficientSum.get(ballNumber);
            coefficientGroups.computeIfAbsent(coefficientSum, k -> new ArrayList<>()).add(ballNumber);
        }

        log.debug("ballNumbersWithCoefficients系数分组：{}", coefficientGroups);

        List<Integer> result = new ArrayList<>();
        
        // 按系数和从高到低处理
        for (Map.Entry<Double, List<Integer>> group : coefficientGroups.entrySet()) {
            Double coefficientSum = group.getKey();
            List<Integer> balls = group.getValue();
            
            log.debug("系数和{}的球号：{}", coefficientSum, balls);
            
            // 检查加入这组球号后是否会超过selectCount个
            if (result.size() + balls.size() <= selectCount) {
                // 不会超过，直接添加所有球号（按球号升序排序）
                Collections.sort(balls);
                result.addAll(balls);
                log.debug("直接添加{}个球号，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过，返回所有相同系数和的球号，让下一层筛选来处理
                result.addAll(balls);
                log.debug("系数和相同，返回所有相同系数和的球号：{}，让下一层筛选处理", balls);
                break;
            }
            
            // 如果已经有selectCount个，结束
            if (result.size() >= selectCount) {
                break;
            }
        }

        log.debug("ballNumbersWithCoefficients筛选结果：{}", result);
        return result;
    }

    /**
     * 计算指定球号在ballNumbersWithCoefficients中的系数和
     * 从ballsWithCoefficients列表中找到所有匹配的球号，计算系数和
     */
    private double calculateBallNumbersWithCoefficientsSum(Integer ballNumber, List<BallWithCoefficient> ballsWithCoefficients) {
        try {
            // 从ballsWithCoefficients中找到所有匹配的球号，计算系数和
            double sum = ballsWithCoefficients.stream()
                    .filter(ball -> ball.getBallNumber().equals(ballNumber))
                    .mapToDouble(BallWithCoefficient::getCoefficient)
                    .sum();
            
            log.debug("球号{}在ballsWithCoefficients中的系数和：{}", ballNumber, sum);
            return sum;
        } catch (Exception e) {
            log.error("计算ballNumbersWithCoefficients系数和时出错，球号：{}", ballNumber, e);
            return 0.0;
        }
    }

    /**
     * 根据ballNumbersWithCoefficients系数和进行筛选（包含详细信息）
     * @param candidateBalls 候选球号列表
     * @param selectCount 需要选择的数量
     * @param ballsWithCoefficients 系数数据
     * @return 筛选结果和详细信息
     */
    private FilteringDetailResult selectBallsByBallNumbersWithCoefficientsWithDetails(List<Integer> candidateBalls, 
            int selectCount, List<BallWithCoefficient> ballsWithCoefficients) {
        log.debug("使用ballNumbersWithCoefficients系数和筛选{}个候选球号，需要选择{}个", candidateBalls.size(), selectCount);
        
        if (candidateBalls.size() <= selectCount) {
            return new FilteringDetailResult(new ArrayList<>(candidateBalls), "");
        }

        // 计算每个球号的系数和
        Map<Integer, Double> ballCoefficientSum = new HashMap<>();
        for (Integer ballNumber : candidateBalls) {
            double coefficientSum = calculateBallNumbersWithCoefficientsSum(ballNumber, ballsWithCoefficients);
            ballCoefficientSum.put(ballNumber, coefficientSum);
            log.debug("球号{}的ballNumbersWithCoefficients系数和：{}", ballNumber, coefficientSum);
        }

        // 按系数和分组（从高到低排序）
        Map<Double, List<Integer>> coefficientGroups = new TreeMap<>(Collections.reverseOrder());
        for (Integer ballNumber : candidateBalls) {
            double coefficientSum = ballCoefficientSum.get(ballNumber);
            coefficientGroups.computeIfAbsent(coefficientSum, k -> new ArrayList<>()).add(ballNumber);
        }

        log.debug("ballNumbersWithCoefficients系数分组：{}", coefficientGroups);

        List<Integer> result = new ArrayList<>();
        StringBuilder detailInfo = new StringBuilder();
        
        // 先记录所有球号的系数和详细信息
        for (Map.Entry<Double, List<Integer>> group : coefficientGroups.entrySet()) {
            Double coefficientSum = group.getKey();
            List<Integer> balls = group.getValue();
            Collections.sort(balls); // 按球号升序排序
            
            // 记录详细信息（显示所有参与筛选的球号）
            if (detailInfo.length() > 0) detailInfo.append("，");
            detailInfo.append(formatBallNumbers(balls)).append("（系数和").append(String.format("%.2f", coefficientSum)).append("）");
        }
        
        // 再按系数和从高到低筛选球号
        for (Map.Entry<Double, List<Integer>> group : coefficientGroups.entrySet()) {
            Double coefficientSum = group.getKey();
            List<Integer> balls = group.getValue();
            
            log.debug("系数和{}的球号：{}", coefficientSum, balls);
            
            // 检查加入这组球号后是否会超过selectCount个
            if (result.size() + balls.size() <= selectCount) {
                // 不会超过，直接添加所有球号（按球号升序排序）
                Collections.sort(balls);
                result.addAll(balls);
                log.debug("直接添加{}个球号，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过，返回所有相同系数和的球号，让下一层筛选来处理
                result.addAll(balls);
                log.debug("系数和相同，返回所有相同系数的球号：{}，让下一层筛选处理", balls);
                break;
            }
            
            // 如果已经有selectCount个，结束
            if (result.size() >= selectCount) {
                break;
            }
        }

        log.debug("ballNumbersWithCoefficients筛选结果：{}", result);
        return new FilteringDetailResult(result, detailInfo.toString());
    }

    /**
     * 根据T3表线系数之和进行筛选
     * 当线系数之和相同时，返回所有相同线系数之和的球号，让下一层筛选来处理
     */
    private List<Integer> selectBallsByT3LineCoefficientSum(List<Integer> candidateBalls, int selectCount) {
        log.debug("使用T3表线系数之和筛选{}个候选球号，需要选择{}个", candidateBalls.size(), selectCount);
        
        if (candidateBalls.size() <= selectCount) {
            return new ArrayList<>(candidateBalls);
        }

        // 计算每个球号作为主球号时的线系数之和
        Map<Integer, Double> ballCoefficientSum = new HashMap<>();
        for (Integer ballNumber : candidateBalls) {
            double coefficientSum = calculateT3LineCoefficientSum(ballNumber);
            ballCoefficientSum.put(ballNumber, coefficientSum);
            log.debug("球号{}的T3线系数之和：{}", ballNumber, coefficientSum);
        }

        // 按线系数之和降序排序
        List<Integer> sortedBalls = candidateBalls.stream()
                .sorted((a, b) -> Double.compare(ballCoefficientSum.get(b), ballCoefficientSum.get(a)))
                .collect(Collectors.toList());

        log.debug("按T3线系数之和排序后的球号：{}", sortedBalls);

        // 按线系数之和分组
        Map<Double, List<Integer>> coefficientGroups = new LinkedHashMap<>();
        for (Integer ballNumber : sortedBalls) {
            double coefficientSum = ballCoefficientSum.get(ballNumber);
            coefficientGroups.computeIfAbsent(coefficientSum, k -> new ArrayList<>()).add(ballNumber);
        }

        log.debug("T3表线系数分组：{}", coefficientGroups);

        List<Integer> result = new ArrayList<>();
        
        // 按线系数之和从高到低处理
        for (Map.Entry<Double, List<Integer>> group : coefficientGroups.entrySet()) {
            Double coefficientSum = group.getKey();
            List<Integer> balls = group.getValue();
            
            log.debug("线系数之和{}的球号：{}", coefficientSum, balls);
            
            // 检查加入这组球号后是否会超过selectCount个
            if (result.size() + balls.size() <= selectCount) {
                // 不会超过，直接添加所有球号（按球号升序排序）
                Collections.sort(balls);
                result.addAll(balls);
                log.debug("直接添加{}个球号，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过，返回所有相同线系数之和的球号，让下一层筛选来处理
                result.addAll(balls);
                log.debug("线系数之和相同，返回所有相同线系数的球号：{}，让下一层筛选处理", balls);
                break;
            }
            
            // 如果已经有selectCount个，结束
            if (result.size() >= selectCount) {
                break;
            }
        }

        log.debug("T3表线系数筛选结果：{}", result);
        return result;
    }

    /**
     * 计算指定球号作为主球号时的T3表线系数之和
     */
    private double calculateT3LineCoefficientSum(Integer masterBallNumber) {
        try {
            // 查询T3表中该主球号的所有记录
            QueryWrapper<T3> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("masterBallNumber", masterBallNumber)
                       .orderByDesc("lineCoefficient");
            
            List<T3> t3List = t3Mapper.selectList(queryWrapper);
            
            if (t3List.isEmpty()) {
                log.debug("T3表中主球{}没有数据，线系数之和为0", masterBallNumber);
                return 0.0;
            }

            // 计算线系数之和
            double sum = t3List.stream()
                    .mapToDouble(T3::getLineCoefficient)
                    .sum();
            
            log.debug("T3表主球{}的线系数之和：{}", masterBallNumber, sum);
            return sum;
            
        } catch (Exception e) {
            log.error("计算T3表线系数之和时出错，主球号：{}", masterBallNumber, e);
            return 0.0;
        }
    }

    /**
     * 跟随球号分析算法
     * @param level 高位/中位/低位标识 (H/M/L)
     * @param firstThreeRedBalls 前3个红球号码（独立的）
     * @param lastSixRedBalls 后6个红球号码（独立的）
     * @param blueBall 1个蓝球号码
     * @return 分析结果：出现频率最高的前10位数字
     */
    public List<Integer> fallowBallAnalysis(String level, List<Integer> firstThreeRedBalls, 
                                          List<Integer> lastSixRedBalls, Integer blueBall) {
        log.info("开始跟随球号分析算法，级别：{}，前3个红球：{}，后6个红球：{}，蓝球：{}", 
                level, firstThreeRedBalls, lastSixRedBalls, blueBall);
        
        // 验证输入参数
        if (firstThreeRedBalls == null || firstThreeRedBalls.size() != 3) {
            throw new IllegalArgumentException("前3个红球数量必须为3个");
        }
        if (lastSixRedBalls == null || lastSixRedBalls.size() != 6) {
            throw new IllegalArgumentException("后6个红球数量必须为6个");
        }
        if (blueBall == null) {
            throw new IllegalArgumentException("蓝球不能为空");
        }
        if (!Arrays.asList("H", "M", "L").contains(level)) {
            throw new IllegalArgumentException("级别必须为H、M或L");
        }

        // 验证球号范围和重复性
        validateBallNumbers(firstThreeRedBalls, lastSixRedBalls, blueBall);

        List<Integer> allNumbers = new ArrayList<>();

        // 第一步：处理第1个红球，从T7表获取10个数字（同时记录系数）
        log.info("第一步：处理第1个红球{}，从T7表获取10个数字，使用{}级别算法", firstThreeRedBalls.get(0), level);
        List<Integer> firstRedBallNumbers = getTop10FromT7(firstThreeRedBalls.get(0), level);
        allNumbers.addAll(firstRedBallNumbers);
        log.info("第1个红球{}获取到{}个数字：{}", firstThreeRedBalls.get(0), firstRedBallNumbers.size(), firstRedBallNumbers);

        // 同步记录上述10个数字在T7表中的面系数，记录格式与analyzeBalls一致
        List<BallWithCoefficient> step1BallsWithCoefficients = getT7CoefficientsFor(firstThreeRedBalls.get(0), firstRedBallNumbers);
        log.info("=== 第一步（T7）球号和系数统计（共{}个） ===", step1BallsWithCoefficients.size());
        for (int i = 0; i < step1BallsWithCoefficients.size(); i++) {
            BallWithCoefficient ballWithCoeff = step1BallsWithCoefficients.get(i);
            log.info("第{}个：{}", i + 1, ballWithCoeff.toString());
        }
        log.info("=== 第一步统计结束 ===");

        // 第二步：处理后6个红球，每个红球从T3表获取26个数字
        log.info("第二步：处理后6个红球，每个红球从T3表获取26个数字");
        for (int i = 0; i < lastSixRedBalls.size(); i++) {
            Integer redBall = lastSixRedBalls.get(i);
            log.info("处理第{}个红球：{}", i + 1, redBall);
            
            List<Integer> ballNumbers = getTop26FromT3(redBall);
            allNumbers.addAll(ballNumbers);
            
            log.info("红球{}获取到{}个数字：{}", redBall, ballNumbers.size(), ballNumbers);
        }

        // 第三步：从history_top_100获取前3个球号
        log.info("第三步：从history_top_100获取前3个球号");
        List<Integer> top3Numbers = getTop3FromHistoryTop100();
        allNumbers.addAll(top3Numbers);
        log.info("从history_top_100获取到{}个数字：{}", top3Numbers.size(), top3Numbers);

        // 第四步：取出前3个红球号码的后两个
        log.info("第四步：取出前3个红球号码的后两个");
        List<Integer> lastTwoOfFirstThree = Arrays.asList(firstThreeRedBalls.get(1), firstThreeRedBalls.get(2));
        allNumbers.addAll(lastTwoOfFirstThree);
        log.info("前3个红球的后两个：{}", lastTwoOfFirstThree);

        // 第五步：用上期蓝球从T4表获取26个蓝球号码
        log.info("第五步：用上期蓝球{}从T4表获取26个蓝球号码", blueBall);
        List<Integer> blueNumbers = getTop26FromT4(blueBall);
        allNumbers.addAll(blueNumbers);
        log.info("蓝球{}获取到{}个数字：{}", blueBall, blueNumbers.size(), blueNumbers);

        log.info("总共收集到{}个数字", allNumbers.size());

        // 第六步：统计频率并获取前10个（若同频超过10个，用第一步T7面系数作为二次筛选依据）
        List<Integer> result = getTop10ByFrequency(allNumbers, step1BallsWithCoefficients);
        
        log.info("跟随球号分析算法完成，结果：{}", result);
        return result;
    }

    /**
     * 跟随球号分析算法（带筛选过程说明）
     * @param level 级别：H-高，M-中，L-低
     * @param firstThreeRedBalls 前3个红球
     * @param lastSixRedBalls 后6个红球
     * @param blueBall 蓝球
     * @return 分析结果VO，包含前10个数字和筛选过程说明
     */
    public FollowBallAnalysisResultVO fallowBallAnalysisWithProcess(String level, List<Integer> firstThreeRedBalls, 
                                          List<Integer> lastSixRedBalls, Integer blueBall) {
        log.info("开始跟随球号分析算法（带过程说明），级别：{}，前3个红球：{}，后6个红球：{}，蓝球：{}", 
                level, firstThreeRedBalls, lastSixRedBalls, blueBall);
        
        // 验证输入参数
        if (firstThreeRedBalls == null || firstThreeRedBalls.size() != 3) {
            throw new IllegalArgumentException("前3个红球数量必须为3个");
        }
        if (lastSixRedBalls == null || lastSixRedBalls.size() != 6) {
            throw new IllegalArgumentException("后6个红球数量必须为6个");
        }
        if (blueBall == null) {
            throw new IllegalArgumentException("蓝球不能为空");
        }
        if (!Arrays.asList("H", "M", "L").contains(level)) {
            throw new IllegalArgumentException("级别必须为H、M或L");
        }

        // 验证球号范围和重复性
        validateBallNumbers(firstThreeRedBalls, lastSixRedBalls, blueBall);

        List<Integer> allNumbers = new ArrayList<>();

        // 第一步：处理第1个红球，从T7表获取10个数字（同时记录系数）
        log.info("第一步：处理第1个红球{}，从T7表获取10个数字，使用{}级别算法", firstThreeRedBalls.get(0), level);
        List<Integer> firstRedBallNumbers = getTop10FromT7(firstThreeRedBalls.get(0), level);
        allNumbers.addAll(firstRedBallNumbers);
        log.info("第1个红球{}获取到{}个数字：{}", firstThreeRedBalls.get(0), firstRedBallNumbers.size(), firstRedBallNumbers);

        // 同步记录上述10个数字在T7表中的面系数，记录格式与analyzeBalls一致
        List<BallWithCoefficient> step1BallsWithCoefficients = getT7CoefficientsFor(firstThreeRedBalls.get(0), firstRedBallNumbers);
        log.info("=== 第一步（T7）球号和系数统计（共{}个） ===", step1BallsWithCoefficients.size());
        for (int i = 0; i < step1BallsWithCoefficients.size(); i++) {
            BallWithCoefficient ballWithCoeff = step1BallsWithCoefficients.get(i);
            log.info("第{}个：{}", i + 1, ballWithCoeff.toString());
        }
        log.info("=== 第一步统计结束 ===");

        // 第二步：处理后6个红球，每个红球从T3表获取26个数字
        log.info("第二步：处理后6个红球，每个红球从T3表获取26个数字");
        for (int i = 0; i < lastSixRedBalls.size(); i++) {
            Integer redBall = lastSixRedBalls.get(i);
            log.info("处理第{}个红球：{}", i + 1, redBall);
            
            List<Integer> ballNumbers = getTop26FromT3(redBall);
            allNumbers.addAll(ballNumbers);
            
            log.info("红球{}获取到{}个数字：{}", redBall, ballNumbers.size(), ballNumbers);
        }

        // 第三步：从history_top_100获取前3个球号
        log.info("第三步：从history_top_100获取前3个球号");
        List<Integer> top3Numbers = getTop3FromHistoryTop100();
        allNumbers.addAll(top3Numbers);
        log.info("从history_top_100获取到{}个数字：{}", top3Numbers.size(), top3Numbers);

        // 第四步：取出前3个红球号码的后两个
        log.info("第四步：取出前3个红球号码的后两个");
        List<Integer> lastTwoOfFirstThree = Arrays.asList(firstThreeRedBalls.get(1), firstThreeRedBalls.get(2));
        allNumbers.addAll(lastTwoOfFirstThree);
        log.info("前3个红球的后两个：{}", lastTwoOfFirstThree);

        // 第五步：用上期蓝球从T4表获取26个蓝球号码
        log.info("第五步：用上期蓝球{}从T4表获取26个蓝球号码", blueBall);
        List<Integer> blueNumbers = getTop26FromT4(blueBall);
        allNumbers.addAll(blueNumbers);
        log.info("蓝球{}获取到{}个数字：{}", blueBall, blueNumbers.size(), blueNumbers);

        log.info("总共收集到{}个数字", allNumbers.size());

        // 第六步：统计频率并获取前10个（带筛选过程说明）
        FollowBallAnalysisResultVO result = getTop10ByFrequencyWithProcess(allNumbers, step1BallsWithCoefficients);
        
        log.info("跟随球号分析算法（带过程说明）完成，结果：{}，过程说明：{}", result.getResult(), result.getFilteringProcess());
        return result;
    }

    /**
     * 验证球号的有效性和唯一性
     */
    private void validateBallNumbers(List<Integer> firstThreeRedBalls, List<Integer> lastSixRedBalls, Integer blueBall) {
        // 验证红球范围
        for (Integer ball : firstThreeRedBalls) {
            if (ball == null || ball < 1 || ball > 33) {
                throw new IllegalArgumentException("前3个红球号码必须在1-33范围内");
            }
        }
        for (Integer ball : lastSixRedBalls) {
            if (ball == null || ball < 1 || ball > 33) {
                throw new IllegalArgumentException("后6个红球号码必须在1-33范围内");
            }
        }
        
        // 验证蓝球范围
        if (blueBall < 1 || blueBall > 16) {
            throw new IllegalArgumentException("蓝球号码必须在1-16范围内");
        }
        
        // 验证前3个红球不重复
        Set<Integer> firstThreeSet = new HashSet<>(firstThreeRedBalls);
        if (firstThreeSet.size() != 3) {
            throw new IllegalArgumentException("前3个红球号码不能重复");
        }
        
        // 验证后6个红球不重复
        Set<Integer> lastSixSet = new HashSet<>(lastSixRedBalls);
        if (lastSixSet.size() != 6) {
            throw new IllegalArgumentException("后6个红球号码不能重复");
        }
        
        log.debug("球号验证通过");
    }

    /**
     * 从T7表获取指定主球的10个从球号
     * 根据不同级别使用不同的选择策略
     * @param masterBallNumber 主球号
     * @param level 级别：H-高位，M-中位，L-低位
     */
    private List<Integer> getTop10FromT7(Integer masterBallNumber, String level) {
        log.debug("从T7表查询主球{}的面系数数据，级别：{}", masterBallNumber, level);
        
        // 查询指定主球的所有数据，按面系数降序排列
        QueryWrapper<T7> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("masterBallNumber", masterBallNumber)
                   .orderByDesc("faceCoefficient")
                   .orderByAsc("slaveBallNumber"); // 面系数相同时按从球号升序
        
        List<T7> t7List = t7Mapper.selectList(queryWrapper);
        
        if (t7List.isEmpty()) {
            log.warn("T7表中主球{}没有数据", masterBallNumber);
            return new ArrayList<>();
        }

        List<Integer> result = new ArrayList<>();
        
        switch (level) {
            case "H":
                // 高位：取面系数最大的前10个
                result = getHighLevelBallsFromT7(t7List);
                break;
            case "M":
                // 中位：取面系数平均值向上5个、向下4个，共10个球号
                result = getMiddleLevelBallsFromT7(t7List);
                break;
            case "L":
                // 低位：取面系数最小值向上第3-13个球共10个球号
                result = getLowLevelBallsFromT7(t7List);
                break;
        }

        log.debug("T7表主球{}{}级别最终选择的{}个从球：{}", masterBallNumber, level, result.size(), result);
        return result;
    }

    /**
     * 高位算法：从T7表取面系数最大的前10个
     */
    private List<Integer> getHighLevelBallsFromT7(List<T7> t7List) {
        if (t7List.size() < 10) {
            log.warn("T7表数据不足10条，实际{}条", t7List.size());
            return t7List.stream().map(T7::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 获取前9个
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < 9; i++) {
            result.add(t7List.get(i).getSlaveBallNumber());
        }

        // 处理第10个位置：检查是否有相同的面系数
        Double targetCoefficient = t7List.get(9).getFaceCoefficient();
        List<T7> candidatesFor10th = new ArrayList<>();
        
        // 找出所有面系数等于第10个位置面系数的记录
        for (int i = 9; i < t7List.size(); i++) {
            if (t7List.get(i).getFaceCoefficient().equals(targetCoefficient)) {
                candidatesFor10th.add(t7List.get(i));
            } else {
                break; // 面系数不同，停止查找
            }
        }

        if (candidatesFor10th.size() == 1) {
            // 只有一个候选，直接添加
            result.add(candidatesFor10th.get(0).getSlaveBallNumber());
        } else {
            // 有多个候选，通过history_top_100表的点系数排序
            log.debug("第10位有{}个相同面系数的候选：{}", candidatesFor10th.size(), 
                candidatesFor10th.stream().map(T7::getSlaveBallNumber).collect(Collectors.toList()));
            
            Integer bestBall = selectBestBallFromHistoryTop100(
                candidatesFor10th.stream().map(T7::getSlaveBallNumber).collect(Collectors.toList())
            );
            result.add(bestBall);
        }

        log.debug("T7高位算法最终选择的10个球：{}", result);
        return result;
    }

    /**
     * 中位算法：从T7表取面系数平均值向上5个、向下4个，共10个球号。
     * 毛边处理要求：
     * - 向上边界包含边界本身（或其上方/下方的同系数项）一起参与比较；
     * - 向下边界包含边界本身（或其下方的同系数项）一起参与比较；
     * - 当边界存在多个相同面系数的候选时，按 history_top_100 → history_top → 随机 的顺序筛选。
     */
    private List<Integer> getMiddleLevelBallsFromT7(List<T7> t7List) {
        if (t7List.size() < 10) {
            log.warn("T7表数据不足10条，实际{}条", t7List.size());
            return t7List.stream().map(T7::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 计算面系数平均值
        double avgCoefficient = t7List.stream()
                .mapToDouble(T7::getFaceCoefficient)
                .average()
                .orElse(0.0);

        log.debug("T7表面系数平均值：{}", avgCoefficient);

        // 找到大于平均值且最接近的位置（如果有多个相同值，选择最后一个）
        int avgIndex = -1;
        double minDiff = Double.MAX_VALUE;
        for (int i = 0; i < t7List.size(); i++) {
            double coefficient = t7List.get(i).getFaceCoefficient();
            // 只考虑大于平均值的值
            if (coefficient > avgCoefficient) {
                double diff = coefficient - avgCoefficient;
                if (diff <= minDiff) { // 使用 <= 来选择最后一个相同值
                minDiff = diff;
                avgIndex = i;
            }
        }
        }

        // 如果没有找到大于平均值的，则取最大值
        if (avgIndex == -1) {
            avgIndex = 0; // T7表按面系数降序排列，第一个就是最大值
            log.debug("未找到大于平均值的位置，使用最大值位置：{}，面系数：{}", avgIndex, t7List.get(avgIndex).getFaceCoefficient());
        } else {
            log.debug("找到大于平均值且最接近的位置：{}，面系数：{}", avgIndex, t7List.get(avgIndex).getFaceCoefficient());
        }

        // 向上5个，向下4个，共10个
        int startIndex = Math.max(0, avgIndex - 5);
        int endIndex = Math.min(t7List.size() - 1, avgIndex + 4);
        
        // 确保总共10个数字
        while (endIndex - startIndex + 1 < 10 && (startIndex > 0 || endIndex < t7List.size() - 1)) {
            if (startIndex > 0) {
                startIndex--;
            }
            if (endIndex < t7List.size() - 1 && endIndex - startIndex + 1 < 10) {
                endIndex++;
            }
        }

        List<Integer> candidates = new ArrayList<>();
        for (int i = startIndex; i <= endIndex && candidates.size() < 10; i++) {
            candidates.add(t7List.get(i).getSlaveBallNumber());
        }

        // 处理边界位置的相同面系数情况（包含边界本身参与比较，并带回退规则）
        List<Integer> result = handleT7BoundaryConflicts(t7List, candidates, startIndex, endIndex);

        log.debug("T7中位算法选择范围：[{}, {}]，共{}个球", startIndex, endIndex, result.size());
        return result;
    }

    /**
     * 低位算法：从T7表取面系数最小值向上第3-13个球共10个球号
     */
    private List<Integer> getLowLevelBallsFromT7(List<T7> t7List) {
        if (t7List.size() < 13) {
            log.warn("T7表数据不足13条，实际{}条", t7List.size());
            return t7List.stream().map(T7::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 从最后13个开始，取第3-13个（即倒数第12到倒数第2个）
        int startIndex = Math.max(0, t7List.size() - 12);
        int endIndex = t7List.size() - 2;
        
        List<Integer> candidates = new ArrayList<>();
        for (int i = startIndex; i <= endIndex; i++) {
            candidates.add(t7List.get(i).getSlaveBallNumber());
        }

        // 处理边界位置的相同面系数情况
        List<Integer> result = handleT7BoundaryConflicts(t7List, candidates, startIndex, endIndex);

        log.debug("T7低位算法选择范围：[{}, {}]，共{}个球", startIndex, endIndex, result.size());
        return result;
    }

    /**
     * 处理T7表边界位置的面系数冲突
     */
    private List<Integer> handleT7BoundaryConflicts(List<T7> t7List, List<Integer> candidates, 
                                                   int startIndex, int endIndex) {
        List<Integer> result = new ArrayList<>(candidates);
        
        if (result.size() >= 10) {
            // 检查第1个位置（最大面系数）的冲突：包含边界本身
            Double firstCoefficient = t7List.get(startIndex).getFaceCoefficient();
            List<T7> candidatesForFirst = new ArrayList<>();
            candidatesForFirst.add(t7List.get(startIndex)); // 边界本身
            
            // 找出第一个位置上面（如果存在）的所有相同面系数的记录
            for (int i = 0; i < startIndex; i++) {
                if (t7List.get(i).getFaceCoefficient().equals(firstCoefficient)) {
                    candidatesForFirst.add(t7List.get(i));
                }
            }

            if (candidatesForFirst.size() > 1) {
                log.debug("第1位及其上面有{}个相同面系数的候选：{}", candidatesForFirst.size(), 
                    candidatesForFirst.stream().map(T7::getSlaveBallNumber).collect(Collectors.toList()));
                
                Integer bestBall = selectBestBallWithFallback(
                    candidatesForFirst.stream().map(T7::getSlaveBallNumber).collect(Collectors.toList())
                );
                result.set(0, bestBall);
            }

            // 检查第10个位置（最小面系数）的冲突：包含边界本身
            Double lastCoefficient = t7List.get(endIndex).getFaceCoefficient();
            List<T7> candidatesForLast = new ArrayList<>();
            candidatesForLast.add(t7List.get(endIndex)); // 边界本身
            
            // 找出第10个位置下面（如果存在）的所有相同面系数的记录
            for (int i = endIndex + 1; i < t7List.size(); i++) {
                if (t7List.get(i).getFaceCoefficient().equals(lastCoefficient)) {
                    candidatesForLast.add(t7List.get(i));
                }
            }

            if (candidatesForLast.size() > 1) {
                log.debug("第10位及其下面有{}个相同面系数的候选：{}", candidatesForLast.size(), 
                    candidatesForLast.stream().map(T7::getSlaveBallNumber).collect(Collectors.toList()));
                
                Integer bestBall = selectBestBallWithFallback(
                    candidatesForLast.stream().map(T7::getSlaveBallNumber).collect(Collectors.toList())
                );
                result.set(result.size() - 1, bestBall);
            }
        }

        return result.subList(0, Math.min(10, result.size()));
    }

    /**
     * 从T3表获取指定主球的26个从球号（按线系数从大到小排列）
     */
    private List<Integer> getTop26FromT3(Integer masterBallNumber) {
        log.debug("从T3表查询主球{}的线系数数据，取前26个", masterBallNumber);
        
        // 查询指定主球的所有数据，按线系数降序排列
        QueryWrapper<T3> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("masterBallNumber", masterBallNumber)
                   .orderByDesc("lineCoefficient")
                   .orderByAsc("slaveBallNumber"); // 线系数相同时按从球号升序
        
        List<T3> t3List = t3Mapper.selectList(queryWrapper);
        
        if (t3List.isEmpty()) {
            log.warn("T3表中主球{}没有数据", masterBallNumber);
            return new ArrayList<>();
        }

        if (t3List.size() < 26) {
            log.warn("T3表数据不足26条，实际{}条", t3List.size());
            return t3List.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 获取前25个
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < 25; i++) {
            result.add(t3List.get(i).getSlaveBallNumber());
        }

        // 处理第26个位置：检查是否有相同的线系数
        Double targetCoefficient = t3List.get(25).getLineCoefficient();
        List<T3> candidatesFor26th = new ArrayList<>();
        
        // 找出所有线系数等于第26个位置线系数的记录
        for (int i = 25; i < t3List.size(); i++) {
            if (t3List.get(i).getLineCoefficient().equals(targetCoefficient)) {
                candidatesFor26th.add(t3List.get(i));
            } else {
                break; // 线系数不同，停止查找
            }
        }

        if (candidatesFor26th.size() == 1) {
            // 只有一个候选，直接添加
            result.add(candidatesFor26th.get(0).getSlaveBallNumber());
        } else {
            // 有多个候选，通过history_top_100表的点系数排序
            log.debug("第26位有{}个相同线系数的候选：{}", candidatesFor26th.size(), 
                candidatesFor26th.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList()));
            
            Integer bestBall = selectBestBallFromHistoryTop100(
                candidatesFor26th.stream().map(T3::getSlaveBallNumber).collect(Collectors.toList())
            );
            result.add(bestBall);
        }

        log.debug("T3表主球{}最终选择的26个从球：{}", masterBallNumber, result);
        return result;
    }

    /**
     * 从history_top_100表获取前3个球号（按点系数排序）
     */
    private List<Integer> getTop3FromHistoryTop100() {
        log.debug("从history_top_100表获取前3个球号");
        
        // 查询前3个点系数最高的球号
        QueryWrapper<HistoryTop100> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("pointCoefficient")
                   .orderByAsc("ballNumber") // 点系数相同时按球号升序
                   .last("LIMIT 3"); // 限制只返回前3条记录
        
        List<HistoryTop100> top100List = historyTop100Mapper.selectList(queryWrapper);
        
        if (top100List.size() < 3) {
            log.warn("history_top_100表数据不足3条，实际{}条", top100List.size());
        }

        List<Integer> result = top100List.stream()
                .map(HistoryTop100::getBallNumber)
                .collect(Collectors.toList());

        log.debug("history_top_100表前3个球号：{}", result);
        return result;
    }

    /**
     * 从T4表获取指定主球的10个从球号
     * 根据不同级别使用不同的选择策略
     * @param masterBallNumber 主球号
     * @param level 级别：H-高位，M-中位，L-低位
     */
    private List<Integer> getTop10FromT4(Integer masterBallNumber, String level) {
        log.debug("从T4表查询主球{}的线系数数据，级别：{}", masterBallNumber, level);
        
        // 查询指定主球的所有数据，按线系数降序排列
        QueryWrapper<T4> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("masterBallNumber", masterBallNumber)
                   .orderByDesc("lineCoefficient")
                   .orderByAsc("slaveBallNumber"); // 线系数相同时按从球号升序
        
        List<T4> t4List = t4Mapper.selectList(queryWrapper);
        
        if (t4List.isEmpty()) {
            log.warn("T4表中主球{}没有数据", masterBallNumber);
            return new ArrayList<>();
        }

        List<Integer> result = new ArrayList<>();
        
        switch (level) {
            case "H":
                // 高位：取线系数最大的前10个
                result = getHighLevelBallsFromT4For10(t4List);
                break;
            case "M":
                // 中位：取线系数平均值向上6个向下4个的10个球号
                result = getMiddleLevelBallsFromT4For10(t4List);
                break;
            case "L":
                // 低位：取线系数最小值向上第4-14个球的10个球号
                result = getLowLevelBallsFromT4For10(t4List);
                break;
        }

        log.debug("T4表主球{}{}级别最终选择的{}个从球：{}", masterBallNumber, level, result.size(), result);
        return result;
    }

    /**
     * 高位算法：从T4表取线系数最大的前10个
     */
    private List<Integer> getHighLevelBallsFromT4For10(List<T4> t4List) {
        if (t4List.size() < 10) {
            log.warn("T4表数据不足10条，实际{}条", t4List.size());
            return t4List.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 获取前9个
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < 9; i++) {
            result.add(t4List.get(i).getSlaveBallNumber());
        }

        // 处理第10个位置：检查是否有相同的线系数
        Double targetCoefficient = t4List.get(9).getLineCoefficient();
        List<T4> candidatesFor10th = new ArrayList<>();
        
        // 找出所有线系数等于第10个位置线系数的记录
        for (int i = 9; i < t4List.size(); i++) {
            if (t4List.get(i).getLineCoefficient().equals(targetCoefficient)) {
                candidatesFor10th.add(t4List.get(i));
            } else {
                break; // 线系数不同，停止查找
            }
        }

        if (candidatesFor10th.size() == 1) {
            // 只有一个候选，直接添加
            result.add(candidatesFor10th.get(0).getSlaveBallNumber());
        } else {
            // 有多个候选，通过history_top_100表的点系数排序
            log.debug("第10位有{}个相同线系数的候选：{}", candidatesFor10th.size(), 
                candidatesFor10th.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList()));
            
            Integer bestBall = selectBestBallFromHistoryTop100(
                candidatesFor10th.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList())
            );
            result.add(bestBall);
        }

        log.debug("T4高位算法最终选择的10个球：{}", result);
        return result;
    }

    /**
     * 中位算法：从T4表取线系数平均值向上5个向下5个的10个球号
     */
    private List<Integer> getMiddleLevelBallsFromT4For10(List<T4> t4List) {
        if (t4List.size() < 10) {
            log.warn("T4表数据不足10条，实际{}条", t4List.size());
            return t4List.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 计算线系数平均值
        double avgCoefficient = t4List.stream()
                .mapToDouble(T4::getLineCoefficient)
                .average()
                .orElse(0.0);

        log.debug("T4表线系数平均值：{}", avgCoefficient);

        // 找到大于平均值且最接近的位置（如果有多个相同值，选择最后一个）
        int avgIndex = -1;
        double minDiff = Double.MAX_VALUE;
        for (int i = 0; i < t4List.size(); i++) {
            double coefficient = t4List.get(i).getLineCoefficient();
            // 只考虑大于平均值的值
            if (coefficient > avgCoefficient) {
                double diff = coefficient - avgCoefficient;
                if (diff <= minDiff) { // 使用 <= 来选择最后一个相同值
                minDiff = diff;
                avgIndex = i;
            }
        }
        }

        // 如果没有找到大于平均值的，则取最大值
        if (avgIndex == -1) {
            avgIndex = 0; // T4表按线系数降序排列，第一个就是最大值
            log.debug("未找到大于平均值的位置，使用最大值位置：{}，线系数：{}", avgIndex, t4List.get(avgIndex).getLineCoefficient());
        } else {
            log.debug("找到大于平均值且最接近的位置：{}，线系数：{}", avgIndex, t4List.get(avgIndex).getLineCoefficient());
        }

        // 向上5个，向下5个，共10个
        int startIndex = Math.max(0, avgIndex - 5);
        int endIndex = Math.min(t4List.size() - 1, avgIndex + 4);
        
        // 确保总共10个数字
        while (endIndex - startIndex + 1 < 10 && (startIndex > 0 || endIndex < t4List.size() - 1)) {
            if (startIndex > 0) {
                startIndex--;
            }
            if (endIndex < t4List.size() - 1 && endIndex - startIndex + 1 < 10) {
                endIndex++;
            }
        }

        List<Integer> candidates = new ArrayList<>();
        for (int i = startIndex; i <= endIndex && candidates.size() < 10; i++) {
            candidates.add(t4List.get(i).getSlaveBallNumber());
        }

        // 处理边界位置的相同线系数情况
        List<Integer> result = handleT4BoundaryConflictsFor10(t4List, candidates, startIndex, endIndex);

        log.debug("T4中位算法选择范围：[{}, {}]，共{}个球", startIndex, endIndex, result.size());
        return result;
    }

    /**
     * 低位算法：从T4表取线系数最小值向上第4-14个球的10个球号
     */
    private List<Integer> getLowLevelBallsFromT4For10(List<T4> t4List) {
        if (t4List.size() < 14) {
            log.warn("T4表数据不足14条，实际{}条", t4List.size());
            return t4List.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 从最后14个开始，取第4-14个（即倒数第11到倒数第1个）
        int startIndex = Math.max(0, t4List.size() - 11);
        int endIndex = t4List.size() - 1;
        
        List<Integer> candidates = new ArrayList<>();
        for (int i = startIndex; i <= endIndex; i++) {
            candidates.add(t4List.get(i).getSlaveBallNumber());
        }

        // 处理边界位置的相同线系数情况
        List<Integer> result = handleT4BoundaryConflictsFor10(t4List, candidates, startIndex, endIndex);

        log.debug("T4低位算法选择范围：[{}, {}]，共{}个球", startIndex, endIndex, result.size());
        return result;
    }

    /**
     * 统计数字出现频率，返回频率最高的前10个数字
     * 如果频次相同的球号超过10个，使用T7表面系数进行二次筛选
     */
    private List<Integer> getTop10ByFrequency(List<Integer> allNumbers, List<BallWithCoefficient> step1BallsWithCoefficients) {
        log.debug("统计{}个数字的出现频率", allNumbers.size());
        
        // 统计频率
        Map<Integer, Integer> frequencyMap = new HashMap<>();
        for (Integer number : allNumbers) {
            frequencyMap.put(number, frequencyMap.getOrDefault(number, 0) + 1);
        }

        log.debug("数字频率统计：{}", frequencyMap);

        // 按频率分组
        Map<Integer, List<Integer>> frequencyGroups = new TreeMap<>(Collections.reverseOrder());
        for (Map.Entry<Integer, Integer> entry : frequencyMap.entrySet()) {
            Integer frequency = entry.getValue();
            Integer ballNumber = entry.getKey();
            
            frequencyGroups.computeIfAbsent(frequency, k -> new ArrayList<>()).add(ballNumber);
        }

        log.debug("按频率分组：{}", frequencyGroups);

        List<Integer> result = new ArrayList<>();
        
        // 按频率从高到低处理
        for (Map.Entry<Integer, List<Integer>> group : frequencyGroups.entrySet()) {
            Integer frequency = group.getKey();
            List<Integer> balls = group.getValue();
            
            // 对同频率的球号按数字升序排序
            Collections.sort(balls);
            
            log.info("频率{}的球号：{}", frequency, balls);
            
            // 检查加入这组球号后是否会超过10个
            if (result.size() + balls.size() <= 10) {
                // 不会超过10个，直接添加所有球号
                result.addAll(balls);
                log.info("直接添加{}个球号，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过10个：先按第一步T7面系数排序；如仍有边界相同面系数，继续按 100期 → 历史 → 随机 的回退规则，只在边界同系数的集合内决策
                int remainingSlots = 10 - result.size();
                log.info("需要从{}个频率相同的球号中选择{}个，先按第一步T7面系数进行筛选（找不到则按0处理）", balls.size(), remainingSlots);

                // 构建 step1 球号->面系数 映射（找不到则为0）
                Map<Integer, Double> num2Coeff = new HashMap<>();
                if (step1BallsWithCoefficients != null) {
                    for (BallWithCoefficient bwc : step1BallsWithCoefficients) {
                        num2Coeff.put(bwc.getBallNumber(), bwc.getCoefficient());
                    }
                }

                // 先按面系数降序，再按球号升序
                List<Integer> sortedByT7 = new ArrayList<>(balls);
                sortedByT7.sort((a, b) -> {
                    double ca = num2Coeff.getOrDefault(a, 0.0);
                    double cb = num2Coeff.getOrDefault(b, 0.0);
                    if (Double.compare(cb, ca) != 0) {
                        return Double.compare(cb, ca);
                    }
                    return Integer.compare(a, b);
                });

                // 如果正好能取齐，直接取
                if (sortedByT7.size() <= remainingSlots) {
                    result.addAll(sortedByT7);
                    log.info("按T7面系数后正好取齐，选择：{}", sortedByT7);
                    break;
                }

                // 存在边界相同面系数：提取阈值、拆分高于阈值与阈值相等的集合
                double thresholdCoeff = num2Coeff.getOrDefault(sortedByT7.get(remainingSlots - 1), 0.0);
                List<Integer> higherThanThreshold = new ArrayList<>();
                List<Integer> equalThresholdGroup = new ArrayList<>();
                for (Integer bn : sortedByT7) {
                    double c = num2Coeff.getOrDefault(bn, 0.0);
                    if (c > thresholdCoeff) {
                        higherThanThreshold.add(bn);
                    } else if (Double.compare(c, thresholdCoeff) == 0) {
                        equalThresholdGroup.add(bn);
                    }
                }

                // 先无条件加入高于阈值的
                result.addAll(higherThanThreshold);

                int stillNeed = remainingSlots - higherThanThreshold.size();
                if (stillNeed > 0) {
                    // 仅在阈值相同的集合内，先用100期取该组的最高点系数组（可能少于需要的数量则直接全部加入），
                    // 若仍未满足，再对剩余候选使用history_top精筛；仍无法区分则随机补齐。
                    List<Integer> tieResolved = selectByTop100ThenHistoryTop(equalThresholdGroup, stillNeed);
                    result.addAll(tieResolved);
                    log.info("边界同面系数通过100期→历史→随机补齐：{}", tieResolved);
                }

                // 已达到8个，结束
                break;
            }
            
            // 如果已经有8个，结束
            if (result.size() >= 8) {
                break;
            }
        }

        log.info("频率统计最终结果（共{}个）：{}", result.size(), result);
        
        // 打印详细的频率信息
        for (int i = 0; i < result.size(); i++) {
            Integer ballNumber = result.get(i);
            Integer frequency = frequencyMap.get(ballNumber);
            log.info("第{}位：数字{}，出现{}次", i + 1, ballNumber, frequency);
        }

        return result;
    }

    /**
     * 统计频率并获取前10个（带筛选过程说明）
     * @param allNumbers 所有数字
     * @param step1BallsWithCoefficients 第一步T7面系数数据
     * @return 包含结果和筛选过程说明的VO对象
     */
    private FollowBallAnalysisResultVO getTop10ByFrequencyWithProcess(List<Integer> allNumbers, List<BallWithCoefficient> step1BallsWithCoefficients) {
        log.debug("统计{}个数字的出现频率", allNumbers.size());
        
        // 统计频率
        Map<Integer, Integer> frequencyMap = new HashMap<>();
        for (Integer number : allNumbers) {
            frequencyMap.put(number, frequencyMap.getOrDefault(number, 0) + 1);
        }

        log.debug("数字频率统计：{}", frequencyMap);

        // 按频率分组
        Map<Integer, List<Integer>> frequencyGroups = new TreeMap<>(Collections.reverseOrder());
        for (Map.Entry<Integer, Integer> entry : frequencyMap.entrySet()) {
            Integer frequency = entry.getValue();
            Integer ballNumber = entry.getKey();
            
            frequencyGroups.computeIfAbsent(frequency, k -> new ArrayList<>()).add(ballNumber);
        }

        log.debug("按频率分组：{}", frequencyGroups);

        List<Integer> result = new ArrayList<>();
        List<Integer> directlySelectedBalls = new ArrayList<>();
        List<Integer> multiLevelCandidates = new ArrayList<>();
        List<Integer> secondaryFilteredBalls = new ArrayList<>();
        boolean needMultiLevelFiltering = false;
        StringBuilder processDescription = new StringBuilder();
        String detailedStepsInfo = "";
        
        // 按频率从高到低处理
        for (Map.Entry<Integer, List<Integer>> group : frequencyGroups.entrySet()) {
            Integer frequency = group.getKey();
            List<Integer> balls = group.getValue();
            
            // 对同频率的球号按数字升序排序
            Collections.sort(balls);
            
            log.info("频率{}的球号：{}", frequency, balls);
            
            // 检查加入这组球号后是否会超过10个
            if (result.size() + balls.size() <= 10) {
                // 不会超过10个，直接添加所有球号
                result.addAll(balls);
                directlySelectedBalls.addAll(balls);
                log.info("直接添加{}个球号，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过10个，需要多级筛选
                needMultiLevelFiltering = true;
                multiLevelCandidates.addAll(balls);
                
                int remainingSlots = 10 - result.size();
                log.info("需要从{}个频率相同的球号中选择{}个，先按第一步T7面系数进行筛选", balls.size(), remainingSlots);

                // 进行多级筛选
                FollowBallMultiLevelFilteringResult filteringResult = performFollowBallMultiLevelFilteringWithDetails(balls, remainingSlots, step1BallsWithCoefficients);
                result.addAll(filteringResult.getSelectedBalls());
                secondaryFilteredBalls = new ArrayList<>(filteringResult.getSelectedBalls());
                processDescription.append("筛选步骤：").append(filteringResult.getStepsDescription());
                detailedStepsInfo = filteringResult.getDetailedStepsInfo();

                // 已达到10个，结束
                break;
            }
            
            // 如果已经有10个，结束
            if (result.size() >= 10) {
                break;
            }
        }

        // 构建详细的过程描述
        StringBuilder detailedDescription = new StringBuilder();
        
        // 1. 频率分布描述 - 只包含实际参与筛选的球号
        List<Integer> allParticipatedBalls = new ArrayList<>();
        allParticipatedBalls.addAll(directlySelectedBalls);
        allParticipatedBalls.addAll(multiLevelCandidates);
        
        Map<Integer, List<Integer>> participatedFrequencyGroups = new LinkedHashMap<>();
        for (Integer ball : allParticipatedBalls) {
            Integer frequency = frequencyMap.get(ball);
            participatedFrequencyGroups.computeIfAbsent(frequency, k -> new ArrayList<>()).add(ball);
        }
        
        // 按频率从高到低排序
        Map<Integer, List<Integer>> sortedParticipatedFrequencyGroups = new TreeMap<>(Collections.reverseOrder());
        for (Map.Entry<Integer, List<Integer>> entry : participatedFrequencyGroups.entrySet()) {
            Integer frequency = entry.getKey();
            List<Integer> balls = new ArrayList<>(entry.getValue());
            Collections.sort(balls); // 球号按升序排序
            sortedParticipatedFrequencyGroups.put(frequency, balls);
        }
        
        detailedDescription.append("参与筛选的候选球号按频率分布为");
        detailedDescription.append(formatBallNumbersWithFrequency(sortedParticipatedFrequencyGroups));
        
        // 2. 筛选结果描述
        if (needMultiLevelFiltering) {
            detailedDescription.append("；无法直接筛选出前10个，其中");
            detailedDescription.append(formatBallNumbers(directlySelectedBalls));
            detailedDescription.append("直接入选，");
            detailedDescription.append(formatBallNumbers(multiLevelCandidates));
            detailedDescription.append("需要进行二次筛选，最终筛选出");
            detailedDescription.append(formatBallNumbers(secondaryFilteredBalls));
            detailedDescription.append("，组成前10个球号：");
            detailedDescription.append(formatBallNumbers(result));
            detailedDescription.append("。");
            detailedDescription.append(processDescription.toString());
            if (!detailedStepsInfo.isEmpty()) {
                detailedDescription.append(" ").append(detailedStepsInfo);
            }
        } else {
            detailedDescription.append("；直接按频率筛选出前10个球号：");
            detailedDescription.append(formatBallNumbers(result));
            detailedDescription.append("。筛选步骤：通过频率筛选确定所有球号，无需进行T7面系数筛选、百期排位、历史排位。");
        }

        return FollowBallAnalysisResultVO.builder()
                .result(result)
                .filteringProcess(detailedDescription.toString())
                .build();
    }

    /**
     * 根据T7表的面系数总和选择球号，支持多级筛选
     * @param candidateBalls 候选球号列表
     * @param selectCount 需要选择的数量
     * @return 选择的球号列表
     */
    private List<Integer> selectBallsByT7FaceCoefficient(List<Integer> candidateBalls, int selectCount) {
        log.info("开始根据T7表面系数总和选择球号，候选：{}，需要选择：{}", candidateBalls, selectCount);
        
        if (candidateBalls.size() <= selectCount) {
            log.info("候选球号数量不超过需要选择的数量，直接返回所有候选球号");
            return new ArrayList<>(candidateBalls);
        }
        
        // 第一级：计算每个球号作为主球时的面系数总和
        Map<Integer, Double> faceCoefficientSumMap = new HashMap<>();
        
        for (Integer ballNumber : candidateBalls) {
            QueryWrapper<T7> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("masterBallNumber", ballNumber);
            
            List<T7> t7List = t7Mapper.selectList(queryWrapper);
            
            double sum = t7List.stream()
                    .mapToDouble(T7::getFaceCoefficient)
                    .sum();
            
            faceCoefficientSumMap.put(ballNumber, sum);
            log.debug("球号{}作为主球的面系数总和：{}", ballNumber, sum);
        }

        // 按面系数总和分组
        Map<Double, List<Integer>> faceCoefficientGroups = new TreeMap<>(Collections.reverseOrder());
        for (Map.Entry<Integer, Double> entry : faceCoefficientSumMap.entrySet()) {
            Double sum = entry.getValue();
            Integer ballNumber = entry.getKey();
            faceCoefficientGroups.computeIfAbsent(sum, k -> new ArrayList<>()).add(ballNumber);
        }

        log.info("T7表面系数总和分组：{}", faceCoefficientGroups);

        List<Integer> result = new ArrayList<>();
        
        // 按面系数总和从高到低处理
        for (Map.Entry<Double, List<Integer>> group : faceCoefficientGroups.entrySet()) {
            Double faceSum = group.getKey();
            List<Integer> balls = group.getValue();
            
            log.info("面系数总和{}的球号：{}", faceSum, balls);
            
            // 检查加入这组球号后是否会超过selectCount个
            if (result.size() + balls.size() <= selectCount) {
                // 不会超过，直接添加所有球号（按球号升序排序）
                Collections.sort(balls);
                result.addAll(balls);
                log.info("直接添加{}个球号，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过，需要进一步筛选
                int remainingSlots = selectCount - result.size();
                log.info("需要从{}个面系数总和相同的球号中选择{}个，开始多级筛选", balls.size(), remainingSlots);
                
                List<Integer> selectedBalls = selectBallsByMultiLevelFilteringFor8(balls, remainingSlots);
                result.addAll(selectedBalls);
                
                log.info("通过多级筛选完成，最终选择：{}", selectedBalls);
                break; // 已经达到selectCount个，结束
            }
            
            // 如果已经有selectCount个，结束
            if (result.size() >= selectCount) {
                break;
            }
        }

        log.info("T7表面系数筛选最终结果（共{}个）：{}", result.size(), result);
        
        // 打印详细的面系数信息
        for (int i = 0; i < result.size(); i++) {
            Integer ballNumber = result.get(i);
            Double sum = faceCoefficientSumMap.get(ballNumber);
            log.info("第{}位：球号{}，面系数总和：{}", i + 1, ballNumber, sum);
        }

        return result;
    }

    /**
     * 多级筛选球号（8个球版本）：history_top_100 -> history_top -> 随机选择
     * @param candidateBalls 候选球号列表
     * @param selectCount 需要选择的数量
     * @return 选择的球号列表
     */
    private List<Integer> selectBallsByMultiLevelFilteringFor8(List<Integer> candidateBalls, int selectCount) {
        log.info("开始多级筛选（8个球版本），候选：{}，需要选择：{}", candidateBalls, selectCount);
        
        if (candidateBalls.size() <= selectCount) {
            log.info("候选球号数量不超过需要选择的数量，直接返回所有候选球号");
            return new ArrayList<>(candidateBalls);
        }
        
        // 第二级：使用history_top_100表的排名进行筛选
        List<Integer> filteredByTop100 = selectBallsByHistoryTop100Ranking(candidateBalls, selectCount);
        
        if (filteredByTop100.size() == selectCount) {
            log.info("通过history_top_100表筛选成功，结果：{}", filteredByTop100);
            return filteredByTop100;
        }
        
        // 第三级：使用history_top表的点系数进行筛选
        log.info("history_top_100表筛选后仍有{}个球号，继续使用history_top表筛选", filteredByTop100.size());
        List<Integer> filteredByTop = selectBallsByHistoryTopPointCoefficient(filteredByTop100, selectCount);
        
        if (filteredByTop.size() == selectCount) {
            log.info("通过history_top表筛选成功，结果：{}", filteredByTop);
            return filteredByTop;
        }
        
        // 第四级：随机选择
        log.info("history_top表筛选后仍有{}个球号，进行随机选择", filteredByTop.size());
        List<Integer> finalResult = selectBallsRandomly(filteredByTop, selectCount);
        
        log.info("随机选择完成，最终结果：{}", finalResult);
        return finalResult;
    }

    /**
     * 根据history_top_100表的排名筛选球号
     * @param candidateBalls 候选球号列表
     * @param selectCount 需要选择的数量
     * @return 筛选后的球号列表
     */
    private List<Integer> selectBallsByHistoryTop100Ranking(List<Integer> candidateBalls, int selectCount) {
        log.info("使用history_top_100表排名筛选球号，候选：{}，需要选择：{}", candidateBalls, selectCount);
        
        // 查询这些球号在history_top_100表中的点系数
        QueryWrapper<HistoryTop100> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("ballNumber", candidateBalls)
                   .orderByDesc("pointCoefficient")
                   .orderByAsc("ballNumber"); // 点系数相同时按球号升序
        
        List<HistoryTop100> top100List = historyTop100Mapper.selectList(queryWrapper);
        
        if (top100List.isEmpty()) {
            log.warn("候选球号{}在history_top_100表中未找到数据，按球号升序返回", candidateBalls);
            Collections.sort(candidateBalls);
            return candidateBalls.subList(0, Math.min(selectCount, candidateBalls.size()));
        }

        // 按点系数分组
        Map<Double, List<Integer>> pointCoefficientGroups = new LinkedHashMap<>();
        for (HistoryTop100 item : top100List) {
            Double pointCoefficient = item.getPointCoefficient();
            Integer ballNumber = item.getBallNumber();
            pointCoefficientGroups.computeIfAbsent(pointCoefficient, k -> new ArrayList<>()).add(ballNumber);
        }

        log.info("history_top_100表点系数分组：{}", pointCoefficientGroups);

        List<Integer> result = new ArrayList<>();
        
        // 按点系数从高到低处理
        for (Map.Entry<Double, List<Integer>> group : pointCoefficientGroups.entrySet()) {
            Double pointCoefficient = group.getKey();
            List<Integer> balls = group.getValue();
            
            log.info("点系数{}的球号：{}", pointCoefficient, balls);
            
            // 检查加入这组球号后是否会超过selectCount个
            if (result.size() + balls.size() <= selectCount) {
                // 不会超过，直接添加所有球号（按球号升序排序）
                Collections.sort(balls);
                result.addAll(balls);
                log.info("直接添加{}个球号，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过，返回所有相同点系数的球号，让下一层筛选来处理
                result.addAll(balls);
                log.info("点系数相同，返回所有相同点系数的球号：{}，让下一层筛选处理", balls);
                break;
            }
            
            // 如果已经有selectCount个，结束
            if (result.size() >= selectCount) {
                break;
            }
        }

        // 处理不在history_top_100表中的球号
        List<Integer> notInTop100 = new ArrayList<>(candidateBalls);
        notInTop100.removeAll(result);
        if (!notInTop100.isEmpty() && result.size() < selectCount) {
            int remainingSlots = selectCount - result.size();
            Collections.sort(notInTop100);
            int addCount = Math.min(remainingSlots, notInTop100.size());
            result.addAll(notInTop100.subList(0, addCount));
            log.info("添加不在history_top_100表中的{}个球号：{}", addCount, notInTop100.subList(0, addCount));
        }

        log.info("history_top_100表筛选结果：{}", result);
        return result;
    }

    /**
     * 根据history_top_100表的排名筛选球号（包含详细信息）
     * @param candidateBalls 候选球号列表
     * @param selectCount 需要选择的数量
     * @return 筛选结果和详细信息
     */
    private FilteringDetailResult selectBallsByHistoryTop100RankingWithDetails(List<Integer> candidateBalls, int selectCount) {
        log.info("使用history_top_100表排名筛选球号，候选：{}，需要选择：{}", candidateBalls, selectCount);
        
        // 查询这些球号在history_top_100表中的点系数
        QueryWrapper<HistoryTop100> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("ballNumber", candidateBalls)
                   .orderByDesc("pointCoefficient")
                   .orderByAsc("ballNumber"); // 点系数相同时按球号升序
        
        List<HistoryTop100> top100List = historyTop100Mapper.selectList(queryWrapper);
        
        if (top100List.isEmpty()) {
            log.warn("候选球号{}在history_top_100表中未找到数据，按球号升序返回", candidateBalls);
            Collections.sort(candidateBalls);
            return new FilteringDetailResult(candidateBalls.subList(0, Math.min(selectCount, candidateBalls.size())), "");
        }

        // 按点系数分组
        Map<Double, List<Integer>> pointCoefficientGroups = new LinkedHashMap<>();
        for (HistoryTop100 item : top100List) {
            Double pointCoefficient = item.getPointCoefficient();
            Integer ballNumber = item.getBallNumber();
            pointCoefficientGroups.computeIfAbsent(pointCoefficient, k -> new ArrayList<>()).add(ballNumber);
        }

        log.info("history_top_100表点系数分组：{}", pointCoefficientGroups);

        List<Integer> result = new ArrayList<>();
        StringBuilder detailInfo = new StringBuilder();
        
        // 先记录所有球号的排位详细信息
        for (Map.Entry<Double, List<Integer>> group : pointCoefficientGroups.entrySet()) {
            Double pointCoefficient = group.getKey();
            List<Integer> balls = group.getValue();
            Collections.sort(balls); // 按球号升序排序
            
            // 记录详细信息（显示所有参与筛选的球号）
            if (detailInfo.length() > 0) detailInfo.append("，");
            detailInfo.append(formatBallNumbers(balls)).append("（排位").append(String.format("%.2f", pointCoefficient)).append("）");
        }
        
        // 再按点系数从高到低筛选球号
        for (Map.Entry<Double, List<Integer>> group : pointCoefficientGroups.entrySet()) {
            Double pointCoefficient = group.getKey();
            List<Integer> balls = group.getValue();
            
            log.info("点系数{}的球号：{}", pointCoefficient, balls);
            
            // 检查加入这组球号后是否会超过selectCount个
            if (result.size() + balls.size() <= selectCount) {
                // 不会超过，直接添加所有球号（按球号升序排序）
                Collections.sort(balls);
                result.addAll(balls);
                log.info("直接添加{}个球号，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过，返回所有相同点系数的球号，让下一层筛选来处理
                result.addAll(balls);
                log.info("点系数相同，返回所有相同点系数的球号：{}，让下一层筛选处理", balls);
                break;
            }
            
            // 如果已经有selectCount个，结束
            if (result.size() >= selectCount) {
                break;
            }
        }

        // 处理不在history_top_100表中的球号
        List<Integer> notInTop100 = new ArrayList<>(candidateBalls);
        List<Integer> inTop100 = new ArrayList<>();
        for (HistoryTop100 item : top100List) {
            inTop100.add(item.getBallNumber());
        }
        notInTop100.removeAll(inTop100);
        
        // 先记录所有不在表中的球号的详细信息
        if (!notInTop100.isEmpty()) {
            Collections.sort(notInTop100);
            if (detailInfo.length() > 0) detailInfo.append("，");
            detailInfo.append(formatBallNumbers(notInTop100)).append("（无排位）");
        }
        
        // 再选择需要的球号加入结果
        if (!notInTop100.isEmpty() && result.size() < selectCount) {
            int remainingSlots = selectCount - result.size();
            int addCount = Math.min(remainingSlots, notInTop100.size());
            result.addAll(notInTop100.subList(0, addCount));
            
            log.info("添加不在history_top_100表中的{}个球号：{}", addCount, notInTop100.subList(0, addCount));
        }

        log.info("history_top_100表筛选结果：{}", result);
        return new FilteringDetailResult(result, detailInfo.toString());
    }

    /**
     * 根据history_top表的点系数筛选球号
     * 当点系数相同时，返回所有相同点系数的球号，让下一层筛选来处理
     */
    private List<Integer> selectBallsByHistoryTopPointCoefficient(List<Integer> candidateBalls, int selectCount) {
        log.info("使用history_top表点系数筛选球号，候选：{}，需要选择：{}", candidateBalls, selectCount);
        
        // 查询这些球号在history_top表中的点系数
        QueryWrapper<HistoryTop> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("ballNumber", candidateBalls)
                   .orderByDesc("pointCoefficient")
                   .orderByAsc("ballNumber"); // 点系数相同时按球号升序
        
        List<HistoryTop> historyTopList = historyTopMapper.selectList(queryWrapper);
        
        if (historyTopList.isEmpty()) {
            log.warn("候选球号{}在history_top表中未找到数据，按球号升序返回", candidateBalls);
            Collections.sort(candidateBalls);
            return candidateBalls.subList(0, Math.min(selectCount, candidateBalls.size()));
        }

        // 按点系数分组
        Map<Double, List<Integer>> pointCoefficientGroups = new LinkedHashMap<>();
        for (HistoryTop item : historyTopList) {
            Double pointCoefficient = item.getPointCoefficient();
            Integer ballNumber = item.getBallNumber();
            pointCoefficientGroups.computeIfAbsent(pointCoefficient, k -> new ArrayList<>()).add(ballNumber);
        }

        log.info("history_top表点系数分组：{}", pointCoefficientGroups);

        List<Integer> result = new ArrayList<>();
        
        // 按点系数从高到低处理
        for (Map.Entry<Double, List<Integer>> group : pointCoefficientGroups.entrySet()) {
            Double pointCoefficient = group.getKey();
            List<Integer> balls = group.getValue();
            
            log.info("点系数{}的球号：{}", pointCoefficient, balls);
            
            // 检查加入这组球号后是否会超过selectCount个
            if (result.size() + balls.size() <= selectCount) {
                // 不会超过，直接添加所有球号（按球号升序排序）
                Collections.sort(balls);
                result.addAll(balls);
                log.info("直接添加{}个球号，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过，返回所有相同点系数的球号，让下一层筛选来处理
                result.addAll(balls);
                log.info("点系数相同，返回所有相同点系数的球号：{}，让下一层筛选处理", balls);
                break;
            }
            
            // 如果已经有selectCount个，结束
            if (result.size() >= selectCount) {
                break;
            }
        }

        // 处理不在history_top表中的球号
        List<Integer> notInHistoryTop = new ArrayList<>(candidateBalls);
        notInHistoryTop.removeAll(result);
        if (!notInHistoryTop.isEmpty() && result.size() < selectCount) {
            int remainingSlots = selectCount - result.size();
            Collections.sort(notInHistoryTop);
            int addCount = Math.min(remainingSlots, notInHistoryTop.size());
            result.addAll(notInHistoryTop.subList(0, addCount));
            log.info("添加不在history_top表中的{}个球号：{}", addCount, notInHistoryTop.subList(0, addCount));
        }

        log.info("history_top表筛选结果：{}", result);
        return result;
    }

    /**
     * 根据history_top表的点系数筛选球号（包含详细信息）
     * @param candidateBalls 候选球号列表
     * @param selectCount 需要选择的数量
     * @return 筛选结果和详细信息
     */
    private FilteringDetailResult selectBallsByHistoryTopPointCoefficientWithDetails(List<Integer> candidateBalls, int selectCount) {
        log.info("使用history_top表点系数筛选球号，候选：{}，需要选择：{}", candidateBalls, selectCount);
        
        // 查询这些球号在history_top表中的点系数
        QueryWrapper<HistoryTop> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("ballNumber", candidateBalls)
                   .orderByDesc("pointCoefficient")
                   .orderByAsc("ballNumber"); // 点系数相同时按球号升序
        
        List<HistoryTop> historyTopList = historyTopMapper.selectList(queryWrapper);
        
        if (historyTopList.isEmpty()) {
            log.warn("候选球号{}在history_top表中未找到数据，按球号升序返回", candidateBalls);
            Collections.sort(candidateBalls);
            return new FilteringDetailResult(candidateBalls.subList(0, Math.min(selectCount, candidateBalls.size())), "");
        }

        // 按点系数分组
        Map<Double, List<Integer>> pointCoefficientGroups = new LinkedHashMap<>();
        for (HistoryTop item : historyTopList) {
            Double pointCoefficient = item.getPointCoefficient();
            Integer ballNumber = item.getBallNumber();
            pointCoefficientGroups.computeIfAbsent(pointCoefficient, k -> new ArrayList<>()).add(ballNumber);
        }

        log.info("history_top表点系数分组：{}", pointCoefficientGroups);

        List<Integer> result = new ArrayList<>();
        StringBuilder detailInfo = new StringBuilder();
        
        // 先记录所有球号的点系数详细信息
        for (Map.Entry<Double, List<Integer>> group : pointCoefficientGroups.entrySet()) {
            Double pointCoefficient = group.getKey();
            List<Integer> balls = group.getValue();
            Collections.sort(balls); // 按球号升序排序
            
            // 记录详细信息（显示所有参与筛选的球号）
            if (detailInfo.length() > 0) detailInfo.append("，");
            detailInfo.append(formatBallNumbers(balls)).append("（点系数").append(String.format("%.2f", pointCoefficient)).append("）");
        }
        
        // 处理不在history_top表中的球号的详细信息
        List<Integer> notInHistoryTop = new ArrayList<>(candidateBalls);
        List<Integer> inHistoryTop = new ArrayList<>();
        for (HistoryTop item : historyTopList) {
            inHistoryTop.add(item.getBallNumber());
        }
        notInHistoryTop.removeAll(inHistoryTop);
        
        // 先记录所有不在表中的球号的详细信息
        if (!notInHistoryTop.isEmpty()) {
            Collections.sort(notInHistoryTop);
            if (detailInfo.length() > 0) detailInfo.append("，");
            detailInfo.append(formatBallNumbers(notInHistoryTop)).append("（无历史数据）");
        }
        
        // 再按点系数从高到低筛选球号
        for (Map.Entry<Double, List<Integer>> group : pointCoefficientGroups.entrySet()) {
            Double pointCoefficient = group.getKey();
            List<Integer> balls = group.getValue();
            
            log.info("点系数{}的球号：{}", pointCoefficient, balls);
            
            // 检查加入这组球号后是否会超过selectCount个
            if (result.size() + balls.size() <= selectCount) {
                // 不会超过，直接添加所有球号（按球号升序排序）
                Collections.sort(balls);
                result.addAll(balls);
                log.info("直接添加{}个球号，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过，返回所有相同点系数的球号，让下一层筛选来处理
                result.addAll(balls);
                log.info("点系数相同，返回所有相同点系数的球号：{}，让下一层筛选处理", balls);
                break;
            }
            
            // 如果已经有selectCount个，结束
            if (result.size() >= selectCount) {
                break;
            }
        }

        // 再选择需要的不在表中的球号加入结果
        if (!notInHistoryTop.isEmpty() && result.size() < selectCount) {
            int remainingSlots = selectCount - result.size();
            int addCount = Math.min(remainingSlots, notInHistoryTop.size());
            result.addAll(notInHistoryTop.subList(0, addCount));
            
            log.info("添加不在history_top表中的{}个球号：{}", addCount, notInHistoryTop.subList(0, addCount));
        }

        log.info("history_top表筛选结果：{}", result);
        return new FilteringDetailResult(result, detailInfo.toString());
    }

    /**
     * 随机选择球号
     * @param candidateBalls 候选球号列表
     * @param selectCount 需要选择的数量
     * @return 随机选择的球号列表
     */
    private List<Integer> selectBallsRandomly(List<Integer> candidateBalls, int selectCount) {
        log.info("随机选择球号，候选：{}，需要选择：{}", candidateBalls, selectCount);
        
        if (candidateBalls.size() <= selectCount) {
            log.info("候选球号数量不超过需要选择的数量，直接返回所有候选球号");
            return new ArrayList<>(candidateBalls);
        }
        
        // 创建候选球号的副本并打乱顺序
        List<Integer> shuffledBalls = new ArrayList<>(candidateBalls);
        Collections.shuffle(shuffledBalls, new Random(System.currentTimeMillis()));
        
        // 选择前selectCount个
        List<Integer> result = shuffledBalls.subList(0, selectCount);
        
        // 按球号升序排序结果
        Collections.sort(result);
        
        log.info("随机选择结果：{}", result);
        return result;
    }

    /**
     * 仅在一组同T7系数的并列候选中，按“先100期再历史最后随机”的规则补齐目标数量。
     * 行为符合“100期先拿能拿的直接加入；不足时再从剩余里用history_top补齐；仍不足再随机补齐”。
     */
    private List<Integer> selectByTop100ThenHistoryTop(List<Integer> candidateBalls, int needCount) {
        if (candidateBalls == null || candidateBalls.isEmpty() || needCount <= 0) {
            return new ArrayList<>();
        }

        // 在同T7系数的候选中，先用100期逐组加入；若某组加入会超出，则该组整体作为未决集合，交给下一层(history_top)
        QueryWrapper<HistoryTop100> query = new QueryWrapper<>();
        query.in("ballNumber", candidateBalls)
             .orderByDesc("pointCoefficient")
             .orderByAsc("ballNumber");
        List<HistoryTop100> top100List = historyTop100Mapper.selectList(query);

        List<Integer> selectedByTop100 = new ArrayList<>();
        List<Integer> undecidedGroup = new ArrayList<>();

        if (top100List != null && !top100List.isEmpty()) {
            // 分组（保持顺序）
            Map<Double, List<Integer>> groups = new LinkedHashMap<>();
            for (HistoryTop100 item : top100List) {
                groups.computeIfAbsent(item.getPointCoefficient(), k -> new ArrayList<>())
                      .add(item.getBallNumber());
            }
            for (Map.Entry<Double, List<Integer>> entry : groups.entrySet()) {
                List<Integer> groupBalls = entry.getValue();
                if (selectedByTop100.size() + groupBalls.size() <= needCount) {
                    selectedByTop100.addAll(groupBalls);
                } else {
                    undecidedGroup.addAll(groupBalls);
                    break; // 只把“超出的那一组”交给下一层
                }
                if (selectedByTop100.size() == needCount) {
                    break;
                }
            }
        }

        if (selectedByTop100.size() >= needCount) {
            return selectedByTop100.subList(0, needCount);
        }

        // 还有缺口：仅对未决集合使用 history_top；若100期没有数据，则把所有候选作为未决集合
        int stillNeed = needCount - selectedByTop100.size();
        List<Integer> forHistoryTop;
        if (!undecidedGroup.isEmpty()) {
            forHistoryTop = new ArrayList<>(undecidedGroup);
        } else if (top100List == null || top100List.isEmpty()) {
            // 100期没有任何记录，则全部交给历史表
            forHistoryTop = new ArrayList<>(candidateBalls);
        } else {
            // 100期记录不足但没有触发未决组（例如所有组总和仍小于needCount），则用剩余候选补齐
            forHistoryTop = new ArrayList<>(candidateBalls);
            forHistoryTop.removeAll(selectedByTop100);
        }

        List<Integer> byTop = forHistoryTop.isEmpty() ? new ArrayList<>()
                : selectBallsByHistoryTopPointCoefficient(forHistoryTop, stillNeed);

        List<Integer> collected = new ArrayList<>();
        collected.addAll(selectedByTop100);
        if (!byTop.isEmpty()) {
            if (byTop.size() > stillNeed) {
                byTop = byTop.subList(0, stillNeed);
            }
            collected.addAll(byTop);
        }

        // 仍不足则在“未被选中的集合”中随机补齐
        int gap = needCount - collected.size();
        if (gap > 0) {
            List<Integer> left = new ArrayList<>(candidateBalls);
            left.removeAll(collected);
            if (!left.isEmpty()) {
                List<Integer> rnd = selectBallsRandomly(left, Math.min(gap, left.size()));
                collected.addAll(rnd);
            }
        }

        return collected;
    }

    /**
     * 蓝球分析算法主方法
     * @param level 高位/中位/低位标识 (H/M/L)
     * @param predictedRedBalls 6个预测的红球号码
     * @param predictedBlueBalls 2个预测的蓝球号码
     * @param lastRedBalls 6个上期红球号码
     * @param lastBlueBall 1个上期蓝球号码
     * @return 分析结果：出现频率最高的前4个蓝球号码
     */
    public List<Integer> blueBallAnalysis(String level, List<Integer> predictedRedBalls, 
                                         List<Integer> predictedBlueBalls, List<Integer> lastRedBalls, 
                                         Integer lastBlueBall) {
        log.info("开始蓝球分析算法，级别：{}，预测红球：{}，预测蓝球：{}，上期红球：{}，上期蓝球：{}", 
                level, predictedRedBalls, predictedBlueBalls, lastRedBalls, lastBlueBall);
        
        // 验证输入参数
        validateBlueBallAnalysisParams(level, predictedRedBalls, predictedBlueBalls, lastRedBalls, lastBlueBall);

        List<Integer> allNumbers = new ArrayList<>();

        // 第一步：处理上期6个红球，每个红球从T6表获取12个蓝球号码
        log.info("第一步：处理上期6个红球，从T6表获取蓝球号码");
        for (int i = 0; i < lastRedBalls.size(); i++) {
            Integer redBall = lastRedBalls.get(i);
            log.info("处理第{}个上期红球：{}", i + 1, redBall);
            
            List<Integer> ballNumbers = getTop12FromT6(redBall);
            allNumbers.addAll(ballNumbers);
            
            log.info("上期红球{}从T6表获取到{}个蓝球号码：{}", redBall, ballNumbers.size(), ballNumbers);
        }

        // 第二步：处理预测的6个红球，每个红球从T8表获取5个蓝球号码
        log.info("第二步：处理预测的6个红球，从T8表获取蓝球号码，使用{}级别算法", level);
        for (int i = 0; i < predictedRedBalls.size(); i++) {
            Integer redBall = predictedRedBalls.get(i);
            log.info("处理第{}个预测红球：{}", i + 1, redBall);

            List<Integer> ballNumbers = getSimpleTop5FromT8ByLevel(redBall, level);
            allNumbers.addAll(ballNumbers);

            // 额外记录这5个蓝球的面系数（与 fallowBallAnalysis 第一步风格一致）
            List<BallWithCoefficient> t8Coeffs = getT8CoefficientsFor(redBall, ballNumbers);
            log.info("=== 预测红球{} 对应T8面系数（共{}个） ===", redBall, t8Coeffs.size());
            for (int k = 0; k < t8Coeffs.size(); k++) {
                log.info("第{}个：{}", k + 1, t8Coeffs.get(k).toString());
            }
            log.info("=== 记录结束 ===");

            log.info("预测红球{}从T8表获取到{}个蓝球号码：{}", redBall, ballNumbers.size(), ballNumbers);
        }

        // 第三步：从blue_history_top_100获取前2个蓝球号码
        log.info("第三步：从blue_history_top_100获取前2个蓝球号码");
        List<Integer> top2BlueNumbers = getTop2FromBlueHistoryTop100();
        allNumbers.addAll(top2BlueNumbers);
        log.info("从blue_history_top_100获取到{}个蓝球号码：{}", top2BlueNumbers.size(), top2BlueNumbers);

        // 第四步：添加预测的2个蓝球号码
        log.info("第四步：添加预测的2个蓝球号码：{}", predictedBlueBalls);
        allNumbers.addAll(predictedBlueBalls);

        // 第五步：用上期蓝球从T5表获取12个蓝球号码（含毛边处理）
        log.info("第五步：用上期蓝球{}从T5表获取12个蓝球号码", lastBlueBall);
        List<Integer> blueNumbers = getTop12FromT5(lastBlueBall);
        allNumbers.addAll(blueNumbers);
        log.info("蓝球{}获取到{}个数字：{}", lastBlueBall, blueNumbers.size(), blueNumbers);

        log.info("总共收集到{}个蓝球号码", allNumbers.size());

        // 汇总第二步收集的T8面系数，供第六步同频二次筛选使用
        List<BallWithCoefficient> step2BlueT8Coefficients = new ArrayList<>();
        // 由于上面的循环内使用了临时变量 t8Coeffs，这里重新按预测红球聚合一次，确保完整
        for (Integer redBall : predictedRedBalls) {
            List<Integer> tmpBalls = getSimpleTop5FromT8ByLevel(redBall, level);
            step2BlueT8Coefficients.addAll(getT8CoefficientsFor(redBall, tmpBalls));
        }

        // 第六步：统计频率并获取前4个（同频时使用第2步收集的T8系数求和作为第一级筛选）
        List<Integer> result = getSimpleTop4ByFrequency(allNumbers, step2BlueT8Coefficients);
        
        log.info("蓝球分析算法完成，结果：{}", result);
        return result;
    }

    /**
     * 蓝球分析算法主方法（带筛选过程说明）
     * @param level 高位/中位/低位标识 (H/M/L)
     * @param predictedRedBalls 6个预测的红球号码
     * @param predictedBlueBalls 2个预测的蓝球号码
     * @param lastRedBalls 6个上期红球号码
     * @param lastBlueBall 1个上期蓝球号码
     * @return 分析结果：出现频率最高的前4个蓝球号码及筛选过程说明
     */
    public BlueBallAnalysisResultVO blueBallAnalysisWithProcess(String level, List<Integer> predictedRedBalls, 
                                         List<Integer> predictedBlueBalls, List<Integer> lastRedBalls, 
                                         Integer lastBlueBall) {
        log.info("开始蓝球分析算法（带过程说明），级别：{}，预测红球：{}，预测蓝球：{}，上期红球：{}，上期蓝球：{}", 
                level, predictedRedBalls, predictedBlueBalls, lastRedBalls, lastBlueBall);
        
        // 验证输入参数
        validateBlueBallAnalysisParams(level, predictedRedBalls, predictedBlueBalls, lastRedBalls, lastBlueBall);

        List<Integer> allNumbers = new ArrayList<>();

        // 第一步：处理上期6个红球，每个红球从T6表获取12个蓝球号码
        log.info("第一步：处理上期6个红球，从T6表获取蓝球号码");
        for (int i = 0; i < lastRedBalls.size(); i++) {
            Integer redBall = lastRedBalls.get(i);
            log.info("处理第{}个上期红球：{}", i + 1, redBall);
            
            List<Integer> ballNumbers = getTop12FromT6(redBall);
            allNumbers.addAll(ballNumbers);
            
            log.info("上期红球{}从T6表获取到{}个蓝球号码：{}", redBall, ballNumbers.size(), ballNumbers);
        }

        // 第二步：处理预测的6个红球，每个红球从T8表获取5个蓝球号码
        log.info("第二步：处理预测的6个红球，从T8表获取蓝球号码，使用{}级别算法", level);
        for (int i = 0; i < predictedRedBalls.size(); i++) {
            Integer redBall = predictedRedBalls.get(i);
            log.info("处理第{}个预测红球：{}", i + 1, redBall);

            List<Integer> ballNumbers = getSimpleTop5FromT8ByLevel(redBall, level);
            allNumbers.addAll(ballNumbers);

            // 额外记录这5个蓝球的面系数（与 fallowBallAnalysis 第一步风格一致）
            List<BallWithCoefficient> t8Coeffs = getT8CoefficientsFor(redBall, ballNumbers);
            log.info("=== 预测红球{} 对应T8面系数（共{}个） ===", redBall, t8Coeffs.size());
            for (int k = 0; k < t8Coeffs.size(); k++) {
                log.info("第{}个：{}", k + 1, t8Coeffs.get(k).toString());
            }
            log.info("=== 记录结束 ===");

            log.info("预测红球{}从T8表获取到{}个蓝球号码：{}", redBall, ballNumbers.size(), ballNumbers);
        }

        // 第三步：从blue_history_top_100获取前2个蓝球号码
        log.info("第三步：从blue_history_top_100获取前2个蓝球号码");
        List<Integer> top2BlueNumbers = getTop2FromBlueHistoryTop100();
        allNumbers.addAll(top2BlueNumbers);
        log.info("从blue_history_top_100获取到{}个蓝球号码：{}", top2BlueNumbers.size(), top2BlueNumbers);

        // 第四步：添加预测的2个蓝球号码
        log.info("第四步：添加预测的2个蓝球号码：{}", predictedBlueBalls);
        allNumbers.addAll(predictedBlueBalls);

        // 第五步：用上期蓝球从T5表获取12个蓝球号码（含毛边处理）
        log.info("第五步：用上期蓝球{}从T5表获取12个蓝球号码", lastBlueBall);
        List<Integer> blueNumbers = getTop12FromT5(lastBlueBall);
        allNumbers.addAll(blueNumbers);
        log.info("蓝球{}获取到{}个数字：{}", lastBlueBall, blueNumbers.size(), blueNumbers);

        log.info("总共收集到{}个蓝球号码", allNumbers.size());

        // 汇总第二步收集的T8面系数，供第六步同频二次筛选使用
        List<BallWithCoefficient> step2BlueT8Coefficients = new ArrayList<>();
        // 由于上面的循环内使用了临时变量 t8Coeffs，这里重新按预测红球聚合一次，确保完整
        for (Integer redBall : predictedRedBalls) {
            List<Integer> tmpBalls = getSimpleTop5FromT8ByLevel(redBall, level);
            step2BlueT8Coefficients.addAll(getT8CoefficientsFor(redBall, tmpBalls));
        }

        // 第六步：统计频率并获取前4个（带筛选过程说明）
        BlueBallAnalysisResultVO result = getSimpleTop4ByFrequencyWithProcess(allNumbers, step2BlueT8Coefficients);
        
        log.info("蓝球分析算法（带过程说明）完成，结果：{}，过程说明：{}", result.getResult(), result.getFilteringProcess());
        return result;
    }

    /**
     * 验证蓝球分析算法的输入参数
     */
    private void validateBlueBallAnalysisParams(String level, List<Integer> predictedRedBalls, 
                                               List<Integer> predictedBlueBalls, List<Integer> lastRedBalls, 
                                               Integer lastBlueBall) {
        // 验证级别
        if (!Arrays.asList("H", "M", "L").contains(level)) {
            throw new IllegalArgumentException("级别必须为H、M或L");
        }
        
        // 验证预测红球
        if (predictedRedBalls == null || predictedRedBalls.size() != 6) {
            throw new IllegalArgumentException("预测红球数量必须为6个");
        }
        for (Integer ball : predictedRedBalls) {
            if (ball == null || ball < 1 || ball > 33) {
                throw new IllegalArgumentException("预测红球号码必须在1-33范围内，实际：" + ball);
            }
        }
        if (predictedRedBalls.stream().distinct().count() != predictedRedBalls.size()) {
            throw new IllegalArgumentException("预测红球号码不能重复");
        }
        
        // 验证预测蓝球
        if (predictedBlueBalls == null || predictedBlueBalls.size() != 2) {
            throw new IllegalArgumentException("预测蓝球数量必须为2个");
        }
        for (Integer ball : predictedBlueBalls) {
            if (ball == null || ball < 1 || ball > 16) {
                throw new IllegalArgumentException("预测蓝球号码必须在1-16范围内，实际：" + ball);
            }
        }
        if (predictedBlueBalls.stream().distinct().count() != predictedBlueBalls.size()) {
            throw new IllegalArgumentException("预测蓝球号码不能重复");
        }
        
        // 验证上期红球
        if (lastRedBalls == null || lastRedBalls.size() != 6) {
            throw new IllegalArgumentException("上期红球数量必须为6个");
        }
        for (Integer ball : lastRedBalls) {
            if (ball == null || ball < 1 || ball > 33) {
                throw new IllegalArgumentException("上期红球号码必须在1-33范围内，实际：" + ball);
            }
        }
        if (lastRedBalls.stream().distinct().count() != lastRedBalls.size()) {
            throw new IllegalArgumentException("上期红球号码不能重复");
        }
        
        // 验证上期蓝球
        if (lastBlueBall == null || lastBlueBall < 1 || lastBlueBall > 16) {
            throw new IllegalArgumentException("上期蓝球号码必须在1-16范围内，实际：" + lastBlueBall);
        }
        
        log.debug("蓝球分析算法参数验证通过");
    }

    /**
     * 从T6表获取指定红球对应的12个蓝球号码（线系数最大的12个）
     * @param masterBallNumber 主球号（红球）
     * @return 12个蓝球号码列表
     */
    private List<Integer> getTop12FromT6(Integer masterBallNumber) {
        log.debug("从T6表查询主球{}的线系数数据", masterBallNumber);
        
        // 查询指定主球的所有数据，按线系数降序排列
        QueryWrapper<T6> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("masterBallNumber", masterBallNumber)
                   .orderByDesc("lineCoefficient")
                   .orderByAsc("slaveBallNumber"); // 线系数相同时按从球号升序
        
        List<T6> t6List = t6Mapper.selectList(queryWrapper);
        
        if (t6List.isEmpty()) {
            log.warn("T6表中主球{}没有数据", masterBallNumber);
            return new ArrayList<>();
        }

        if (t6List.size() < 12) {
            log.warn("T6表数据不足12条，实际{}条", t6List.size());
            return t6List.stream().map(T6::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 获取前11个
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < 11; i++) {
            result.add(t6List.get(i).getSlaveBallNumber());
        }

        // 处理第12个位置：检查是否有相同的线系数
        Double targetCoefficient = t6List.get(11).getLineCoefficient();
        List<T6> candidatesFor12th = new ArrayList<>();
        
        // 找出所有线系数等于第12个位置线系数的记录
        for (int i = 11; i < t6List.size(); i++) {
            if (t6List.get(i).getLineCoefficient().equals(targetCoefficient)) {
                candidatesFor12th.add(t6List.get(i));
            } else {
                break; // 线系数不同，停止查找
            }
        }

        if (candidatesFor12th.size() == 1) {
            // 只有一个候选，直接添加
            result.add(candidatesFor12th.get(0).getSlaveBallNumber());
        } else {
            // 有多个候选，通过blue_history_top_100表的点系数排序
            log.debug("第12位有{}个相同线系数的候选：{}", candidatesFor12th.size(), 
                candidatesFor12th.stream().map(T6::getSlaveBallNumber).collect(Collectors.toList()));
            
            Integer bestBall = selectBestBallFromBlueHistoryTop100(
                candidatesFor12th.stream().map(T6::getSlaveBallNumber).collect(Collectors.toList())
            );
            result.add(bestBall);
        }

        log.debug("T6表主球{}最终选择的{}个从球：{}", masterBallNumber, result.size(), result);
        return result;
    }

    /**
     * 从blue_history_top_100表中选择点系数最高的球号
     * @param candidates 候选球号列表
     * @return 点系数最高的球号
     */
    private Integer selectBestBallFromBlueHistoryTop100(List<Integer> candidates) {
        log.debug("从blue_history_top_100表中选择最佳球号，候选：{}", candidates);
        
        if (candidates.isEmpty()) {
            throw new IllegalArgumentException("候选球号列表不能为空");
        }
        
        if (candidates.size() == 1) {
            return candidates.get(0);
        }
        
        // 查询这些球号在blue_history_top_100表中的点系数
        QueryWrapper<BlueHistoryTop100> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("ballNumber", candidates)
                   .orderByDesc("pointCoefficient")
                   .orderByAsc("ballNumber"); // 点系数相同时按球号升序
        
        List<BlueHistoryTop100> top100List = blueHistoryTop100Mapper.selectList(queryWrapper);
        
        if (top100List.isEmpty()) {
            log.warn("候选球号{}在blue_history_top_100表中未找到数据，返回最小球号", candidates);
            return Collections.min(candidates);
        }
        
        // 返回点系数最高的球号
        Integer bestBall = top100List.get(0).getBallNumber();
        log.debug("选择的最佳球号：{}，点系数：{}", bestBall, top100List.get(0).getPointCoefficient());
        
        return bestBall;
    }

    /**
     * 蓝球多级回退选择：先用 blue_history_top_100，再用 blue_history_top，最后随机
     */
    private List<Integer> selectBlueBallsWithFallback(List<Integer> candidates, int needCount) {
        if (candidates == null || candidates.isEmpty() || needCount <= 0) {
            return new ArrayList<>();
        }
        if (candidates.size() <= needCount) {
            return new ArrayList<>(candidates);
        }

        // 先用 blue_history_top_100
        QueryWrapper<BlueHistoryTop100> top100Query = new QueryWrapper<>();
        top100Query.in("ballNumber", candidates)
                   .orderByDesc("pointCoefficient")
                   .orderByAsc("ballNumber");
        List<BlueHistoryTop100> top100List = blueHistoryTop100Mapper.selectList(top100Query);
        
        if (top100List != null && top100List.size() >= needCount) {
            return top100List.subList(0, needCount).stream()
                    .map(BlueHistoryTop100::getBallNumber)
                    .collect(Collectors.toList());
        }

        // 不足，继续用 blue_history_top
        List<Integer> selectedBy100 = top100List == null ? new ArrayList<>() 
                : top100List.stream().map(BlueHistoryTop100::getBallNumber).collect(Collectors.toList());
        List<Integer> remaining = new ArrayList<>(candidates);
        remaining.removeAll(selectedBy100);
        int stillNeed = needCount - selectedBy100.size();

        QueryWrapper<BlueHistoryTop> topQuery = new QueryWrapper<>();
        topQuery.in("ballNumber", remaining)
                .orderByDesc("pointCoefficient")
                .orderByAsc("ballNumber");
        List<BlueHistoryTop> topList = blueHistoryTopMapper.selectList(topQuery);
        
        List<Integer> selectedByTop = topList == null ? new ArrayList<>() 
                : topList.stream().limit(stillNeed).map(BlueHistoryTop::getBallNumber).collect(Collectors.toList());

        List<Integer> result = new ArrayList<>();
        result.addAll(selectedBy100);
        result.addAll(selectedByTop);

        // 仍不足则随机补齐
        int gap = needCount - result.size();
        if (gap > 0) {
            List<Integer> left = new ArrayList<>(candidates);
            left.removeAll(result);
            if (!left.isEmpty()) {
                Collections.shuffle(left);
                result.addAll(left.subList(0, Math.min(gap, left.size())));
            }
        }

        return result;
    }

    /**
     * 从T8表获取指定红球对应的5个蓝球号码（简化版本）
     * @param masterBallNumber 主球号（红球）
     * @param level 级别：H-高位，M-中位，L-低位
     * @return 5个蓝球号码列表
     */
    private List<Integer> getSimpleTop5FromT8ByLevel(Integer masterBallNumber, String level) {
        log.debug("从T8表查询主球{}的面系数数据，级别：{}", masterBallNumber, level);
        
        // 查询指定主球的所有数据，按面系数降序排列
        QueryWrapper<T8> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("masterBallNumber", masterBallNumber)
                   .orderByDesc("faceCoefficient")
                   .orderByAsc("slaveBallNumber"); // 面系数相同时按从球号升序
        
        List<T8> t8List = t8Mapper.selectList(queryWrapper);
        
        if (t8List.isEmpty()) {
            log.warn("T8表中主球{}没有数据", masterBallNumber);
            return new ArrayList<>();
        }

        List<Integer> result = new ArrayList<>();
        int size = t8List.size();

        switch (level) {
            case "H": {
                // 高位：系数最大值向下5个；第5个位置毛边：向下查找相同面系数，使用blue_history_top_100比较
                if (size < 5) {
                    log.warn("T8表数据不足5条，实际{}条", size);
                    return t8List.stream().map(T8::getSlaveBallNumber).collect(Collectors.toList());
                }

                // 前4个直接取
                for (int i = 0; i < 4; i++) {
                    result.add(t8List.get(i).getSlaveBallNumber());
                }

                // 处理第5个位置的毛边
                Double fifthCoeff = t8List.get(4).getFaceCoefficient();
                List<T8> candidatesFor5th = new ArrayList<>();
                candidatesFor5th.add(t8List.get(4));
                for (int i = 5; i < size; i++) {
                    if (t8List.get(i).getFaceCoefficient().equals(fifthCoeff)) {
                        candidatesFor5th.add(t8List.get(i));
                    } else {
                        break;
                    }
                }

                if (candidatesFor5th.size() == 1) {
                    result.add(candidatesFor5th.get(0).getSlaveBallNumber());
                } else {
                    log.debug("高位第5位有{}个相同面系数的候选：{}", candidatesFor5th.size(),
                        candidatesFor5th.stream().map(T8::getSlaveBallNumber).collect(Collectors.toList()));
                    Integer bestBall = selectBestBallFromBlueHistoryTop100(
                        candidatesFor5th.stream().map(T8::getSlaveBallNumber).collect(Collectors.toList())
                    );
                    result.add(bestBall);
                }
                break;
            }
            case "M": {
                // 中位：取比平均值大的、且最近的一个为中心，向上2个、向下2个，共5个；
                // 上面两个的第一个(中心上方最近的)毛边向上查找；下面两个的第二个(更小的)毛边向下查找；均用blue_history_top_100比较
                if (size < 5) {
                    log.warn("T8表数据不足5条，实际{}条", size);
                    return t8List.stream().map(T8::getSlaveBallNumber).collect(Collectors.toList());
                }

                double avg = t8List.stream().mapToDouble(T8::getFaceCoefficient).average().orElse(0.0);
                int avgIndex = -1;
                double minDiff = Double.MAX_VALUE;
                for (int i = 0; i < size; i++) {
                    double c = t8List.get(i).getFaceCoefficient();
                    if (c > avg) {
                        double diff = c - avg;
                        if (diff <= minDiff) { // 取最后一个最接近的
                            minDiff = diff;
                            avgIndex = i;
                        }
                    }
                }
                if (avgIndex == -1) {
                    avgIndex = 0;
                }

                int startIndex = Math.max(0, avgIndex - 2);
                int endIndex = Math.min(size - 1, avgIndex + 2);
                while (endIndex - startIndex + 1 < 5 && (startIndex > 0 || endIndex < size - 1)) {
                    if (startIndex > 0) startIndex--;
                    if (endIndex < size - 1 && endIndex - startIndex + 1 < 5) endIndex++;
                }

                for (int i = startIndex; i <= endIndex; i++) {
                    result.add(t8List.get(i).getSlaveBallNumber());
                }

                // 上面两个中的第一个（系数更大的那个）：索引 startIndex（即 avgIndex-2）
                int upFirstIndex = startIndex;
                if (upFirstIndex >= 0) {
                    Double coeff = t8List.get(upFirstIndex).getFaceCoefficient();
                    List<T8> candidatesUp = new ArrayList<>();
                    // 包含窗口内的这个位置本身
                    candidatesUp.add(t8List.get(upFirstIndex));
                    // 只向上（窗口之外）查找相同面系数，避免与窗口内其它位置重复
                    for (int i = upFirstIndex - 1; i >= 0; i--) {
                        if (t8List.get(i).getFaceCoefficient().equals(coeff)) {
                            candidatesUp.add(t8List.get(i));
                        } else {
                            break;
                        }
                    }
                    if (candidatesUp.size() > 1) {
                        log.debug("中位上方第一个位置存在{}个相同面系数候选：{}", candidatesUp.size(),
                            candidatesUp.stream().map(T8::getSlaveBallNumber).collect(Collectors.toList()));
                        Integer best = selectBestBallFromBlueHistoryTop100(
                            candidatesUp.stream().map(T8::getSlaveBallNumber).collect(Collectors.toList())
                        );
                        // 替换窗口内的第一个位置
                        result.set(0, best);
                    }
                }

                // 下面两个中的第二个（系数更小的那个）：索引 endIndex（即 avgIndex+2）
                int downSecondIndex = endIndex;
                if (downSecondIndex < size) {
                    Double coeff = t8List.get(downSecondIndex).getFaceCoefficient();
                    List<T8> candidatesDown = new ArrayList<>();
                    // 包含窗口内该位置
                    candidatesDown.add(t8List.get(downSecondIndex));
                    // 只向下（窗口之外）查找相同系数，避免与窗口内其它位置重复
                    for (int i = downSecondIndex + 1; i < size; i++) {
                        if (t8List.get(i).getFaceCoefficient().equals(coeff)) {
                            candidatesDown.add(t8List.get(i));
                        } else {
                            break;
                        }
                    }
                    if (candidatesDown.size() > 1) {
                        log.debug("中位下方第二个位置存在{}个相同面系数候选：{}", candidatesDown.size(),
                            candidatesDown.stream().map(T8::getSlaveBallNumber).collect(Collectors.toList()));
                        Integer best = selectBestBallFromBlueHistoryTop100(
                            candidatesDown.stream().map(T8::getSlaveBallNumber).collect(Collectors.toList())
                        );
                        // 替换窗口内的最后一个位置
                        result.set(result.size() - 1, best);
                    }
                }
                break;
            }
            case "L": {
                // 低位：取系数倒数第二个向上5个；窗口顶部和底部位置均有毛边：使用blue_history_top_100/blue_history_top多级回退比较
                if (size < 5) {
                    log.warn("T8表数据不足5条，实际{}条", size);
                    return t8List.stream().map(T8::getSlaveBallNumber).collect(Collectors.toList());
                }
                int endIndex = size - 2; // 倒数第二
                int startIndex = Math.max(0, endIndex - 4);

                // 先按基本规则选出5个位置的球号
                List<Integer> baseResult = new ArrayList<>();
                for (int i = startIndex; i <= endIndex; i++) {
                    baseResult.add(t8List.get(i).getSlaveBallNumber());
                }

                // 处理顶部位置（系数最大的）毛边：向上查找相同面系数
                Double topCoeff = t8List.get(startIndex).getFaceCoefficient();
                List<T8> candidatesTop = new ArrayList<>();
                candidatesTop.add(t8List.get(startIndex));
                for (int i = startIndex - 1; i >= 0; i--) {
                    if (t8List.get(i).getFaceCoefficient().equals(topCoeff)) {
                        candidatesTop.add(t8List.get(i));
                    } else {
                        break;
                    }
                }
                if (candidatesTop.size() > 1) {
                    log.debug("低位顶部位置存在{}个相同面系数候选：{}", candidatesTop.size(),
                        candidatesTop.stream().map(T8::getSlaveBallNumber).collect(Collectors.toList()));
                    List<Integer> topSelected = selectBlueBallsWithFallback(
                        candidatesTop.stream().map(T8::getSlaveBallNumber).collect(Collectors.toList()), 1
                    );
                    if (!topSelected.isEmpty()) {
                        baseResult.set(0, topSelected.get(0));
                    }
                }

                // 处理底部位置（系数最小的）毛边：向下查找相同面系数
                Double bottomCoeff = t8List.get(endIndex).getFaceCoefficient();
                List<T8> candidatesBottom = new ArrayList<>();
                candidatesBottom.add(t8List.get(endIndex));
                for (int i = endIndex + 1; i < size; i++) {
                    if (t8List.get(i).getFaceCoefficient().equals(bottomCoeff)) {
                        candidatesBottom.add(t8List.get(i));
                    } else {
                        break;
                    }
                }
                if (candidatesBottom.size() > 1) {
                    log.debug("低位底部位置存在{}个相同面系数候选：{}", candidatesBottom.size(),
                        candidatesBottom.stream().map(T8::getSlaveBallNumber).collect(Collectors.toList()));
                    List<Integer> bottomSelected = selectBlueBallsWithFallback(
                        candidatesBottom.stream().map(T8::getSlaveBallNumber).collect(Collectors.toList()), 1
                    );
                    if (!bottomSelected.isEmpty()) {
                        baseResult.set(baseResult.size() - 1, bottomSelected.get(0));
                    }
                }

                result = baseResult;
                break;
            }
        }

        log.debug("T8表主球{}{}级别最终选择的{}个从球：{}", masterBallNumber, level, result.size(), result);
        return result;
    }

    /**
     * 从blue_history_top_100表获取点系数最高的前2个蓝球号码
     * 如果第2个点系数不唯一，使用blue_history_top表进行二级筛选
     * @return 前2个蓝球号码列表
     */
    private List<Integer> getTop2FromBlueHistoryTop100() {
        log.debug("从blue_history_top_100表获取前2个蓝球号码");
        
        // 查询所有数据，按点系数降序排列
        QueryWrapper<BlueHistoryTop100> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("pointCoefficient")
                   .orderByAsc("ballNumber"); // 点系数相同时按球号升序
        
        List<BlueHistoryTop100> top100List = blueHistoryTop100Mapper.selectList(queryWrapper);
        
        if (top100List.isEmpty()) {
            log.warn("blue_history_top_100表中没有数据");
            return new ArrayList<>();
        }
        
        if (top100List.size() == 1) {
            List<Integer> result = Arrays.asList(top100List.get(0).getBallNumber());
            log.debug("blue_history_top_100表只有1条数据：{}", result);
            return result;
        }
        
        List<Integer> result = new ArrayList<>();
        
        // 第1个直接取点系数最高的
        result.add(top100List.get(0).getBallNumber());
        log.debug("第1个蓝球号码：{}，点系数：{}", top100List.get(0).getBallNumber(), top100List.get(0).getPointCoefficient());
        
        // 处理第2个位置：检查是否有相同的点系数
        Double targetCoefficient = top100List.get(1).getPointCoefficient();
        List<BlueHistoryTop100> candidatesFor2nd = new ArrayList<>();
        
        // 找出所有点系数等于第2个位置点系数的记录
        for (int i = 1; i < top100List.size(); i++) {
            if (top100List.get(i).getPointCoefficient().equals(targetCoefficient)) {
                candidatesFor2nd.add(top100List.get(i));
            } else {
                break; // 点系数不同，停止查找
            }
        }
        
        if (candidatesFor2nd.size() == 1) {
            // 只有一个候选，直接添加
            result.add(candidatesFor2nd.get(0).getBallNumber());
            log.debug("第2个蓝球号码：{}，点系数：{}", candidatesFor2nd.get(0).getBallNumber(), candidatesFor2nd.get(0).getPointCoefficient());
        } else {
            // 有多个候选，通过blue_history_top表的点系数排序
            log.debug("第2位有{}个相同点系数的候选：{}", candidatesFor2nd.size(), 
                candidatesFor2nd.stream().map(BlueHistoryTop100::getBallNumber).collect(Collectors.toList()));
            
            Integer bestBall = selectBestBallFromBlueHistoryTop(
                candidatesFor2nd.stream().map(BlueHistoryTop100::getBallNumber).collect(Collectors.toList())
            );
            result.add(bestBall);
            log.debug("通过blue_history_top表筛选的第2个蓝球号码：{}", bestBall);
        }
        
        log.debug("从blue_history_top_100表最终获取到{}个蓝球号码：{}", result.size(), result);
        return result;
    }

    /**
     * 从T5表获取指定蓝球对应的5个蓝球号码（简化版本）
     * @param masterBallNumber 主球号（蓝球）
     * @param level 级别：H-高位，M-中位，L-低位
     * @return 5个蓝球号码列表
     */
    private List<Integer> getSimpleTop5FromT5ByLevel(Integer masterBallNumber, String level) {
        log.debug("从T5表查询主球{}的线系数数据，级别：{}", masterBallNumber, level);
        
        // 查询指定主球的所有数据，按线系数降序排列
        QueryWrapper<T5> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("masterBallNumber", masterBallNumber)
                   .orderByDesc("lineCoefficient")
                   .orderByAsc("slaveBallNumber"); // 线系数相同时按从球号升序
        
        List<T5> t5List = t5Mapper.selectList(queryWrapper);
        
        if (t5List.isEmpty()) {
            log.warn("T5表中主球{}没有数据", masterBallNumber);
            return new ArrayList<>();
        }

        List<Integer> result = new ArrayList<>();
        int size = t5List.size();
        
        switch (level) {
            case "H":
                // 高位：取线系数最大的5个
                for (int i = 0; i < Math.min(5, size); i++) {
                    result.add(t5List.get(i).getSlaveBallNumber());
                }
                break;
            case "M":
                // 中位：取中间的5个
                int start = Math.max(0, (size - 5) / 2);
                for (int i = start; i < Math.min(start + 5, size); i++) {
                    result.add(t5List.get(i).getSlaveBallNumber());
                }
                break;
            case "L":
                // 低位：取最后5个（线系数最小的）
                int startLow = Math.max(0, size - 5);
                for (int i = startLow; i < size; i++) {
                    result.add(t5List.get(i).getSlaveBallNumber());
                }
                break;
        }

        log.debug("T5表主球{}{}级别最终选择的{}个从球：{}", masterBallNumber, level, result.size(), result);
        return result;
    }

    /**
     * 统计数字出现频率，返回频率最高的前4个蓝球号码
     * 如果频次相同的球号超过4个，使用多级筛选机制
     */
    private List<Integer> getSimpleTop4ByFrequency(List<Integer> allNumbers, List<BallWithCoefficient> step2BlueT8Coefficients) {
        log.debug("统计{}个蓝球号码的出现频率", allNumbers.size());
        
        // 统计频率
        Map<Integer, Integer> frequencyMap = new HashMap<>();
        for (Integer number : allNumbers) {
            frequencyMap.put(number, frequencyMap.getOrDefault(number, 0) + 1);
        }

        log.debug("蓝球号码频率统计：{}", frequencyMap);

        // 按频率分组
        Map<Integer, List<Integer>> frequencyGroups = new TreeMap<>(Collections.reverseOrder());
        for (Map.Entry<Integer, Integer> entry : frequencyMap.entrySet()) {
            Integer frequency = entry.getValue();
            Integer ballNumber = entry.getKey();
            
            frequencyGroups.computeIfAbsent(frequency, k -> new ArrayList<>()).add(ballNumber);
        }

        log.debug("按频率分组：{}", frequencyGroups);

        List<Integer> result = new ArrayList<>();
        
        // 按频率从高到低处理
        for (Map.Entry<Integer, List<Integer>> group : frequencyGroups.entrySet()) {
            Integer frequency = group.getKey();
            List<Integer> balls = group.getValue();
            
            // 对同频率的球号按数字升序排序
            Collections.sort(balls);
            
            log.info("频率{}的蓝球号码：{}", frequency, balls);
            
            // 检查加入这组球号后是否会超过4个
            if (result.size() + balls.size() <= 4) {
                // 不会超过4个，直接添加所有球号
                result.addAll(balls);
                log.info("直接添加{}个蓝球号码，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过4个：第一级直接用“第二步收集的T8面系数求和”进行筛选
                int remainingSlots = 4 - result.size();
                log.info("需要从{}个频率相同的蓝球号码中选择{}个，使用第二步T8面系数求和进行筛选", balls.size(), remainingSlots);

                // 统计同频候选在step2BlueT8Coefficients中的面系数和（找不到视为0）
                Map<Integer, Double> sumMap = new HashMap<>();
                for (Integer b : balls) {
                    double sum = 0.0;
                    if (step2BlueT8Coefficients != null) {
                        for (BallWithCoefficient bwc : step2BlueT8Coefficients) {
                            if (b.equals(bwc.getBallNumber())) {
                                sum += bwc.getCoefficient();
                            }
                        }
                    }
                    sumMap.put(b, sum);
                }

                // 按“系数和降序、球号升序”排序，取前remainingSlots
                balls.sort((a, b) -> {
                    int cmp = Double.compare(sumMap.getOrDefault(b, 0.0), sumMap.getOrDefault(a, 0.0));
                    if (cmp != 0) return cmp;
                    return Integer.compare(a, b);
                });

                List<Integer> selectedBalls = balls.subList(0, Math.min(remainingSlots, balls.size()));
                result.addAll(selectedBalls);
                
                log.info("通过第二步T8面系数和筛选完成，最终选择：{}", selectedBalls);
                break; // 已经达到4个，结束
            }
            
            // 如果已经有4个，结束
            if (result.size() >= 4) {
                break;
            }
        }

        log.info("频率统计最终结果（共{}个）：{}", result.size(), result);
        
        // 打印详细的频率信息
        for (int i = 0; i < result.size(); i++) {
            Integer ballNumber = result.get(i);
            Integer frequency = frequencyMap.get(ballNumber);
            log.info("第{}位：蓝球号码{}，出现{}次", i + 1, ballNumber, frequency);
        }

        return result;
    }

    /**
     * 统计频率并获取前4个蓝球号码（带筛选过程说明）
     * @param allNumbers 所有蓝球号码
     * @param step2BlueT8Coefficients 第二步T8面系数数据
     * @return 包含结果和筛选过程说明的VO对象
     */
    private BlueBallAnalysisResultVO getSimpleTop4ByFrequencyWithProcess(List<Integer> allNumbers, List<BallWithCoefficient> step2BlueT8Coefficients) {
        log.debug("统计{}个蓝球号码的出现频率", allNumbers.size());
        
        // 统计频率
        Map<Integer, Integer> frequencyMap = new HashMap<>();
        for (Integer number : allNumbers) {
            frequencyMap.put(number, frequencyMap.getOrDefault(number, 0) + 1);
        }

        log.debug("蓝球号码频率统计：{}", frequencyMap);

        // 按频率分组
        Map<Integer, List<Integer>> frequencyGroups = new TreeMap<>(Collections.reverseOrder());
        for (Map.Entry<Integer, Integer> entry : frequencyMap.entrySet()) {
            Integer frequency = entry.getValue();
            Integer ballNumber = entry.getKey();
            
            frequencyGroups.computeIfAbsent(frequency, k -> new ArrayList<>()).add(ballNumber);
        }

        log.debug("按频率分组：{}", frequencyGroups);

        List<Integer> result = new ArrayList<>();
        List<Integer> directlySelectedBalls = new ArrayList<>();
        List<Integer> multiLevelCandidates = new ArrayList<>();
        List<Integer> secondaryFilteredBalls = new ArrayList<>();
        boolean needMultiLevelFiltering = false;
        StringBuilder processDescription = new StringBuilder();
        String detailedStepsInfo = "";
        
        // 构建参与筛选的球号频率分布描述
        List<Integer> allParticipatedBalls = new ArrayList<>();
        Map<Integer, List<Integer>> participatedFrequencyGroups = new LinkedHashMap<>();
        
        // 按频率从高到低处理
        for (Map.Entry<Integer, List<Integer>> group : frequencyGroups.entrySet()) {
            Integer frequency = group.getKey();
            List<Integer> balls = group.getValue();
            
            // 对同频率的球号按数字升序排序
            Collections.sort(balls);
            
            log.info("频率{}的蓝球号码：{}", frequency, balls);
            
            // 检查加入这组球号后是否会超过4个
            if (result.size() + balls.size() <= 4) {
                // 不会超过4个，直接添加所有球号
                result.addAll(balls);
                directlySelectedBalls.addAll(balls);
                allParticipatedBalls.addAll(balls);
                log.info("直接添加{}个蓝球号码，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过4个，需要多级筛选
                needMultiLevelFiltering = true;
                multiLevelCandidates.addAll(balls);
                allParticipatedBalls.addAll(balls);
                
                int remainingSlots = 4 - result.size();
                log.info("需要从{}个频率相同的蓝球号码中选择{}个，使用第二步T8面系数求和进行筛选", balls.size(), remainingSlots);

                // 进行多级筛选
                BlueBallMultiLevelFilteringResult filteringResult = performBlueBallMultiLevelFilteringWithDetails(balls, remainingSlots, step2BlueT8Coefficients);
                result.addAll(filteringResult.getSelectedBalls());
                secondaryFilteredBalls = new ArrayList<>(filteringResult.getSelectedBalls());
                processDescription.append("筛选步骤：").append(filteringResult.getStepsDescription());
                detailedStepsInfo = filteringResult.getDetailedStepsInfo();

                // 已达到4个，结束
                break;
            }
            
            // 如果已经有4个，结束
            if (result.size() >= 4) {
                break;
            }
        }

        // 构建参与筛选的频率分布
        for (Integer ball : allParticipatedBalls) {
            Integer frequency = frequencyMap.get(ball);
            participatedFrequencyGroups.computeIfAbsent(frequency, k -> new ArrayList<>()).add(ball);
        }
        
        // 按频率从高到低排序
        Map<Integer, List<Integer>> sortedParticipatedFrequencyGroups = new TreeMap<>(Collections.reverseOrder());
        for (Map.Entry<Integer, List<Integer>> entry : participatedFrequencyGroups.entrySet()) {
            Integer frequency = entry.getKey();
            List<Integer> balls = new ArrayList<>(entry.getValue());
            Collections.sort(balls); // 球号按升序排序
            sortedParticipatedFrequencyGroups.put(frequency, balls);
        }

        // 构建详细的过程描述
        StringBuilder detailedDescription = new StringBuilder();
        
        // 1. 频率分布描述 - 只包含实际参与筛选的球号
        detailedDescription.append("参与筛选的候选蓝球号码按频率分布为");
        detailedDescription.append(formatBallNumbersWithFrequency(sortedParticipatedFrequencyGroups));
        
        // 2. 筛选结果描述
        if (needMultiLevelFiltering) {
            detailedDescription.append("；无法直接筛选出前4个，其中");
            detailedDescription.append(formatBallNumbersComplete(directlySelectedBalls));
            detailedDescription.append("直接入选，");
            detailedDescription.append(formatBallNumbersComplete(multiLevelCandidates));
            detailedDescription.append("需要进行二次筛选，最终筛选出");
            detailedDescription.append(formatBallNumbersComplete(secondaryFilteredBalls));
            detailedDescription.append("，组成前4个蓝球号码：");
            detailedDescription.append(formatBallNumbersComplete(result));
            detailedDescription.append("。");
            detailedDescription.append(processDescription.toString());
            if (!detailedStepsInfo.isEmpty()) {
                detailedDescription.append(" ").append(detailedStepsInfo);
            }
        } else {
            detailedDescription.append("；直接按频率筛选出前4个蓝球号码：");
            detailedDescription.append(formatBallNumbersComplete(result));
            detailedDescription.append("。筛选步骤：通过频率筛选确定所有球号，无需进行T8面系数和筛选、百期排位、历史排位。");
        }

        return BlueBallAnalysisResultVO.builder()
                .result(result)
                .filteringProcess(detailedDescription.toString())
                .build();
    }

    /**
     * 从blue_history_top表中选择点系数最高的球号
     * @param candidates 候选球号列表
     * @return 点系数最高的球号
     */
    private Integer selectBestBallFromBlueHistoryTop(List<Integer> candidates) {
        log.debug("从blue_history_top表中选择最佳球号，候选：{}", candidates);
        
        if (candidates.isEmpty()) {
            throw new IllegalArgumentException("候选球号列表不能为空");
        }
        
        if (candidates.size() == 1) {
            return candidates.get(0);
        }
        
        // 查询这些球号在blue_history_top表中的点系数
        QueryWrapper<BlueHistoryTop> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("ballNumber", candidates)
                   .orderByDesc("pointCoefficient")
                   .orderByAsc("ballNumber"); // 点系数相同时按球号升序
        
        List<BlueHistoryTop> historyTopList = blueHistoryTopMapper.selectList(queryWrapper);
        
        if (historyTopList.isEmpty()) {
            log.warn("候选球号{}在blue_history_top表中未找到数据，返回最小球号", candidates);
            return Collections.min(candidates);
        }
        
        // 返回点系数最高的球号
        Integer bestBall = historyTopList.get(0).getBallNumber();
        log.debug("选择的最佳球号：{}，点系数：{}", bestBall, historyTopList.get(0).getPointCoefficient());
        
        return bestBall;
    }

    /**
     * 四级筛选蓝球号码：T8面系数 -> blue_history_top_100排名 -> blue_history_top点系数 -> 随机选择
     * @param candidateBalls 候选蓝球号码列表
     * @param selectCount 需要选择的数量
     * @return 选择的蓝球号码列表
     */
    private List<Integer> selectBallsByMultiLevelFilteringForBlue4(List<Integer> candidateBalls, int selectCount) {
        log.info("开始四级筛选蓝球号码，候选：{}，需要选择：{}", candidateBalls, selectCount);
        
        if (candidateBalls.size() <= selectCount) {
            log.info("候选蓝球号码数量不超过需要选择的数量，直接返回所有候选蓝球号码");
            return new ArrayList<>(candidateBalls);
        }
        
        // 第一级：使用T8表的面系数总和进行筛选
        List<Integer> filteredByT8 = selectBallsByT8FaceCoefficientSumForBlue4(candidateBalls, selectCount);
        
        if (filteredByT8.size() == selectCount) {
            log.info("通过T8表面系数筛选成功，结果：{}", filteredByT8);
            return filteredByT8;
        }
        
        // 第二级：使用blue_history_top_100表的排名进行筛选
        log.info("T8表筛选后仍有{}个蓝球号码，继续使用blue_history_top_100表筛选", filteredByT8.size());
        List<Integer> filteredByTop100 = selectBallsByBlueHistoryTop100RankingForBlue4(filteredByT8, selectCount);
        
        if (filteredByTop100.size() == selectCount) {
            log.info("通过blue_history_top_100表筛选成功，结果：{}", filteredByTop100);
            return filteredByTop100;
        }
        
        // 第三级：使用blue_history_top表的点系数进行筛选
        log.info("blue_history_top_100表筛选后仍有{}个蓝球号码，继续使用blue_history_top表筛选", filteredByTop100.size());
        List<Integer> filteredByTop = selectBallsByBlueHistoryTopPointCoefficientForBlue4(filteredByTop100, selectCount);
        
        if (filteredByTop.size() == selectCount) {
            log.info("通过blue_history_top表筛选成功，结果：{}", filteredByTop);
            return filteredByTop;
        }
        
        // 第四级：随机选择
        log.info("blue_history_top表筛选后仍有{}个蓝球号码，进行随机选择", filteredByTop.size());
        List<Integer> finalResult = selectBallsRandomlyForBlue4(filteredByTop, selectCount);
        
        log.info("随机选择完成，最终结果：{}", finalResult);
        return finalResult;
    }

    /**
     * 根据T8表的面系数总和选择蓝球号码（第一级筛选）
     * @param candidateBalls 候选蓝球号码列表
     * @param selectCount 需要选择的数量
     * @return 选择的蓝球号码列表
     */
    private List<Integer> selectBallsByT8FaceCoefficientSumForBlue4(List<Integer> candidateBalls, int selectCount) {
        log.info("开始根据T8表面系数总和选择蓝球号码，候选：{}，需要选择：{}", candidateBalls, selectCount);
        
        if (candidateBalls.size() <= selectCount) {
            log.info("候选蓝球号码数量不超过需要选择的数量，直接返回所有候选蓝球号码");
            return new ArrayList<>(candidateBalls);
        }
        
        // 计算每个蓝球号码作为主球时的面系数总和
        Map<Integer, Double> faceCoefficientSumMap = new HashMap<>();
        
        for (Integer ballNumber : candidateBalls) {
            QueryWrapper<T8> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("masterBallNumber", ballNumber);
            
            List<T8> t8List = t8Mapper.selectList(queryWrapper);
            
            double sum = t8List.stream()
                    .mapToDouble(T8::getFaceCoefficient)
                    .sum();
            
            faceCoefficientSumMap.put(ballNumber, sum);
            log.debug("蓝球号码{}作为主球的面系数总和：{}", ballNumber, sum);
        }

        // 按面系数总和分组
        Map<Double, List<Integer>> faceCoefficientGroups = new TreeMap<>(Collections.reverseOrder());
        for (Map.Entry<Integer, Double> entry : faceCoefficientSumMap.entrySet()) {
            Double sum = entry.getValue();
            Integer ballNumber = entry.getKey();
            faceCoefficientGroups.computeIfAbsent(sum, k -> new ArrayList<>()).add(ballNumber);
        }

        log.info("T8表面系数总和分组：{}", faceCoefficientGroups);

        List<Integer> result = new ArrayList<>();
        
        // 按面系数总和从高到低处理
        for (Map.Entry<Double, List<Integer>> group : faceCoefficientGroups.entrySet()) {
            Double faceSum = group.getKey();
            List<Integer> balls = group.getValue();
            
            log.info("面系数总和{}的蓝球号码：{}", faceSum, balls);
            
            // 检查加入这组球号后是否会超过selectCount个
            if (result.size() + balls.size() <= selectCount) {
                // 不会超过，直接添加所有球号（按球号升序排序）
                Collections.sort(balls);
                result.addAll(balls);
                log.info("直接添加{}个蓝球号码，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过，按球号升序选择需要的数量
                int remainingSlots = selectCount - result.size();
                Collections.sort(balls);
                result.addAll(balls.subList(0, remainingSlots));
                log.info("选择前{}个蓝球号码：{}", remainingSlots, balls.subList(0, remainingSlots));
                break;
            }
            
            // 如果已经有selectCount个，结束
            if (result.size() >= selectCount) {
                break;
            }
        }

        log.info("T8表面系数筛选结果：{}", result);
        return result;
    }

    /**
     * 根据blue_history_top_100表的点系数选择蓝球号码（第二级筛选）
     * @param candidateBalls 候选蓝球号码列表
     * @param selectCount 需要选择的数量
     * @return 选择的蓝球号码列表
     */
    private List<Integer> selectBallsByBlueHistoryTop100RankingForBlue4(List<Integer> candidateBalls, int selectCount) {
        log.info("使用blue_history_top_100表点系数筛选蓝球号码，候选：{}，需要选择：{}", candidateBalls, selectCount);
        
        // 查询这些球号在blue_history_top_100表中的点系数
        QueryWrapper<BlueHistoryTop100> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("ballNumber", candidateBalls)
                   .orderByDesc("pointCoefficient")
                   .orderByAsc("ballNumber"); // 点系数相同时按球号升序
        
        List<BlueHistoryTop100> top100List = blueHistoryTop100Mapper.selectList(queryWrapper);
        
        if (top100List.isEmpty()) {
            log.warn("候选球号{}在blue_history_top_100表中未找到数据，按球号升序返回", candidateBalls);
            Collections.sort(candidateBalls);
            return candidateBalls.subList(0, Math.min(selectCount, candidateBalls.size()));
        }

        // 按点系数分组
        Map<Double, List<Integer>> pointCoefficientGroups = new TreeMap<>(Collections.reverseOrder());
        for (BlueHistoryTop100 item : top100List) {
            Double pointCoefficient = item.getPointCoefficient();
            Integer ballNumber = item.getBallNumber();
            pointCoefficientGroups.computeIfAbsent(pointCoefficient, k -> new ArrayList<>()).add(ballNumber);
        }

        log.info("blue_history_top_100表点系数分组：{}", pointCoefficientGroups);

        List<Integer> result = new ArrayList<>();
        
        // 按点系数从高到低处理
        for (Map.Entry<Double, List<Integer>> group : pointCoefficientGroups.entrySet()) {
            Double pointCoefficient = group.getKey();
            List<Integer> balls = group.getValue();
            
            log.info("点系数{}的蓝球号码：{}", pointCoefficient, balls);
            
            // 检查加入这组球号后是否会超过selectCount个
            if (result.size() + balls.size() <= selectCount) {
                // 不会超过，直接添加所有球号（按球号升序排序）
                Collections.sort(balls);
                result.addAll(balls);
                log.info("直接添加{}个蓝球号码，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过，按球号升序选择需要的数量
                int remainingSlots = selectCount - result.size();
                Collections.sort(balls);
                result.addAll(balls.subList(0, remainingSlots));
                log.info("选择前{}个蓝球号码：{}", remainingSlots, balls.subList(0, remainingSlots));
                break;
            }
            
            // 如果已经有selectCount个，结束
            if (result.size() >= selectCount) {
                break;
            }
        }

        // 处理不在blue_history_top_100表中的球号
        List<Integer> notInTop100 = new ArrayList<>(candidateBalls);
        notInTop100.removeAll(result);
        if (!notInTop100.isEmpty() && result.size() < selectCount) {
            int remainingSlots = selectCount - result.size();
            Collections.sort(notInTop100);
            int addCount = Math.min(remainingSlots, notInTop100.size());
            result.addAll(notInTop100.subList(0, addCount));
            log.info("添加不在blue_history_top_100表中的{}个蓝球号码：{}", addCount, notInTop100.subList(0, addCount));
        }

        log.info("blue_history_top_100表筛选结果：{}", result);
        return result;
    }

    /**
     * 根据blue_history_top表的点系数选择蓝球号码（第三级筛选）
     * @param candidateBalls 候选蓝球号码列表
     * @param selectCount 需要选择的数量
     * @return 选择的蓝球号码列表
     */
    private List<Integer> selectBallsByBlueHistoryTopPointCoefficientForBlue4(List<Integer> candidateBalls, int selectCount) {
        log.info("使用blue_history_top表点系数筛选蓝球号码，候选：{}，需要选择：{}", candidateBalls, selectCount);
        
        // 查询这些球号在blue_history_top表中的点系数
        QueryWrapper<BlueHistoryTop> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("ballNumber", candidateBalls)
                   .orderByDesc("pointCoefficient")
                   .orderByAsc("ballNumber"); // 点系数相同时按球号升序
        
        List<BlueHistoryTop> historyTopList = blueHistoryTopMapper.selectList(queryWrapper);
        
        if (historyTopList.isEmpty()) {
            log.warn("候选球号{}在blue_history_top表中未找到数据，按球号升序返回", candidateBalls);
            Collections.sort(candidateBalls);
            return candidateBalls.subList(0, Math.min(selectCount, candidateBalls.size()));
        }

        // 按点系数分组
        Map<Double, List<Integer>> pointCoefficientGroups = new TreeMap<>(Collections.reverseOrder());
        for (BlueHistoryTop item : historyTopList) {
            Double pointCoefficient = item.getPointCoefficient();
            Integer ballNumber = item.getBallNumber();
            pointCoefficientGroups.computeIfAbsent(pointCoefficient, k -> new ArrayList<>()).add(ballNumber);
        }

        log.info("blue_history_top表点系数分组：{}", pointCoefficientGroups);

        List<Integer> result = new ArrayList<>();
        
        // 按点系数从高到低处理
        for (Map.Entry<Double, List<Integer>> group : pointCoefficientGroups.entrySet()) {
            Double pointCoefficient = group.getKey();
            List<Integer> balls = group.getValue();
            
            log.info("点系数{}的蓝球号码：{}", pointCoefficient, balls);
            
            // 检查加入这组球号后是否会超过selectCount个
            if (result.size() + balls.size() <= selectCount) {
                // 不会超过，直接添加所有球号（按球号升序排序）
                Collections.sort(balls);
                result.addAll(balls);
                log.info("直接添加{}个蓝球号码，当前总数：{}", balls.size(), result.size());
            } else {
                // 会超过，按球号升序选择需要的数量
                int remainingSlots = selectCount - result.size();
                Collections.sort(balls);
                result.addAll(balls.subList(0, remainingSlots));
                log.info("选择前{}个蓝球号码：{}", remainingSlots, balls.subList(0, remainingSlots));
                break;
            }
            
            // 如果已经有selectCount个，结束
            if (result.size() >= selectCount) {
                break;
            }
        }

        // 处理不在blue_history_top表中的球号
        List<Integer> notInHistoryTop = new ArrayList<>(candidateBalls);
        notInHistoryTop.removeAll(result);
        if (!notInHistoryTop.isEmpty() && result.size() < selectCount) {
            int remainingSlots = selectCount - result.size();
            Collections.sort(notInHistoryTop);
            int addCount = Math.min(remainingSlots, notInHistoryTop.size());
            result.addAll(notInHistoryTop.subList(0, addCount));
            log.info("添加不在blue_history_top表中的{}个蓝球号码：{}", addCount, notInHistoryTop.subList(0, addCount));
        }

        log.info("blue_history_top表筛选结果：{}", result);
        return result;
    }

    /**
     * 随机选择蓝球号码（第四级筛选）
     * @param candidateBalls 候选蓝球号码列表
     * @param selectCount 需要选择的数量
     * @return 随机选择的蓝球号码列表
     */
    private List<Integer> selectBallsRandomlyForBlue4(List<Integer> candidateBalls, int selectCount) {
        log.info("随机选择蓝球号码，候选：{}，需要选择：{}", candidateBalls, selectCount);
        
        if (candidateBalls.size() <= selectCount) {
            log.info("候选蓝球号码数量不超过需要选择的数量，直接返回所有候选蓝球号码");
            return new ArrayList<>(candidateBalls);
        }
        
        // 创建候选球号的副本并打乱顺序
        List<Integer> shuffledBalls = new ArrayList<>(candidateBalls);
        Collections.shuffle(shuffledBalls, new Random(System.currentTimeMillis()));
        
        // 选择前selectCount个
        List<Integer> result = shuffledBalls.subList(0, selectCount);
        
        // 按球号升序排序结果
        Collections.sort(result);
        
        log.info("随机选择结果：{}", result);
        return result;
    }

    /**
     * 首球命中率统计
     * @param userId 用户ID
     * @return 首球命中率统计信息
     */
    public BallHitRateVO getFirstBallHitRate(Long userId) {
        log.info("开始统计用户{}的首球命中率", userId);
        
        // 查询用户的所有预测记录（除了"待开奖"状态的）
        QueryWrapper<PredictRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId)
                .ne("predictStatus", "待开奖");
        
        List<PredictRecord> predictRecords = predictRecordMapper.selectList(queryWrapper);
        
        // 总预测次数
        int totalCount = predictRecords.size();
        
        // 统计首球命中次数
        int hitCount = 0;
        
        for (PredictRecord record : predictRecords) {
            // 获取该预测记录对应的开奖信息
            LotteryDraws draw = lotteryDrawsService.getByDrawId(record.getDrawId());
            
            if (draw != null && record.getRedBall1() != null) {
                Integer predictedFirstBall = record.getRedBall1();
                List<Integer> drawnRedBalls = Arrays.asList(
                        draw.getRedBall1(),
                        draw.getRedBall2(),
                        draw.getRedBall3(),
                        draw.getRedBall4(),
                        draw.getRedBall5(),
                        draw.getRedBall6()
                );
                // 比较预测的第一个红球是否在开奖的六个红球中
                if (drawnRedBalls.contains(predictedFirstBall)) {
                    hitCount++;
                }
            }
        }
        
        // 计算命中率
        double hitRate = totalCount > 0 ? (double) hitCount / totalCount * 100 : 0;
        
        BallHitRateVO result = BallHitRateVO.builder()
                .hitCount(hitCount)
                .totalCount(totalCount)
                .hitRate(hitRate)
                .build();
        
        log.info("用户{}的首球命中率统计结果：命中{}次，总计{}次，命中率{}%", 
                userId, hitCount, totalCount, String.format("%.2f", hitRate));
        return result;
    }
    
    /**
     * 蓝球命中率统计
     * @param userId 用户ID
     * @return 蓝球命中率统计信息
     */
    public BallHitRateVO getBlueBallHitRate(Long userId) {
        log.info("开始统计用户{}的蓝球命中率", userId);
        
        // 查询用户的所有预测记录（除了"待开奖"状态的）
        QueryWrapper<PredictRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId)
                .ne("predictStatus", "待开奖");
        
        List<PredictRecord> predictRecords = predictRecordMapper.selectList(queryWrapper);
        
        // 总预测次数
        int totalCount = predictRecords.size();
        
        // 统计蓝球命中次数
        int hitCount = 0;
        
        for (PredictRecord record : predictRecords) {
            // 获取该预测记录对应的开奖信息
            LotteryDraws draw = lotteryDrawsService.getByDrawId(record.getDrawId());
            
            if (draw != null && record.getBlueBall() != null) {
                // 比较预测的蓝球与开奖的蓝球是否相等
                if (record.getBlueBall().equals(draw.getBlueBall())) {
                    hitCount++;
                }
            }
        }
        
        // 计算命中率
        double hitRate = totalCount > 0 ? (double) hitCount / totalCount * 100 : 0;
        
        BallHitRateVO result = BallHitRateVO.builder()
                .hitCount(hitCount)
                .totalCount(totalCount)
                .hitRate(hitRate)
                .build();
        
        log.info("用户{}的蓝球命中率统计结果：命中{}次，总计{}次，命中率{}%", 
                userId, hitCount, totalCount, String.format("%.2f", hitRate));
        return result;
    }
    
    /**
     * 奖金统计
     * @param userId 用户ID
     * @param predictId 预测记录ID，如果为null则统计所有记录
     * @return 奖金统计信息
     */
    public PrizeEstimateVO getPrizeStatistics(Long userId, Long predictId) {
        log.info("开始为用户{}进行奖金统计，predictId={}", userId, predictId);
        
        // 查询条件
        QueryWrapper<PredictRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        // 排除待开奖状态
        queryWrapper.ne("predictStatus", "待开奖");
        // 如果指定了预测记录ID，则只查询该记录
        if (predictId != null) {
            queryWrapper.eq("id", predictId);
        }
        
        // 查询用户的预测记录
        List<PredictRecord> records = predictRecordMapper.selectList(queryWrapper);
        
        // 按中奖等级分组统计
        Map<String, Integer> countByPrizeLevel = new HashMap<>();
        Map<String, BigDecimal> totalBonusByPrizeLevel = new HashMap<>();
        
        // 初始化所有等级
        String[] prizeLevels = {"一等奖", "二等奖", "三等奖", "四等奖", "五等奖", "六等奖", "未中奖"};
        for (String level : prizeLevels) {
            countByPrizeLevel.put(level, 0);
            totalBonusByPrizeLevel.put(level, BigDecimal.ZERO);
        }
        
        // 统计各等级数量和奖金
        for (PredictRecord record : records) {
            String prizeLevel = record.getPredictResult();
            if (prizeLevel == null) {
                prizeLevel = "未中奖";
            }
            
            // 更新计数
            countByPrizeLevel.put(prizeLevel, countByPrizeLevel.getOrDefault(prizeLevel, 0) + 1);
            
            // 累计奖金
            if (record.getBonus() != null) {
                BigDecimal bonus = new BigDecimal(record.getBonus().toString());
                totalBonusByPrizeLevel.put(prizeLevel, 
                    totalBonusByPrizeLevel.getOrDefault(prizeLevel, BigDecimal.ZERO).add(bonus));
            }
        }
        
        // 构建返回结果
        List<PrizeEstimateVO.PrizeDetailItem> prizeDetails = new ArrayList<>();
        
        // 按顺序添加各等级的统计结果（从高到低）
        for (String level : prizeLevels) {
            // 跳过没有记录的等级
            if (countByPrizeLevel.get(level) <= 0) {
                continue;
            }
            
            int count = countByPrizeLevel.get(level);
            BigDecimal totalBonus = totalBonusByPrizeLevel.get(level);
            BigDecimal singlePrize = BigDecimal.ZERO;
            if (count > 0) {
                singlePrize = totalBonus.divide(new BigDecimal(count), 2, RoundingMode.HALF_UP);
            }
            
            prizeDetails.add(PrizeEstimateVO.PrizeDetailItem.builder()
                    .prizeLevel(level)
                    .winningCount(count)
                    .singlePrize(singlePrize)
                    .subtotal(totalBonus)
                    .build());
        }
        
        // 计算总奖金
        BigDecimal totalPrize = prizeDetails.stream()
                .map(PrizeEstimateVO.PrizeDetailItem::getSubtotal)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        
        PrizeEstimateVO result = PrizeEstimateVO.builder()
                .totalPrize(totalPrize)
                .prizeDetails(prizeDetails)
                .build();
        
        log.info("用户{}的奖金统计结果：总奖金{}，各等级明细：{}", 
                userId, totalPrize, prizeDetails);
        return result;
    }

    /**
     * 红球命中率统计
     * @param userId 用户ID
     * @return 红球命中率统计信息
     */
    public RedBallHitRateVO getRedBallHitRate(Long userId) {
        log.info("开始统计用户{}的红球命中率", userId);

        // 查询用户的所有预测记录（除了"待开奖"状态的）
        QueryWrapper<PredictRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId)
                .ne("predictStatus", "待开奖");
        
        List<PredictRecord> predictRecords = predictRecordMapper.selectList(queryWrapper);
        
        int totalHitCount = 0;
        int totalRecords = predictRecords.size();

        for (PredictRecord record : predictRecords) {
            // 获取该预测记录对应的开奖信息
            LotteryDraws draw = lotteryDrawsService.getByDrawId(record.getDrawId());
            
            if (draw != null) {
                List<Integer> predictedRedBalls = Arrays.asList(
                        record.getRedBall1(),
                        record.getRedBall2(),
                        record.getRedBall3(),
                        record.getRedBall4(),
                        record.getRedBall5(),
                        record.getRedBall6()
                );
                
                List<Integer> drawnRedBalls = Arrays.asList(
                        draw.getRedBall1(),
                        draw.getRedBall2(),
                        draw.getRedBall3(),
                        draw.getRedBall4(),
                        draw.getRedBall5(),
                        draw.getRedBall6()
                );

                // 计算当前记录命中的红球数
                for (Integer predictedBall : predictedRedBalls) {
                    if (predictedBall != null && drawnRedBalls.contains(predictedBall)) {
                        totalHitCount++;
                    }
                }
            }
        }

        int totalPredictedCount = totalRecords * 6;
        double hitRate = (totalPredictedCount > 0) ? ((double) totalHitCount / totalPredictedCount) * 100 : 0;
        
        RedBallHitRateVO result = RedBallHitRateVO.builder()
                .totalHitCount(totalHitCount)
                .totalPredictedCount(totalPredictedCount)
                .hitRate(hitRate)
                .build();
        
        log.info("用户{}的红球命中率统计结果：命中总数{}，预测总数{}，命中率{}%", 
                userId, totalHitCount, totalPredictedCount, String.format("%.2f", hitRate));
        
        return result;
    }

    /**
     * 从T4表获取指定主球的26个从球号，取线系数最大值向下26个数字
     * @param masterBallNumber 主球号
     */
    private List<Integer> getTop26FromT4(Integer masterBallNumber) {
        log.debug("从T4表查询主球{}的线系数数据，取前26个", masterBallNumber);
        
        // 查询指定主球的所有数据，按线系数降序排列
        QueryWrapper<T4> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("masterBallNumber", masterBallNumber)
                   .orderByDesc("lineCoefficient")
                   .orderByAsc("slaveBallNumber"); // 线系数相同时按从球号升序
        
        List<T4> t4List = t4Mapper.selectList(queryWrapper);
        
        if (t4List.isEmpty()) {
            log.warn("T4表中主球{}没有数据", masterBallNumber);
            return new ArrayList<>();
        }

        if (t4List.size() < 26) {
            log.warn("T4表数据不足26条，实际{}条", t4List.size());
            return t4List.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 获取前25个
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < 25; i++) {
            result.add(t4List.get(i).getSlaveBallNumber());
        }

        // 处理第26个位置：检查是否有相同的线系数
        Double targetCoefficient = t4List.get(25).getLineCoefficient();
        List<T4> candidatesFor26th = new ArrayList<>();
        
        // 找出所有线系数等于第26个位置线系数的记录
        for (int i = 25; i < t4List.size(); i++) {
            if (t4List.get(i).getLineCoefficient().equals(targetCoefficient)) {
                candidatesFor26th.add(t4List.get(i));
            } else {
                break; // 线系数不同，停止查找
            }
        }

        if (candidatesFor26th.size() == 1) {
            // 只有一个候选，直接添加
            result.add(candidatesFor26th.get(0).getSlaveBallNumber());
        } else {
            // 有多个候选，通过history_top_100表的点系数排序
            log.debug("第26位有{}个相同线系数的候选：{}", candidatesFor26th.size(), 
                candidatesFor26th.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList()));
            
            Integer bestBall = selectBestBallFromHistoryTop100(
                candidatesFor26th.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList())
            );
            result.add(bestBall);
        }

        log.debug("T4表主球{}最终选择的26个从球：{}", masterBallNumber, result);
        return result;
    }

    /**
     * 从T5表获取指定主球的12个从球号，取线系数最大值向下12个数字
     * @param masterBallNumber 主球号
     */
    private List<Integer> getTop12FromT5(Integer masterBallNumber) {
        log.debug("从T5表查询主球{}的线系数数据，取前12个（含毛边处理）", masterBallNumber);
        
        // 查询指定主球的所有数据，按线系数降序排列
        QueryWrapper<T5> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("masterBallNumber", masterBallNumber)
                   .orderByDesc("lineCoefficient")
                   .orderByAsc("slaveBallNumber"); // 线系数相同时按从球号升序
        
        List<T5> t5List = t5Mapper.selectList(queryWrapper);
        
        if (t5List.isEmpty()) {
            log.warn("T5表中主球{}没有数据", masterBallNumber);
            return new ArrayList<>();
        }

        if (t5List.size() < 12) {
            log.warn("T5表数据不足12条，实际{}条", t5List.size());
            return t5List.stream().map(T5::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 完全复刻补齐策略：
        // 1) 找到第12位的阈值系数 targetCoefficient
        // 2) 统计 strictlyGreaterCount = 面系数 > targetCoefficient 的数量
        // 3) 全部选入这 strictlyGreaterCount 个
        // 4) 在“等于 targetCoefficient”的集合中，按 blue_history_top_100 点系数选择 (12 - strictlyGreaterCount) 个
        // 5) 若等于集合不足，再从更小系数中按顺序补足

        Double targetCoefficient = t5List.get(11).getLineCoefficient();

        // 统计大于阈值的数量
        int strictlyGreaterCount = 0;
        while (strictlyGreaterCount < t5List.size() &&
               t5List.get(strictlyGreaterCount).getLineCoefficient() > targetCoefficient) {
            strictlyGreaterCount++;
        }

        // 先加入所有大于阈值的球号
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < Math.min(strictlyGreaterCount, 12); i++) {
            result.add(t5List.get(i).getSlaveBallNumber());
        }
        if (result.size() >= 12) {
            log.debug("T5表主球{}最终选择的{}个从球：{}", masterBallNumber, result.size(), result);
            return result;
        }

        // 收集等于阈值的连续区间（紧随其后的相等区）
        List<T5> equalsGroup = new ArrayList<>();
        int idx = strictlyGreaterCount;
        while (idx < t5List.size() && t5List.get(idx).getLineCoefficient().equals(targetCoefficient)) {
            equalsGroup.add(t5List.get(idx));
            idx++;
        }

        int needFromEquals = 12 - result.size();
        if (!equalsGroup.isEmpty()) {
            List<Integer> eqNums = equalsGroup.stream().map(T5::getSlaveBallNumber).collect(Collectors.toList());
            if (eqNums.size() <= needFromEquals) {
                result.addAll(eqNums);
            } else {
                // 在等于组中先用 blue_history_top_100 分组排序，若边界存在同分，再到 blue_history_top 打破平局
                QueryWrapper<BlueHistoryTop100> qw = new QueryWrapper<>();
                qw.in("ballNumber", eqNums)
                  .orderByDesc("pointCoefficient")
                  .orderByAsc("ballNumber");
                List<BlueHistoryTop100> top100 = blueHistoryTop100Mapper.selectList(qw);

                List<Integer> picked = new ArrayList<>();
                if (top100.isEmpty()) {
                    log.warn("等值候选{}在blue_history_top_100中未找到，按球号升序取前{}个", eqNums, needFromEquals);
                    Collections.sort(eqNums);
                    picked.addAll(eqNums.subList(0, Math.min(needFromEquals, eqNums.size())));
                } else {
                    // 将top100结果按点系数分组（降序）
                    Map<Double, List<Integer>> pcGroups = new TreeMap<>(Collections.reverseOrder());
                    Set<Integer> inTop100 = new HashSet<>();
                    for (BlueHistoryTop100 row : top100) {
                        inTop100.add(row.getBallNumber());
                        pcGroups.computeIfAbsent(row.getPointCoefficient(), k -> new ArrayList<>())
                                .add(row.getBallNumber());
                    }

                    // 按组累加，遇到超额的组（边界同分）用 blue_history_top 打破平局
                    for (Map.Entry<Double, List<Integer>> e : pcGroups.entrySet()) {
                        if (picked.size() >= needFromEquals) break;
                        List<Integer> group = e.getValue();
                        int remaining = needFromEquals - picked.size();
                        if (group.size() <= remaining) {
                            // 整组加入
                            for (Integer n : group) {
                                if (!picked.contains(n)) picked.add(n);
                            }
                        } else {
                            // 边界同分：到 blue_history_top 进行最终排序
                            QueryWrapper<BlueHistoryTop> qwTop = new QueryWrapper<>();
                            qwTop.in("ballNumber", group)
                                 .orderByDesc("pointCoefficient")
                                 .orderByAsc("ballNumber");
                            List<BlueHistoryTop> topRows = blueHistoryTopMapper.selectList(qwTop);
                            if (!topRows.isEmpty()) {
                                List<Integer> ordered = topRows.stream()
                                        .map(BlueHistoryTop::getBallNumber)
                                        .collect(Collectors.toList());
                                picked.addAll(ordered.subList(0, Math.min(remaining, ordered.size())));
                            } else {
                                // 若blue_history_top也没有，按球号升序取
                                List<Integer> ordered = new ArrayList<>(group);
                                Collections.sort(ordered);
                                picked.addAll(ordered.subList(0, Math.min(remaining, ordered.size())));
                            }
                            break;
                        }
                    }

                    // 若top100覆盖的候选仍不足needFromEquals，再从不在top100中的等值候选补齐（球号升序）
                    if (picked.size() < needFromEquals) {
                        List<Integer> notInTop100 = eqNums.stream()
                                .filter(n -> !inTop100.contains(n))
                                .sorted()
                                .collect(Collectors.toList());
                        int remain = needFromEquals - picked.size();
                        picked.addAll(notInTop100.subList(0, Math.min(remain, notInTop100.size())));
                    }
                }
                result.addAll(picked);
            }
        }

        // 若仍不足12，从更小系数的条目继续顺序补足
        idx = strictlyGreaterCount + equalsGroup.size();
        while (result.size() < 12 && idx < t5List.size()) {
            result.add(t5List.get(idx).getSlaveBallNumber());
            idx++;
        }

        log.debug("T5表主球{}最终选择的{}个从球：{}", masterBallNumber, result.size(), result);
        return result;
    }

    /**
     * 从T4表获取指定主球的17个从球号，取线系数最大值向下17个数字
     * @param masterBallNumber 主球号
     */
    private List<Integer> getTop17FromT4(Integer masterBallNumber) {
        log.debug("从T4表查询主球{}的线系数数据，取前17个", masterBallNumber);
        
        // 查询指定主球的所有数据，按线系数降序排列
        QueryWrapper<T4> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("masterBallNumber", masterBallNumber)
                   .orderByDesc("lineCoefficient")
                   .orderByAsc("slaveBallNumber"); // 线系数相同时按从球号升序
        
        List<T4> t4List = t4Mapper.selectList(queryWrapper);
        
        if (t4List.isEmpty()) {
            log.warn("T4表中主球{}没有数据", masterBallNumber);
            return new ArrayList<>();
        }

        if (t4List.size() < 17) {
            log.warn("T4表数据不足17条，实际{}条", t4List.size());
            return t4List.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList());
        }

        // 获取前16个
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < 16; i++) {
            result.add(t4List.get(i).getSlaveBallNumber());
        }

        // 处理第17个位置：检查是否有相同的线系数
        Double targetCoefficient = t4List.get(16).getLineCoefficient();
        List<T4> candidatesFor17th = new ArrayList<>();
        
        // 找出所有线系数等于第17个位置线系数的记录
        for (int i = 16; i < t4List.size(); i++) {
            if (t4List.get(i).getLineCoefficient().equals(targetCoefficient)) {
                candidatesFor17th.add(t4List.get(i));
            } else {
                break; // 线系数不同，停止查找
            }
        }

        if (candidatesFor17th.size() == 1) {
            // 只有一个候选，直接添加
            result.add(candidatesFor17th.get(0).getSlaveBallNumber());
        } else {
            // 有多个候选，通过history_top_100表的点系数排序
            log.debug("第17位有{}个相同线系数的候选：{}", candidatesFor17th.size(), 
                candidatesFor17th.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList()));
            
            Integer bestBall = selectBestBallFromHistoryTop100(
                candidatesFor17th.stream().map(T4::getSlaveBallNumber).collect(Collectors.toList())
            );
            result.add(bestBall);
        }

        log.debug("T4表主球{}最终选择的17个从球：{}", masterBallNumber, result);
        return result;
    }
} 