package com.zanxiang.game.gs.data.serve.service.impl;


import com.google.common.base.CaseFormat;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.zanxiang.game.gs.data.serve.dao.mapper.game.parent.AdsGsServerDayParentMapper;
import com.zanxiang.game.gs.data.serve.pojo.dto.ServerPayDTO;
import com.zanxiang.game.gs.data.serve.pojo.entity.game.parent.AdsGameServerGsActiveRDayParent;
import com.zanxiang.game.gs.data.serve.pojo.entity.game.parent.AdsGsServerDayParent;
import com.zanxiang.game.gs.data.serve.pojo.vo.GsRTotalVO;
import com.zanxiang.game.gs.data.serve.pojo.vo.PageVO;
import com.zanxiang.game.gs.data.serve.pojo.vo.ServerPayVO;
import com.zanxiang.game.gs.data.serve.service.IAdsGameServerGsActiveRDayParentService;
import com.zanxiang.game.gs.data.serve.service.IAdsGsServerDayParentService;
import com.zanxiang.game.gs.data.serve.service.IGameServerSonMergeService;
import com.zanxiang.game.gs.data.serve.service.ISysUserService;
import com.zanxiang.module.util.DateUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

import static com.mybatisflex.core.query.QueryMethods.sum;
import static com.zanxiang.game.gs.data.serve.pojo.entity.game.parent.table.AdsGameServerGsActiveRDayParentTableDef.ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT;
import static com.zanxiang.game.gs.data.serve.pojo.entity.game.parent.table.AdsGsServerDayParentTableDef.ADS_GS_SERVER_DAY_PARENT;

/**
 * GS区服付费留存(游戏GS管理) 服务层实现。
 *
 * @author shaosong
 * @since 1.0
 */
@Service
public class AdsGsServerDayParentServiceImpl extends ServiceImpl<AdsGsServerDayParentMapper, AdsGsServerDayParent> implements IAdsGsServerDayParentService {
    @Resource
    private ISysUserService sysUserService;
    @Resource
    private IGameServerSonMergeService serverSonMergeService;
    @Resource
    private IAdsGameServerGsActiveRDayParentService rDayParentService;

    @Override
    public PageVO<ServerPayVO, Row> serverPay(ServerPayDTO dto) {
        if (StringUtils.isBlank(dto.getSortFiled())) {
            dto.setSortFiled(ADS_GS_SERVER_DAY_PARENT.SON_SERVER_AMOUNT.getName());
        } else {
            String column = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, dto.getSortFiled());
            dto.setSortFiled(column);
        }
        QueryWrapper wrapper = getServePayWrapper(dto);

        Page<ServerPayVO> paginate = getMapper().paginateAs(dto.getPageNum(), dto.getPageSize(), wrapper, ServerPayVO.class);
        if (0 == paginate.getTotalRow()) {
            return PageVO.empty(dto);
        }
        List<ServerPayVO> records = paginate.getRecords();

        Set<String> serverIdList = records.stream().map(ServerPayVO::getServerId).collect(Collectors.toSet());

        Set<Long> gsIdList = new HashSet<>();
        records.stream().map(ServerPayVO::getGsIds)
                .filter(item -> StringUtils.isNotBlank(item) && !"null".equals(item))
                .forEach(gsIds -> {
                    List<String> gsIdStr = Arrays.asList(gsIds.split(","));
                    Set<Long> collect = gsIdStr.stream().map(Long::valueOf).collect(Collectors.toSet());
                    gsIdList.addAll(collect);
                });
        // GS名称查询
        Map<Long, String> sysUserMap = CollectionUtils.isEmpty(gsIdList) ? null : sysUserService.getSysUserMap(gsIdList);
        // 是否原始服
        Map<Long, Map<String, Integer>> serverOriginMap = serverSonMergeService.gameServerOriginMap(dto.getSourceSystem(), serverIdList);
        // R数据查询
        String uniqueServeList = records.stream().map(item -> item.getParentGameId() + "_" + item.getServerId())
                .map(item -> "'" + item + "'")
                .collect(Collectors.joining(", "));
        List<AdsGameServerGsActiveRDayParent> rDayParentList = rDayParentService.list(getDayServeRWrapper(uniqueServeList, dto.getSourceSystem()));
        Map<String, List<AdsGameServerGsActiveRDayParent>> rDayMap = rDayParentList.stream().collect(Collectors.groupingBy(rDayParent -> rDayParent.getParentGameId() + "_" + rDayParent.getServerId()));

        //获取所有的da字段
        List<Field> fieldList = Arrays.stream(ServerPayVO.class.getDeclaredFields())
                .filter(field -> field.getName().contains("da"))
                .peek(field -> field.setAccessible(true))
                .collect(Collectors.toList());

        //获取所有的da对应的retainedDa
        List<Field> fieldObjectList = Arrays.stream(ServerPayVO.class.getDeclaredFields())
                .filter(field -> field.getName().contains("retainedDa"))
                .peek(field -> field.setAccessible(true))
                .collect(Collectors.toList());
        records.forEach(record -> {
            // 基础信息
            String gsIds = record.getGsIds();
            if (StringUtils.isNotBlank(gsIds) && !"null".equals(gsIds)) {
                List<String> gsIdStr = Arrays.asList(gsIds.split(","));
                record.setGsStatus(gsIdStr.size() == 1 ? 1 : 2);
                Set<String> collect = gsIdStr.stream().map(Long::valueOf)
                        .map(gsId -> sysUserMap != null ? sysUserMap.get(gsId) : null)
                        .collect(Collectors.toSet());
                record.setGsName(StringUtils.join(collect, ","));
            }
            Integer original = null == serverOriginMap.get(record.getSuperGameId()) ? null : serverOriginMap.get(record.getSuperGameId()).get(record.getServerId());
            record.setOriginServer(null == original ? null : 1 == original);

            record.setStartDiff(DateUtil.intervalOfDays(record.getStartTime(), LocalDate.now()));
            record.setServeDiff(record.getEndTime().isBefore(LocalDate.now()) ? DateUtil.intervalOfDays(record.getStartTime(), record.getEndTime())
                    : DateUtil.intervalOfDays(record.getStartTime(), LocalDate.now()));
            record.setServeStatus(record.getEndTime().isBefore(LocalDate.now()));

            // R数据计算
            List<AdsGameServerGsActiveRDayParent> rDataList = rDayMap.get(record.getParentGameId() + "_" + record.getServerId());

            // Da的总计
            String totalRDa;
            String totalDa = record.getTotal();
            if (CollectionUtils.isNotEmpty(rDataList)) {
                long sumSR = rDataList.stream().mapToLong(AdsGameServerGsActiveRDayParent::getSmallR).sum();
                long sumMR = rDataList.stream().mapToLong(AdsGameServerGsActiveRDayParent::getCentreR).sum();
                long sumBR = rDataList.stream().mapToLong(AdsGameServerGsActiveRDayParent::getBigR).sum();
                long sumSuperR = rDataList.stream().mapToLong(AdsGameServerGsActiveRDayParent::getSuperLargeR).sum();
                long sumTotalR = rDataList.stream().mapToLong(AdsGameServerGsActiveRDayParent::getTotalR).sum();

                AdsGameServerGsActiveRDayParent build = AdsGameServerGsActiveRDayParent.builder()
                        .smallR(sumSR)
                        .centreR(sumMR)
                        .bigR(sumBR)
                        .superLargeR(sumSuperR)
                        .totalR(sumTotalR)
                        .build();

                //累计充值人数
                String[] split = totalDa.split("/");
                totalRDa = getRDataStr(build, Long.valueOf(split[2]), Long.valueOf(split[1]));
            } else {
                // 小R付费比 7/中R付费比 8/大R付费比 9/超R付费比 10/小R留存率 11/中R留存率 12/大R留存率 13/超R留存率 14/R总留存率 15/小R人数 16/中R人数 17/大R人数 18/超R人数 19/总R人数 20
                totalRDa = "0/0/0/0/0/0/0/0/0/0/0/0/0/0";
            }
            totalDa +=  "/" + totalRDa;
            String[] totalSplit = totalDa.split("/");
            ServerPayVO.DayN retentionTotal = ServerPayVO.DayN.builder()
                    .activeNum(Long.parseLong(totalSplit[0]))
                    .payNum(Long.parseLong(totalSplit[1]))
                    .totalPayNum(Long.parseLong(totalSplit[2]))
                    .sRNum(Long.parseLong(totalSplit[16]))
                    .mRNum(Long.parseLong(totalSplit[17]))
                    .bRNum(Long.parseLong(totalSplit[18]))
                    .superRNum(Long.parseLong(totalSplit[19]))
                    .totalRNum(Long.parseLong(totalSplit[20]))
                    .payMoney(new BigDecimal(totalSplit[3]))
                    .payRate(new BigDecimal(totalSplit[4]))
                    .sRRate(new BigDecimal(totalSplit[7]))
                    .mRRate(new BigDecimal(totalSplit[8]))
                    .bRRate(new BigDecimal(totalSplit[9]))
                    .superRRate(new BigDecimal(totalSplit[10]))
                    .activeRetainedRate(new BigDecimal(totalSplit[5]))
                    .payRetainedRate(new BigDecimal(totalSplit[6]))
                    .sRRetainedRate(new BigDecimal(totalSplit[11]))
                    .mRRetainedRate(new BigDecimal(totalSplit[12]))
                    .bRRetainedRate(new BigDecimal(totalSplit[13]))
                    .superRRetainedRate(new BigDecimal(totalSplit[14]))
                    .totalRRetainedRate(new BigDecimal(totalSplit[15]))
                    .build();
            record.setRetainedTotal(retentionTotal);

            // Da的90天
            for (int i = 0; i < fieldList.size(); i++) {
                try {
                    Field field = fieldList.get(i);
                    // 活跃人数 0/付费人数 1/累计付费人数 2/付费金额 3/付费率 4/活跃留存率 5/付费留存率 6
                    String da = (String) field.get(record);
                    String rDa;
                    if (rDataList != null && rDataList.size() >= i + 1) {
                        //累计充值人数
                        String[] split = da.split("/");
                        AdsGameServerGsActiveRDayParent gsGameServerDayRVO = rDataList.get(i);
                        rDa = getRDataStr(gsGameServerDayRVO, Long.valueOf(split[2]), Long.valueOf(split[1]));
                    } else {
                        // 小R付费比 7/中R付费比 8/大R付费比 9/超R付费比 10/小R留存率 11/中R留存率 12/大R留存率 13/超R留存率 14/R总留存率 15/小R人数 16/中R人数 17/大R人数 18/超R人数 19/总R人数 20
                        rDa = "0/0/0/0/0/0/0/0/0/0/0/0/0/0";
                    }
                    da = removeLastPart(da) + "/" + rDa;
                    String[] split = da.split("/");
                    ServerPayVO.DayN retentionVO = ServerPayVO.DayN.builder()
                            .activeNum(Long.parseLong(split[0]))
                            .payNum(Long.parseLong(split[1]))
                            .totalPayNum(Long.parseLong(split[2]))
                            .sRNum(Long.parseLong(split[16]))
                            .mRNum(Long.parseLong(split[17]))
                            .bRNum(Long.parseLong(split[18]))
                            .superRNum(Long.parseLong(split[19]))
                            .totalRNum(Long.parseLong(split[20]))
                            .payMoney(new BigDecimal(split[3]))
                            .payRate(new BigDecimal(split[4]))
                            .sRRate(new BigDecimal(split[7]))
                            .mRRate(new BigDecimal(split[8]))
                            .bRRate(new BigDecimal(split[9]))
                            .superRRate(new BigDecimal(split[10]))
                            .activeRetainedRate(new BigDecimal(split[5]))
                            .payRetainedRate(new BigDecimal(split[6]))
                            .sRRetainedRate(new BigDecimal(split[11]))
                            .mRRetainedRate(new BigDecimal(split[12]))
                            .bRRetainedRate(new BigDecimal(split[13]))
                            .superRRetainedRate(new BigDecimal(split[14]))
                            .totalRRetainedRate(new BigDecimal(split[15]))
                            .build();
                    fieldObjectList.get(i).set(record, retentionVO);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        return new PageVO<>(paginate);
    }

    /**
     * 拼接R留存率  (R/充值总人数)
     *
     * @param rvo
     * @param payNum
     * @return
     */
    private String getRDataStr(AdsGameServerGsActiveRDayParent rvo, Long totalPayNum, Long payNum) {
        String rateD = "";
        String payRateD = "";
        if (rvo != null && totalPayNum != null && totalPayNum != 0) {
            //R人数
            Long smallRCount = rvo.getSmallR();
            Long mediumRCount = rvo.getCentreR();
            Long largeRCont = rvo.getBigR();
            Long superRCount = rvo.getSuperLargeR();
            //R总
            long totalRCount = rvo.getTotalR();
            //R留存率
            BigDecimal smallRate = rRate(smallRCount, totalPayNum);
            BigDecimal mediumRate = rRate(mediumRCount, totalPayNum);
            BigDecimal largeRate = rRate(largeRCont, totalPayNum);
            BigDecimal superRate = rRate(superRCount, totalPayNum);
            //R总留存
            BigDecimal rTotalRate = rRate(totalRCount, totalPayNum);
            rateD = smallRate + "/" + mediumRate + "/" + largeRate + "/" + superRate + "/" + rTotalRate + "/" + smallRCount + "/" + mediumRCount + "/" + largeRCont + "/" + superRCount + "/" + totalRCount;
        } else {
            // 小R留存率/中R留存率/大R留存率/超R留存率/R总留存率/小R人数/中R人数/大R人数/超R人数/总R人数
            rateD = "0/0/0/0/0/0/0/0/0/0";
        }

        if (rvo != null && payNum != null && payNum != 0) {
            //R人数
            Long smallRCount = rvo.getSmallR();
            Long mediumRCount = rvo.getCentreR();
            Long largeRCont = rvo.getBigR();
            Long superRCount = rvo.getSuperLargeR();

            //R付费比
            BigDecimal smallPayRate = rRate(smallRCount, payNum);
            BigDecimal mediumPayRate = rRate(mediumRCount, payNum);
            BigDecimal largePayRate = rRate(largeRCont, payNum);
            BigDecimal superPayRate = rRate(superRCount, payNum);
            payRateD = smallPayRate + "/" + mediumPayRate + "/" + largePayRate + "/" + superPayRate + "/";
        } else {
            // 小R付费比/中R付费比/大R付费比/超R付费比
            payRateD = "0/0/0/0/";
        }
        return payRateD + rateD;
    }

    public String removeLastPart(String str) {
        if (str.contains("/")) {
            // 查找最后一个斜杠的索引
            int lastSlashIndex = str.lastIndexOf("/");
            // 查找倒数第二个斜杠的索引
            int secondLastSlashIndex = str.substring(0, lastSlashIndex).lastIndexOf("/");

            // 如果倒数第二个斜杠的索引是-1，说明没有倒数第二个斜杠
            if (secondLastSlashIndex == -1) {
                return null;
            }
            return str.substring(0, secondLastSlashIndex);
        } else {
            return str;
        }
    }

    private BigDecimal rRate(Long molecule, Long denominator) {
        return new BigDecimal(molecule).divide(new BigDecimal(denominator), 4, RoundingMode.HALF_UP).setScale(2, RoundingMode.DOWN);
    }

    private QueryWrapper getDayServeRWrapper(String uniqueServeList, String sourceSystem) {
        return QueryWrapper.create()
                .from(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT)
                .select(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.ALL_COLUMNS)
                .eq(AdsGameServerGsActiveRDayParent::getSourceSystem, sourceSystem)
                .and("CONCAT_WS('_', parent_game_id, server_id) IN (" + uniqueServeList + ")");
    }

    private QueryWrapper getTotalServeRWrapper(ServerPayDTO dto) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .from(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT)
                .leftJoin(ADS_GS_SERVER_DAY_PARENT)
                .on(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.SOURCE_SYSTEM.eq(ADS_GS_SERVER_DAY_PARENT.SOURCE_SYSTEM)
                        .and(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.DT.eq(ADS_GS_SERVER_DAY_PARENT.START_TIME)
                                .and(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.PARENT_GAME_ID.eq(ADS_GS_SERVER_DAY_PARENT.PARENT_GAME_ID))
                                .and(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.SERVER_ID.eq(ADS_GS_SERVER_DAY_PARENT.SERVER_ID))))
                .select("GROUP_CONCAT(CONCAT(CAST(small_r AS CHAR))) as small_r")
                .select("GROUP_CONCAT(CONCAT(CAST(centre_r AS CHAR))) as centre_r")
                .select("GROUP_CONCAT(CONCAT(CAST(big_r AS CHAR))) as big_r")
                .select("GROUP_CONCAT(CONCAT(CAST(super_large_r AS CHAR))) as super_large_r")
                .select("GROUP_CONCAT(CONCAT(CAST(total_r AS CHAR))) as total_r")
                .groupBy(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.DT)
                .groupBy(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.SOURCE_SYSTEM)
                .groupBy(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.PARENT_GAME_ID)
                .groupBy(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.SERVER_ID);
        condition(dto, queryWrapper);
        return queryWrapper;
    }

    private QueryWrapper getAllServeRWrapper(ServerPayDTO dto) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .from(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT)
                .leftJoin(ADS_GS_SERVER_DAY_PARENT)
                .on(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.SOURCE_SYSTEM.eq(ADS_GS_SERVER_DAY_PARENT.SOURCE_SYSTEM)
                        .and(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.DT.eq(ADS_GS_SERVER_DAY_PARENT.START_TIME)
                                .and(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.PARENT_GAME_ID.eq(ADS_GS_SERVER_DAY_PARENT.PARENT_GAME_ID))
                                .and(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.SERVER_ID.eq(ADS_GS_SERVER_DAY_PARENT.SERVER_ID))))
                .select(sum(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.SMALL_R).as(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.SMALL_R.getName()))
                .select(sum(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.CENTRE_R).as(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.CENTRE_R.getName()))
                .select(sum(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.BIG_R).as(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.BIG_R.getName()))
                .select(sum(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.SUPER_LARGE_R).as(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.SUPER_LARGE_R.getName()))
                .select(sum(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.TOTAL_R).as(ADS_GAME_SERVER_GS_ACTIVE_R_DAY_PARENT.TOTAL_R.getName()));
        condition(dto, queryWrapper);
        return queryWrapper;
    }

    private QueryWrapper getServePayWrapper(ServerPayDTO dto) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .from(ADS_GS_SERVER_DAY_PARENT)
                .select(ADS_GS_SERVER_DAY_PARENT.ALL_COLUMNS)
                .orderBy(dto.getSortFiled(), dto.getSortAsc());
        condition(dto, queryWrapper);
        return queryWrapper;
    }

    private QueryWrapper getServePayTotalWrapper(ServerPayDTO dto) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .from(ADS_GS_SERVER_DAY_PARENT)
                .select(getDayN())
                .select(sum(ADS_GS_SERVER_DAY_PARENT.TOTAL_ROLE_NUM).as(ADS_GS_SERVER_DAY_PARENT.TOTAL_ROLE_NUM.getName()))
                .select(sum(ADS_GS_SERVER_DAY_PARENT.TOTAL_AMOUNT_NUM).as(ADS_GS_SERVER_DAY_PARENT.TOTAL_AMOUNT_NUM.getName()))
                .select(sum(ADS_GS_SERVER_DAY_PARENT.TOTAL_AMOUNT_COUNT).as(ADS_GS_SERVER_DAY_PARENT.TOTAL_AMOUNT_COUNT.getName()))
                .select(sum(ADS_GS_SERVER_DAY_PARENT.TOTAL_AMOUNT).as(ADS_GS_SERVER_DAY_PARENT.TOTAL_AMOUNT.getName()))
                .select("ROUND(IF(SUM(total_amount_num) > 0 , SUM(total_amount) / SUM(total_amount_num), 0), 4) AS arppu")
                .select("ROUND(IF(SUM(total_amount_count) > 0 , SUM(total_amount) / SUM(total_amount_count), 0), 4) AS avg_amount")
                .select("ROUND(IF(SUM(total_role_num) > 0 , SUM(total_amount_num) / SUM(total_role_num), 0), 4) AS amount_rate")
                .select(sum(ADS_GS_SERVER_DAY_PARENT.SON_ROLE_NUM).as(ADS_GS_SERVER_DAY_PARENT.SON_ROLE_NUM.getName()))
                .select(sum(ADS_GS_SERVER_DAY_PARENT.SON_SERVER_AMOUNT_NUM).as(ADS_GS_SERVER_DAY_PARENT.SON_SERVER_AMOUNT_NUM.getName()))
                .select(sum(ADS_GS_SERVER_DAY_PARENT.SON_SERVER_AMOUNT_COUNT).as(ADS_GS_SERVER_DAY_PARENT.SON_SERVER_AMOUNT_COUNT.getName()))
                .select(sum(ADS_GS_SERVER_DAY_PARENT.SON_SERVER_AMOUNT).as(ADS_GS_SERVER_DAY_PARENT.SON_SERVER_AMOUNT.getName()))
                .select("ROUND(IF(SUM(son_server_amount_num) > 0 , SUM(son_server_amount) / SUM(son_server_amount_num), 0), 4) AS son_arppu")
                .select("ROUND(IF(SUM(son_server_amount_count) > 0 , SUM(son_server_amount) / SUM(son_server_amount_count), 0), 4) AS son_avg_amount")
                .select("ROUND(IF(SUM(son_role_num) > 0 , SUM(son_server_amount_num) / SUM(son_role_num), 0), 4) AS son_amount_rate");
        condition(dto, queryWrapper);
        return queryWrapper;
    }

    private static void condition(ServerPayDTO dto, QueryWrapper queryWrapper) {
        queryWrapper.eq(AdsGsServerDayParent::getSourceSystem, dto.getSourceSystem())
                .ge(AdsGsServerDayParent::getStartTime, dto.getServerStartBegin(), null != dto.getServerStartBegin())
                .le(AdsGsServerDayParent::getStartTime, dto.getServerStartEnd(), null != dto.getServerStartEnd())
                .ge(AdsGsServerDayParent::getStartTime, dto.getServeDayBegin(), null != dto.getServeDayBegin())
                .le(AdsGsServerDayParent::getEndTime, dto.getServeDayEnd(), null != dto.getServeDayEnd())
                .eq(AdsGsServerDayParent::getSuperGameId, dto.getSuperGameId(), null != dto.getSuperGameId())
                .eq(AdsGsServerDayParent::getParentGameId, dto.getParentGameId(), null != dto.getParentGameId())
                .in(AdsGsServerDayParent::getServerId, dto.getServerIdList(), CollectionUtils.isNotEmpty(dto.getServerIdList()))
                .ge(AdsGsServerDayParent::getEndTime, LocalDate.now(), null != dto.getServeStatus() && 1 == dto.getServeStatus())
                .le(AdsGsServerDayParent::getEndTime, LocalDate.now(), null != dto.getServeStatus() && 2 == dto.getServeStatus());
        if (CollectionUtils.isNotEmpty(dto.getGsIdList())) {
            StringBuilder gsSql = new StringBuilder();
            dto.getGsIdList().forEach(gsId -> {
                gsSql.append(String.format(" FIND_IN_SET(%s, gs_ids) > 0", gsId));
                gsSql.append(" OR ");
            });
            gsSql.setLength(gsSql.length() - 4);
            queryWrapper.and(gsSql.toString());
        }
        if (null != dto.getGsStatus() && 1 == dto.getGsStatus()) {
            queryWrapper.and("gs_ids != 'null' and LENGTH(gs_ids) - LENGTH(REPLACE(gs_ids, ',', '')) + 1 = 1");
        }

        if (null != dto.getGsStatus() && 2 == dto.getGsStatus()) {
            queryWrapper.and("gs_ids != 'null' and LENGTH(gs_ids) - LENGTH(REPLACE(gs_ids, ',', '')) + 1 > 1");
        }
    }

    private String getDayN() {
        StringBuilder currDayN = new StringBuilder(StringUtils.EMPTY);
        for (int day = 1; day <= 90; day++) {
            String dayN = "da" + day;
            daySplit(currDayN, dayN);
        }
        String total = "total";
        dayTotalSplit(currDayN, total);
        // 移除最后一个逗号和空格
        if (currDayN.length() > 0) {
            currDayN.setLength(currDayN.length() - 2);
        }
        //返回结果
        return currDayN.toString();
    }

    private void daySplit(StringBuilder totalDayN, String dayN) {
        totalDayN.append(" CONCAT_WS('/', ")
                .append("IFNULL(SUM(SPLIT_PART(").append(dayN).append(", '/', 1)), 0), ")
                .append("IFNULL(SUM(SPLIT_PART(").append(dayN).append(", '/', 2)), 0), ")
                .append("IFNULL(SUM(SPLIT_PART(").append(dayN).append(", '/', 3)), 0), ")
                .append("IFNULL(SUM(SPLIT_PART(").append(dayN).append(", '/', 4)), 0), ")
                .append("ROUND(IF(SUM(SPLIT_PART(").append(dayN).append(", '/', 1)) > 0, ")
                .append("SUM(SPLIT_PART(").append(dayN).append(", '/', 2)) / SUM(SPLIT_PART(").append(dayN).append(", '/', 1)), 0), 4), ")

                .append("ROUND(IF(SUM(SPLIT_PART(").append(dayN).append(", '/', 8)) > 0, ")
                .append("SUM(SPLIT_PART(").append(dayN).append(", '/', 1)) / SUM(SPLIT_PART(").append(dayN).append(", '/', 8)), 0), 4), ")

                .append("ROUND(IF(SUM(SPLIT_PART(").append(dayN).append(", '/', 9)) > 0, ")
                .append("SUM(SPLIT_PART(").append(dayN).append(", '/', 2)) / SUM(SPLIT_PART(").append(dayN).append(", '/', 9)), 0), 4)) ")
                .append("as ").append(dayN).append(", ");
    }

    private void dayTotalSplit(StringBuilder totalDayN, String dayN) {
        totalDayN.append(" CONCAT_WS('/', ")
                .append("IFNULL(SUM(SPLIT_PART(").append(dayN).append(", '/', 1)), 0), ")
                .append("IFNULL(SUM(SPLIT_PART(").append(dayN).append(", '/', 2)), 0), ")
                .append("IFNULL(SUM(SPLIT_PART(").append(dayN).append(", '/', 3)), 0), ")
                .append("IFNULL(SUM(SPLIT_PART(").append(dayN).append(", '/', 4)), 0), ")
                .append("ROUND(IF(SUM(SPLIT_PART(").append(dayN).append(", '/', 1)) > 0, ")
                .append("SUM(SPLIT_PART(").append(dayN).append(", '/', 2)) / SUM(SPLIT_PART(").append(dayN).append(", '/', 1)), 0), 4), ")
                .append("ROUND(IF(SUM(son_role_num) > 0, SUM(SPLIT_PART(").append(dayN).append(", '/', 1)) / SUM(son_role_num), 0), 4), ")
                .append("ROUND(IF(SUM(son_server_amount_num) > 0, SUM(SPLIT_PART(").append(dayN).append(", '/', 2)) / SUM(son_server_amount_num), 0), 4)) ")
                .append("as ").append(dayN).append(", ");
    }

    @Override
    public ServerPayVO serverPayTotal(ServerPayDTO dto) {
        QueryWrapper wrapper = getServePayTotalWrapper(dto);

        ServerPayVO serverPayVO = getMapper().selectOneByQueryAs(wrapper, ServerPayVO.class);
        if (null == serverPayVO) {
            return ServerPayVO.builder().build();
        }
        // R数据查询
        List<GsRTotalVO> rDayParentList = rDayParentService.listAs(getTotalServeRWrapper(dto), GsRTotalVO.class);
        AdsGameServerGsActiveRDayParent allRData = rDayParentService.getOne(getAllServeRWrapper(dto));

        //获取所有的da字段
        List<Field> fieldList = Arrays.stream(ServerPayVO.class.getDeclaredFields())
                .filter(field -> field.getName().contains("da"))
                .peek(field -> field.setAccessible(true))
                .collect(Collectors.toList());

        //获取所有的da对应的retainedDa
        List<Field> fieldObjectList = Arrays.stream(ServerPayVO.class.getDeclaredFields())
                .filter(field -> field.getName().contains("retainedDa"))
                .peek(field -> field.setAccessible(true))
                .collect(Collectors.toList());

        Map<Integer, AdsGameServerGsActiveRDayParent> dayRVOMap = new HashMap<>();
        for (GsRTotalVO rData : rDayParentList) {
            if (StringUtils.isEmpty(rData.getSmallR())) {
                continue;
            }
            String[] smallRs = rData.getSmallR().split(",");
            String[] mediumRs = rData.getCentreR().split(",");
            String[] bigRs = rData.getBigR().split(",");
            String[] superRs = rData.getSuperLargeR().split(",");
            String[] totalRs = rData.getTotalR().split(",");
            for (int i = 0; i < fieldList.size(); i++) {
                if (i >= smallRs.length) {
                    continue;
                }
                String smallR = smallRs[i];
                String mediumR = mediumRs[i];
                String largeR = bigRs[i];
                String superR = superRs[i];
                String totalR = totalRs[i];
                if (dayRVOMap.get(i) == null) {
                    AdsGameServerGsActiveRDayParent gsGameServerDayRVO = new AdsGameServerGsActiveRDayParent();
                    gsGameServerDayRVO.setSmallR(Long.parseLong(smallR.trim()));
                    gsGameServerDayRVO.setCentreR(Long.parseLong(mediumR.trim()));
                    gsGameServerDayRVO.setBigR(Long.parseLong(largeR.trim()));
                    gsGameServerDayRVO.setSuperLargeR(Long.parseLong(superR.trim()));
                    gsGameServerDayRVO.setTotalR(Long.parseLong(totalR.trim()));
                    dayRVOMap.put(i, gsGameServerDayRVO);
                } else {
                    AdsGameServerGsActiveRDayParent gsGameServerDayRVO = dayRVOMap.get(i);
                    gsGameServerDayRVO.setSmallR(gsGameServerDayRVO.getSmallR() + Long.parseLong(smallR.trim()));
                    gsGameServerDayRVO.setCentreR(gsGameServerDayRVO.getCentreR() + Long.parseLong(mediumR.trim()));
                    gsGameServerDayRVO.setBigR(gsGameServerDayRVO.getBigR() + Long.parseLong(largeR.trim()));
                    gsGameServerDayRVO.setSuperLargeR(gsGameServerDayRVO.getSuperLargeR() + Long.parseLong(superR.trim()));
                    gsGameServerDayRVO.setTotalR(gsGameServerDayRVO.getTotalR() + Long.parseLong(totalR.trim()));
                }
            }
        }

        // DaN数据
        for (int i = 0; i < fieldList.size(); i++) {
            Field field = fieldList.get(i);
            try {
                String daN = (String) field.get(serverPayVO);
                //总的累计数据
                String[] daNSplit = daN.split("/");
                AdsGameServerGsActiveRDayParent gsGameServerDayRVO = dayRVOMap.get(i);
                String rData = getRDataStr(gsGameServerDayRVO, Long.valueOf(daNSplit[2]), Long.valueOf(daNSplit[1]));
                daN += "/" + rData;
                String[] retention = daN.split("/");
                ServerPayVO.DayN retentionVO = ServerPayVO.DayN.builder()
                        .activeNum(Long.parseLong(retention[0]))
                        .payNum(Long.parseLong(retention[1]))
                        .totalPayNum(Long.parseLong(retention[2]))
                        .sRNum(Long.parseLong(retention[16]))
                        .mRNum(Long.parseLong(retention[17]))
                        .bRNum(Long.parseLong(retention[18]))
                        .superRNum(Long.parseLong(retention[19]))
                        .totalRNum(Long.parseLong(retention[20]))
                        .payMoney(new BigDecimal(retention[3]))
                        .payRate(new BigDecimal(retention[4]))
                        .sRRate(new BigDecimal(retention[7]))
                        .mRRate(new BigDecimal(retention[8]))
                        .bRRate(new BigDecimal(retention[9]))
                        .superRRate(new BigDecimal(retention[10]))
                        .activeRetainedRate(new BigDecimal(retention[5]))
                        .payRetainedRate(new BigDecimal(retention[6]))
                        .sRRetainedRate(new BigDecimal(retention[11]))
                        .mRRetainedRate(new BigDecimal(retention[12]))
                        .bRRetainedRate(new BigDecimal(retention[13]))
                        .superRRetainedRate(new BigDecimal(retention[14]))
                        .totalRRetainedRate(new BigDecimal(retention[15]))
                        .build();
                fieldObjectList.get(i).set(serverPayVO, retentionVO);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        String totalRDa;
        String totalDa = serverPayVO.getTotal();
        if (null != allRData) {
            //累计充值人数
            String[] split = totalDa.split("/");
            totalRDa = getRDataStr(allRData, Long.valueOf(split[2]), Long.valueOf(split[1]));
        } else {
            // 小R付费比 7/中R付费比 8/大R付费比 9/超R付费比 10/小R留存率 11/中R留存率 12/大R留存率 13/超R留存率 14/R总留存率 15/小R人数 16/中R人数 17/大R人数 18/超R人数 19/总R人数 20
            totalRDa = "0/0/0/0/0/0/0/0/0/0/0/0/0/0";
        }
        totalDa += "/" + totalRDa;
        String[] totalSplit = totalDa.split("/");
        ServerPayVO.DayN retentionTotal = ServerPayVO.DayN.builder()
                .activeNum(Long.parseLong(totalSplit[0]))
                .payNum(Long.parseLong(totalSplit[1]))
                .totalPayNum(Long.parseLong(totalSplit[2]))
                .sRNum(Long.parseLong(totalSplit[16]))
                .mRNum(Long.parseLong(totalSplit[17]))
                .bRNum(Long.parseLong(totalSplit[18]))
                .superRNum(Long.parseLong(totalSplit[19]))
                .totalRNum(Long.parseLong(totalSplit[20]))
                .payMoney(new BigDecimal(totalSplit[3]))
                .payRate(new BigDecimal(totalSplit[4]))
                .sRRate(new BigDecimal(totalSplit[7]))
                .mRRate(new BigDecimal(totalSplit[8]))
                .bRRate(new BigDecimal(totalSplit[9]))
                .superRRate(new BigDecimal(totalSplit[10]))
                .activeRetainedRate(new BigDecimal(totalSplit[5]))
                .payRetainedRate(new BigDecimal(totalSplit[6]))
                .sRRetainedRate(new BigDecimal(totalSplit[11]))
                .mRRetainedRate(new BigDecimal(totalSplit[12]))
                .bRRetainedRate(new BigDecimal(totalSplit[13]))
                .superRRetainedRate(new BigDecimal(totalSplit[14]))
                .totalRRetainedRate(new BigDecimal(totalSplit[15]))
                .build();
        serverPayVO.setRetainedTotal(retentionTotal);
        return serverPayVO;
    }
}