package com.awspaas.user.apps.bnbm.datalinkup.service.impl;

import com.actionsoft.bpms.bo.engine.BO;
import com.actionsoft.bpms.commons.database.DBUtils;
import com.actionsoft.bpms.commons.database.RowMap;
import com.actionsoft.bpms.commons.pagination.SQLPagination;
import com.actionsoft.bpms.server.UserContext;
import com.actionsoft.bpms.util.DBSql;
import com.actionsoft.bpms.util.UtilDate;
import com.actionsoft.sdk.local.SDK;
import com.actionsoft.sdk.local.api.cc.RDSAPI;
import com.awspaas.user.apps.bnbm.datalinkup.entity.DateRange;
import com.awspaas.user.apps.bnbm.datalinkup.service.DataSyncService;
import com.awspaas.user.apps.bnbm.datalinkup.util.SaleUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.Date;
import java.util.stream.Collectors;

/**
 * @ClassName: SaleDataSyncServiceImpl
 * @Description: 销售数据同步服务实现类，处理数据同步的核心逻辑
 */
public class SaleDataSyncServiceImpl implements DataSyncService {
    private static final Logger LOGGER = LoggerFactory.getLogger(SaleDataSyncServiceImpl.class);
    // 时间范围常量：同步最近30天数据（不包括当天）
    private static final int DAYS_BACK = Integer.parseInt(SDK.getAppAPI().getProperty("com.awspaas.user.apps.bnbm.datalinkup", "days_back"));

    // 增加分页大小常量
    private static final int PAGE_SIZE = 10000; // 每页查询1000条记录

    private static final String ORACLE_DATE_FORMAT = "YYYY-MM-DD HH24:MI:SS";
    private static final String SEND_EMAIL_ACCOUNT_NUMBER = SDK.getAppAPI().getProperty("com.awspaas.user.apps.bnbm.datalinkup","sendEmailAccountNumber");
    private static final String RECEIVE_EMAIL_ACCOUNT = SDK.getAppAPI().getProperty("com.awspaas.user.apps.bnbm.datalinkup","RECEIVE_EMAIL_ACCOUNT");


    /**
     * 根据主配置列表执行数据同步
     * @param configs 主配置列表（按所属板块分组处理）
     */
    @Override
    public ArrayList<DateRange> syncDataByConfigs(List<BO> configs) {
        ArrayList<DateRange> list = new ArrayList<>();
        if (configs.isEmpty()) {
            LOGGER.info("未找到有效的同步配置");
            return list;
        }

        // 按所属板块(SSBK)分组配置
        Map<String, List<BO>> configsByPlate = configs.stream()
                .collect(Collectors.groupingBy(bo -> bo.getString("SSBK")));
        // 遍历处理每个板块的配置
        for (Map.Entry<String, List<BO>> entry : configsByPlate.entrySet()) {
            String plate = entry.getKey();
            List<BO> plateConfigs = entry.getValue();
            LOGGER.info("处理板块【{}】的{}条配置", plate, plateConfigs.size());

            // 处理当前板块的每条配置
            for (BO mainConfig : plateConfigs) {
                try {
                    DateRange dateRange = processMainConfig(mainConfig);
                    list.add(dateRange);
                } catch (Exception e) {
                    LOGGER.error("处理配置失败 [板块={}, BindID={}]: {}",
                            plate, mainConfig.getString("BINDID"), e.getMessage(), e);
                }
            }
        }
        return list;
    }

    /**
     * 处理单个主配置的数据同步流程
     * @param mainConfig 主配置对象
     * 步骤：
     * 1. 获取字段映射配置
     * 2. 计算时间范围
     * 3. 删除目标表旧数据
     * 4. 查询源表数据
     * 5. 转换并插入目标表
     */
    @Override
    public DateRange processMainConfig(BO mainConfig) {
        String bindId = mainConfig.getString("BINDID");
        String tableName = mainConfig.getString("TBB");
        String timeField = mainConfig.getString("SJZD");
        String targetTable = mainConfig.getString("LDB");
        String ccId = mainConfig.getString("CC_ID");
        String partitionField = mainConfig.getString("FQBZD");
        String bkgs = mainConfig.getString("SSBK");
        String jezd = mainConfig.getString("JEZD");
        String tablename = mainConfig.getString("TABLENAME");
        DateRange dateRange = new DateRange();

        LOGGER.info("处理配置：BindID={}, 源表={}, 目标表={}, CC_ID={}, 时间字段={}, 分区字段配置={}, 板块={}",
                bindId, tableName, targetTable, ccId,timeField,partitionField,bkgs);
        if (tablename.equals("应收表") && (bkgs.equals("北新防水") || bkgs.equals("北新涂料"))) {
            LOGGER.info("北新防水、北新涂料不需要定时器拉取应收单数据");
            return dateRange;
        }

        // 查询子表字段映射配置
        List<BO> fieldMappings = SDK.getBOAPI()
                .query("BO_EU_BNBM_DATALINKUP_SJGTPZ_SUB")
                .addQuery("BINDID =", bindId)
                .list();

        if (fieldMappings.isEmpty()) {
            LOGGER.warn("未找到BindID={}的字段映射配置", bindId);
            return dateRange;
        }

        // 根据时间字段是否为空设置日期范围
        Date startDate = null;
        Date endDate = null;

        // 删除目标表数据（根据时间字段是否为空决定删除范围）
        if (timeField == null || timeField.isEmpty()) {
            // 全量删除
            deleteAllTargetData(targetTable);
        } else {
            // 计算时间范围（当前日期-30天 ~ 昨天）
            Calendar cal = Calendar.getInstance();
            cal.add(Calendar.DATE, -1); // 昨天
            endDate = cal.getTime();
            cal.add(Calendar.DATE, -DAYS_BACK + 1); // 30天前（含）
            startDate = cal.getTime();

            // 获取目标表时间字段名
            String targetTimeField = getTargetTimeField(fieldMappings, timeField);
            if (targetTimeField == null) {
                LOGGER.error("无法找到源时间字段[{}]对应的目标表字段，跳过同步", timeField);
                return dateRange;
            }
            // 按时间范围删除
            deleteTargetData(targetTable, targetTimeField, startDate, endDate);
        }
        // 查询源表数据（跨库查询）
        if (tablename.equals("应收表")){
            LOGGER.info("开始执行应收的明细汇总");
            if (!bkgs.equals("北新防水") || !bkgs.equals("北新涂料")) {
                querySourceDataYS(ccId, tableName, timeField, startDate, endDate, partitionField,
                        fieldMappings, targetTable, jezd);
            }
        }else {
            querySourceData(ccId, tableName, timeField, startDate, endDate, partitionField,
                    fieldMappings, targetTable, jezd);
        }
        dateRange.setStartDate(startDate);
        dateRange.setEndDate(endDate);
        return dateRange;
    }

    /**
     * 跨库查询源表数据
     * @param ccId 跨库连接ID
     * @param tableName 源表名
     * @param timeField 源表时间字段名
     * @param startDated 开始时间
     * @param endDated 结束时间
     * @return 查询结果数据集
     * @throws RuntimeException 查询失败或参数无效时抛出
     */
    @Override
    public void querySourceData(String ccId, String tableName,
                                String timeField, Date startDated, Date endDated,String partitionField,
                                List<BO> fieldMappings, String targetTable,String jezd) {
//        LOGGER.info("ccId:{},tableName:{},timeField:{},startDated:{},endDated:{},partitionField:{},fieldMappings:{},targetTable:{},jezd:{}",ccId,tableName,timeField,startDated,endDated,partitionField,Arrays.asList(fieldMappings.toArray()),targetTable,jezd);
        int totalRows = 0;      // 总查询行数
        int totalSuccess = 0;   // 总成功插入行数
        int pageNo = 1;
        boolean hasMore;
        RDSAPI rdsapi = null;
        DBUtils.SUPPLY supply = null;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd");

        // 重试配置
        int maxRetries = 3;
        int retryDelayMs = 2000; // 重试延迟2秒
        try {
            // 带重试机制的连接获取
            for (int retry = 0; retry < maxRetries; retry++) {
                try {
                    rdsapi = SDK.getCCAPI().getRDSAPI(ccId);
                    supply = rdsapi.getSupply();
                    if (rdsapi != null && supply!=null) {
                        break; // 连接成功，跳出重试循环
                    }
                } catch (Exception e) {
                    if (retry == maxRetries - 1) {
                        String result = fieldMappings.stream()
                                .filter(bo -> "BKGS".equals(bo.getString("LDBZD")))   // 条件1: LDBZD 为 "BKGS"
                                .filter(bo -> bo.getString("LDZDMRZ") != null)        // 条件2: LDZDMRZ 不为空
                                .map(bo -> bo.getString("LDZDMRZ"))                          // 提取 LDZDMRZ 的值
                                .collect(Collectors.joining());                // 收集到 List
                        // 最后一次重试仍然失败
                        String errorMsg = String.format("获取数据库连接失败(CC_ID: %s)，板块为：%s，已重试%d次，同步时间为：%s\n错误信息为：%s",
                                ccId, result, maxRetries, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), e.getMessage());
                        LOGGER.error(errorMsg, e);
                        // 发送邮件通知
//                        boolean eMailSync = SDK.getNotificationAPI().sendEMailSync(SEND_EMAIL_ACCOUNT_NUMBER, RECEIVE_EMAIL_ACCOUNT, null, "数据贯通数据库连接通知", errorMsg);
//                        if (eMailSync){
//                            LOGGER.info("短信发送成功");
//                        }else {
//                            LOGGER.info("短信发送失败");
//                        }
                        throw new RuntimeException(errorMsg, e);
                    }
                    LOGGER.warn("获取数据库连接失败(CC_ID: {})，第{}次重试...", ccId, retry + 1);
                    Thread.sleep(retryDelayMs);
                }
            }

            String DBname = supply.getName();
            LOGGER.info("数据库为：{}",DBname);

            // 计算时间范围并拆分为30天一组
            List<Date[]> timeRanges = splitTimeRange(startDated, endDated, 30);
            LOGGER.info("时间范围拆分为 {} 个查询区间", timeRanges.size());
            String maxPartition = "";
            if (partitionField != null && !partitionField.isEmpty()) {
                // 1. 查询最大分区值
                String maxPartitionSql = "SELECT MAX(" + partitionField + ") AS max_partition FROM " + tableName;
                List<RowMap> maxPartitionResult = rdsapi.getMaps(maxPartitionSql);

                if (maxPartitionResult.isEmpty() || maxPartitionResult.get(0).get("max_partition") == null) {
                    LOGGER.warn("表[{}]没有找到分区字段[{}]的数据", tableName, partitionField);
                    return;
                }

                maxPartition = maxPartitionResult.get(0).getString("max_partition");
                LOGGER.info("表[{}]的最大分区为: {}", tableName, maxPartition);
            }

            for (int i = 0; i < timeRanges.size(); i++) {
                Date[] range = timeRanges.get(i);
                String startDate = simpleDateFormat2.format(range[0])+" 00:00:00";
                String endDate = simpleDateFormat2.format(range[1])+" 23:59:59";

                LOGGER.info("正在处理第 {} 个时间区间: {} 至 {}", i + 1, startDate, endDate);
                if ("ORACLE".equalsIgnoreCase(DBname)) {
                    // 构建查询条件
                    StringBuilder conditionBuilder = new StringBuilder();
                    StringBuilder orderByBuilder = new StringBuilder(); // 用于构建排序子句
                    List<Object> params = new ArrayList<>(); // 存储查询参数

                    // 分区字段和时间字段组合查询条件
                    if (partitionField != null && !partitionField.isEmpty()) {

                        // 添加分区条件
                        conditionBuilder.append(partitionField)
                                .append(" = '")
                                .append(maxPartition)
                                .append("'");

                        // 如果时间字段存在，添加时间范围条件
                        if (timeField != null && !timeField.isEmpty()) {
                            conditionBuilder.append("TO_DATE(")
                                    .append(timeField)
                                    .append(", '")
                                    .append(ORACLE_DATE_FORMAT)
                                    .append("') >= TO_DATE(?, '")
                                    .append(ORACLE_DATE_FORMAT)
                                    .append("') AND TO_DATE(")
                                    .append(timeField)
                                    .append(", '")
                                    .append(ORACLE_DATE_FORMAT)
                                    .append("') < TO_DATE(?, '")
                                    .append(ORACLE_DATE_FORMAT)
                                    .append("')");

                            // 构建排序子句
                            orderByBuilder.append(" ORDER BY ").append(timeField);
                            if (jezd != null && !jezd.isEmpty()) {
                                orderByBuilder.append(", ").append(jezd);
                            }
                            orderByBuilder.append(" DESC");
                            params.add(startDate);
                            params.add(endDate);
                        }
                    } else if (timeField != null && !timeField.isEmpty()) {
                        // 没有分区字段，但时间字段存在，使用时间范围条件
                        // 仅时间范围条件（使用占位符）
                        conditionBuilder.append("TO_DATE(")
                                .append(timeField)
                                .append(", '")
                                .append(ORACLE_DATE_FORMAT)
                                .append("') >= TO_DATE(?, '")
                                .append(ORACLE_DATE_FORMAT)
                                .append("') AND TO_DATE(")
                                .append(timeField)
                                .append(", '")
                                .append(ORACLE_DATE_FORMAT)
                                .append("') < TO_DATE(?, '")
                                .append(ORACLE_DATE_FORMAT)
                                .append("')");
                        // 构建排序子句
                        orderByBuilder.append(" ORDER BY ").append(timeField);
                        if (jezd != null && !jezd.isEmpty()) {
                            orderByBuilder.append(", ").append(jezd);
                        }
                        orderByBuilder.append(" DESC");
                        params.add(startDate);
                        params.add(endDate);
                    } else {
                        // 既没有分区字段也没有时间字段，查询全表
                        LOGGER.warn("警告：未配置分区字段和时间字段，将查询全表数据！");
                        conditionBuilder.append("1=1");
                    }

                    // 分页查询数据
                    do {
                        // 使用Oracle分页语法 (12c+)
                        String querySql = "SELECT * FROM " + tableName +
                                " WHERE " + conditionBuilder.toString() +
                                orderByBuilder.toString(); // 添加排序子句

                        LOGGER.info("执行Oracle查询: {}", querySql);

                        List<RowMap> pageData;
                        // 根据条件类型执行查询
                        if (partitionField != null && !partitionField.isEmpty() &&
                                timeField != null && !timeField.isEmpty()) {
                            // 分区+时间范围查询
                            pageData = rdsapi.getMaps(querySql, startDate, endDate);
                        } else if (timeField != null && !timeField.isEmpty()) {
                            // 仅时间范围查询
                            pageData = rdsapi.getMaps(querySql, startDate, endDate);
                        } else {
                            // 无时间范围查询（仅分区或全表）
                            pageData = rdsapi.getMaps(querySql);
                        }

                        if (pageData != null && !pageData.isEmpty()) {
                            // 直接处理当前页数据
                            int successCount = this.processAndInsertData(pageData, fieldMappings, targetTable);
                            totalRows += pageData.size();
                            totalSuccess += successCount;
                            hasMore = pageData.size() == PAGE_SIZE;
                            pageNo++;
                        } else {
                            hasMore = false;
                        }
                    } while (hasMore);
                } else {
                    // 构建查询条件
                    StringBuilder conditionBuilder = new StringBuilder();
                    // 修改点：分区字段和时间字段组合查询条件
                    if (partitionField != null && !partitionField.isEmpty()) {

                        // 添加分区条件
                        conditionBuilder.append(partitionField)
                                .append(" = '")
                                .append(maxPartition)
                                .append("'");

                        // 如果时间字段存在，添加时间范围条件
                        if (timeField != null && !timeField.isEmpty()) {
                            conditionBuilder.append(" AND ")
                                    .append(timeField).append(" >= ?")
                                    .append(" AND ")
                                    .append(timeField).append(" < ?")
                                    .append(" ORDER BY ").append(timeField);
                            if (jezd != null && !jezd.isEmpty()) {
                                conditionBuilder.append(", " + jezd + " ");
                            }
                            conditionBuilder.append(" DESC");
                        }
                    } else if (timeField != null && !timeField.isEmpty()) {
                        // 没有分区字段，但时间字段存在，使用时间范围条件
                        conditionBuilder.append(timeField).append(" >= ?")
                                .append(" AND ")
                                .append(timeField).append(" < ?")
                                .append(" ORDER BY ").append(timeField);
                        if (jezd != null && !jezd.isEmpty()) {
                            conditionBuilder.append(", " + jezd + " ");
                        }
                        conditionBuilder.append(" DESC");
                    } else {
                        // 既没有分区字段也没有时间字段，查询全表（实际应避免这种情况）
                        LOGGER.warn("警告：未配置分区字段和时间字段，将查询全表数据！");
                        conditionBuilder.append("1=1");
                    }
                    // 分页查询数据
                    do {
                        String querySql = "SELECT * FROM " + tableName +
                                " WHERE " + conditionBuilder.toString();

                        LOGGER.info("执行查询querySql: {}", querySql);

                        List<RowMap> pageData;
                        // 根据条件类型执行查询
                        if (partitionField != null && !partitionField.isEmpty() &&
                                timeField != null && !timeField.isEmpty()) {
                            // 分区+时间范围查询
                            pageData = rdsapi.getMaps(querySql, startDate, endDate);
                        } else if (timeField != null && !timeField.isEmpty()) {
                            // 仅时间范围查询
                            pageData = rdsapi.getMaps(querySql, startDate, endDate);
                        } else {
                            // 无时间范围查询（仅分区或全表）
                            pageData = rdsapi.getMaps(querySql);
                        }

                        if (pageData != null && !pageData.isEmpty()) {
                            // 直接处理当前页数据
                            int successCount = this.processAndInsertData(pageData, fieldMappings, targetTable);
                            totalRows += pageData.size();
                            totalSuccess += successCount;
                            hasMore = pageData.size() == PAGE_SIZE;
                            pageNo++;
                        } else {
                            hasMore = false;
                        }
                    } while (hasMore);
                }
            }

            LOGGER.info("从表[{}]共查询到{}条数据，成功同步{}条数据",
                    tableName, totalRows, totalSuccess);
        } catch (Exception e) {
            throw new RuntimeException("查询源表[" + tableName + "]数据失败: " + e.getMessage(), e);
        }
    }

    /**
     * 获取目标表时间字段名
     * @param mappings 字段映射配置列表
     * @param sourceTimeField 源表时间字段名
     * @return 目标表时间字段名，未找到返回null
     */
    @Override
    public String getTargetTimeField(List<BO> mappings, String sourceTimeField) {
        for (BO mapping : mappings) {
            if (sourceTimeField.equals(mapping.getString("TBBZD"))) {
                return mapping.getString("LDBZD");
            }
        }
        return null;
    }


    /**
     * 删除目标表中指定时间范围的数据
     * @param targetTable 目标表名
     * @param targetTimeField 目标表时间字段名
     * @param startDate 开始时间
     * @param endDate 结束时间
     * @throws RuntimeException 删除失败时抛出
     */
    @Override
    public void deleteTargetData(String targetTable, String targetTimeField,
                                  Date startDate, Date endDate) {
        try {
            String deleteSql = "DELETE FROM " + targetTable +
                    " WHERE " + targetTimeField + " >= ? ";
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            String startDateStr = simpleDateFormat.format(startDate)+" 00:00:00";
            String endDateStr = simpleDateFormat.format(endDate)+" 23:59:59";
            int deletedCount = DBSql.update(deleteSql, new Object[]{startDateStr});
            LOGGER.info("已删除目标表[{}]中{}条数据（时间范围: {} ）",
                    targetTable, deletedCount, startDateStr);
        } catch (Exception e) {
            throw new RuntimeException("删除目标表数据失败: " + e.getMessage(), e);
        }
    }

    /**
     * 删除目标表所有数据
     */
    @Override
    public void deleteAllTargetData(String targetTable) {
        try {
            String deleteSql = "DELETE FROM " + targetTable ;
            int deletedCount = DBSql.update(deleteSql);
            LOGGER.info("已全量删除目标表[{}]中{}条数据", targetTable, deletedCount);
        } catch (Exception e) {
            throw new RuntimeException("全量删除目标表数据失败: " + e.getMessage(), e);
        }
    }

    /**
     * 将时间范围拆分为多个区间，确保区间连续（下一个区间的开始时间 = 上一个区间的结束时间）且无重叠
     * 所有日期均强制转换为 yyyy-MM-dd 格式（时间部分固定为 00:00:00）
     * @param startDate 开始时间（建议传入 yyyy-MM-dd 格式，若含时间部分会自动截断为 00:00:00）
     * @param endDate 结束时间（建议传入 yyyy-MM-dd 格式，若含时间部分会自动截断为 00:00:00）
     * @param daysInterval 每个区间的间隔天数（例如：传入 7 表示每个区间覆盖 7 天，如 2024-01-01~2024-01-08）
     * @return 时间区间列表，每个元素为 [开始时间, 结束时间]，均为 yyyy-MM-dd 00:00:00 格式
     */
    public static List<Date[]> splitTimeRange(Date startDate, Date endDate, int daysInterval) {
        // 1. 入参合法性校验：避免无效区间或负间隔
        if (startDate == null || endDate == null) {
            throw new IllegalArgumentException("开始时间和结束时间不可为 null");
        }
        if (daysInterval < 1) {
            throw new IllegalArgumentException("间隔天数必须大于等于 1");
        }
        // 2. 初始化日历并强制截断时间部分（确保所有日期都是 yyyy-MM-dd 00:00:00）
        Calendar calendar = Calendar.getInstance();
        setTimeToStartOfDay(calendar); // 先重置时间，再设日期（避免原日期含时间的影响）
        calendar.setTime(startDate);
        Date currentStart = calendar.getTime();

        Calendar endCal = Calendar.getInstance();
        setTimeToStartOfDay(endCal);
        endCal.setTime(endDate);
        Date finalEnd = endCal.getTime();
        // 额外校验：开始时间不能晚于结束时间
        if (currentStart.after(finalEnd)) {
            throw new IllegalArgumentException("开始时间不能晚于结束时间");
        }
        List<Date[]> ranges = new ArrayList<>();
        // 3. 循环拆分区间：核心逻辑确保“下一个开始 = 上一个结束”
        while (true) {
            // 计算当前区间的结束时间：开始时间 + 间隔天数（如 2024-01-01 + 7天 = 2024-01-08）
            calendar.add(Calendar.DAY_OF_MONTH, daysInterval);
            setTimeToStartOfDay(calendar); // 再次确保时间部分为 00:00:00
            Date currentEnd = calendar.getTime();
            // 处理最后一个区间：若计算的结束时间超过最终结束时间，强制对齐到 finalEnd
            if (currentEnd.after(finalEnd)) {
                currentEnd = finalEnd;
            }
            // 添加当前区间 [currentStart, currentEnd]
            ranges.add(new Date[]{currentStart, currentEnd});
            // 终止条件：当前区间的结束时间已等于最终结束时间，无需继续拆分
            if (currentEnd.equals(finalEnd)) {
                break;
            }
            // 准备下一个区间的开始时间：当前结束时间（满足“连续”要求）
            currentStart = currentEnd;
        }

        return ranges;
    }

    /**
     * 工具方法：将 Calendar 的时间部分强制设置为当天 00:00:00（截断时分秒毫秒）
     * @param calendar 待处理的 Calendar 实例（非 null）
     */
    private static void setTimeToStartOfDay(Calendar calendar) {
        if (calendar == null) {
            throw new IllegalArgumentException("Calendar 实例不可为 null");
        }
        calendar.set(Calendar.HOUR_OF_DAY, 0);  // 24小时制，0 表示凌晨 0 点
        calendar.set(Calendar.MINUTE, 0);       // 分钟设为 0
        calendar.set(Calendar.SECOND, 0);       // 秒设为 0
        calendar.set(Calendar.MILLISECOND, 0);  // 毫秒设为 0
    }

    /**
     * 处理并插入数据到目标表
     * @param sourceData 源数据列表
     * @param mappings 字段映射配置
     * @param targetTable 目标表名
     */
    public int processAndInsertData(List<RowMap> sourceData,
                                      List<BO> mappings, String targetTable) {
        String bkgs = "";
        Set<String> bkgsSet = null;
        if (sourceData.isEmpty()) {
            LOGGER.info("没有需要同步的数据");
            return 0;
        }

        List<BO> batchList = new ArrayList<>();
        int successCount = 0;
        int totalCount = sourceData.size();
        int processedCount = 0; // 已处理记录数

        for (int i = 0; i < totalCount; i++) {
            RowMap record = sourceData.get(i);
            processedCount++; // 增加已处理计数
            try {
                // 字段映射转换
                BO targetData = convertFields(record, mappings);
                batchList.add(targetData);


                // 批量插入条件：达到批处理大小或最后一条
                if (batchList.size() >= PAGE_SIZE || i == totalCount - 1) {
                    // 使用管理员权限批量插入
                    SDK.getBOAPI().createDataBO(targetTable, batchList, UserContext.fromUID("admin"));
                    successCount += batchList.size();
                    bkgsSet = sourceData.stream().map(o->o.getString("BKGS")).distinct().collect(Collectors.toSet());
                    batchList.clear(); // 清空批次
                }
            } catch (Exception e) {
                LOGGER.error("数据处理失败: {}", e.getMessage(), e);
            }
        }
        // 增加详细日志输出：共处理多少条，成功同步多少条
        bkgs = bkgsSet.stream().collect(Collectors.joining(", "));
        LOGGER.info("同步板块为：{}；落地表为：{}，本次处理{}条数据，成功同步{}条数据到表[{}]",
                bkgs,targetTable,processedCount, successCount, targetTable);
        return successCount;
    }

    /**
     * 字段映射转换
     * @param source 源数据记录
     * @param mappings 字段映射配置
     * @return 转换后的BO对象
     */
    public BO convertFields(RowMap source, List<BO> mappings) {
        BO target = new BO();

        for (BO mapping : mappings) {
            String sourceField = mapping.getString("TBBZD");
            String targetField = mapping.getString("LDBZD");

//            if (!source.containsKey(sourceField)) {
//                LOGGER.debug("源字段[{}]不存在于查询结果中", sourceField);
//                continue;
//            }

            String operationExpr = mapping.getString("TBBZDJSLJ");
            if (StringUtils.isNotBlank(operationExpr)) {
                // 解析运算表达式 (格式: [运算符][数字])
                char operator = operationExpr.charAt(0);
                String numberPart = operationExpr.substring(1);

                try {
                    // 获取源值并转换为BigDecimal
                    String sourceValue = source.getString(sourceField);
                    if (StringUtils.isBlank(sourceValue)) {
                        target.set(targetField, null);
                        continue;
                    }

                    if ("GG".equals(sourceField)){
                        String string = source.getString(sourceField);
                        if (StringUtils.isNotBlank(string) && string.contains("×")){
                            String[] split = string.split("×");
                            String s = split[split.length - 1];
                            target.set(targetField, s);
                            continue;
                        }
                    }

                    BigDecimal sourceNum = new BigDecimal(sourceValue);
                    BigDecimal operand = new BigDecimal(numberPart);
                    BigDecimal result;
                    // 执行相应运算
                    switch (operator) {
                        case '*':
                            result = sourceNum.multiply(operand);
                            break;
                        case '/':
                            if (BigDecimal.ZERO.compareTo(operand) == 0) {
                                LOGGER.error("除零错误: 源字段[{}] 除数为0", sourceField);
                                result = sourceNum; // 避免除零异常
                            } else {
                                // 除法保留10位小数并四舍五入
                                result = sourceNum.divide(operand, 10, RoundingMode.HALF_UP);
                            }
                            break;
                        case '+':
                            result = sourceNum.add(operand);
                            break;
                        case '-':
                            result = sourceNum.subtract(operand);
                            break;
                        default:
                            LOGGER.error("未知运算符: {} 字段[{}]", operator, sourceField);
                            result = sourceNum;
                    }
                    target.set(targetField, result);
                } catch (NumberFormatException e) {
                    LOGGER.error("数值转换失败: 源字段[{}]={}, 操作数={}",
                            sourceField, source.getString(sourceField), numberPart, e);
                    target.set(targetField, source.getString(sourceField));
                }
            } else {
                if ("GG".equals(sourceField)){
                    String string = source.getString(sourceField);
                    if (StringUtils.isNotBlank(string) && string.contains("×")){
                        String[] split = string.split("×");
                        String s = split[split.length - 1];
                        target.set(targetField, s);
                    }else {
                        // 无运算表达式时直接复制原始值
                        target.set(targetField, StringUtils.isNotBlank(string)?string:"");
                    }
                }else {
                    // 无运算表达式时直接复制原始值
                    target.set(targetField, source.getString(sourceField));
                }
            }
            String ldzdmrz = mapping.getString("LDZDMRZ");
            if (StringUtils.isNotBlank(ldzdmrz)){
                target.set(mapping.getString("LDBZD"),ldzdmrz);
            }
        }
        return target;
    }

    /**
     * 销售各板块数据汇总表
     * @param configs
     */
    @Override
    public void sumBkTable(List<BO> configs) {
        Map<String, List<BO>> configsByPlate = configs.stream()
                .collect(Collectors.groupingBy(bo -> bo.getString("SSBK")));

        // 遍历处理每个板块的配置
        for (Map.Entry<String, List<BO>> entry : configsByPlate.entrySet()) {
            String plate = entry.getKey();
            List<BO> plateConfigs = entry.getValue();
            LOGGER.info("处理板块【{}】的{}条配置", plate, plateConfigs.size());

            // 处理当前板块的每条配置
            for (BO mainConfig : plateConfigs) {
                String targetTable = mainConfig.getString("LDB");//落地表
                String timeField = mainConfig.getString("SJZD");//时间字段
                String bindId = mainConfig.getString("BINDID");//bindid
                String tablename = mainConfig.getString("TABLENAME");//同步表名

                String hzb = "";
                try {
                    if ("销售表".equals(tablename)){
                        hzb = "BO_EU_BNBM_DATALINKUP_XS_XSL_HZ";
                    }else {
                        hzb = "BO_EU_BNBM_DATALINKUP_XS_YSL";
                    }
                    // 查询子表字段映射配置
                    List<BO> fieldMappings = SDK.getBOAPI()
                            .query("BO_EU_BNBM_DATALINKUP_SJGTPZ_SUB")
                            .addQuery("BINDID =", bindId)
                            .list();
                    //获取板块公司
                    List<RowMap> maps = DBSql.getMaps("SELECT BKGS FROM " + targetTable + " GROUP BY BKGS");
                    String bkgss = maps.stream().map(o -> o.getString("BKGS")).collect(Collectors.joining(","));
                    String[] bkgsArr = bkgss.split(",");

                    // 根据时间字段是否为空设置日期范围
                    Date startDate = null;
                    Date endDate = null;

                    // 删除目标表数据（根据时间字段是否为空决定删除范围）
                    if (timeField == null || timeField.isEmpty()) {
                        // 全量删除
                        for (String bkgs : bkgsArr) {
                            String deleteSql = "DELETE FROM "+hzb+" WHERE BKGS = '"+bkgs+"'";
                            int deletedCount = DBSql.update(deleteSql);
                            LOGGER.info("已删除目标表[{}]中{}条数据（时间范围: {} - {})",
                                    hzb,deletedCount, startDate, endDate);
                        }
                        // 根据时间范围增加数据分页查询数据存储到BO_EU_BNBM_DATALINKUP_XS_XSL_HZ
                        // 全量分页迁移数据到汇总表
                        if (tablename.equals("应收表")){
                            summarizeScopeDataYs(targetTable, null, null, null, hzb);
                        }else {
                            summarizeScopeData(targetTable, null, null, null, hzb);
                        }
                    } else {
                        // 计算时间范围（当前日期-30天 ~ 昨天）
                        Calendar cal = Calendar.getInstance();
                        cal.add(Calendar.DATE, -1); // 昨天
                        endDate = cal.getTime();
                        cal.add(Calendar.DATE, -DAYS_BACK + 1); // 30天前（含）
                        startDate = cal.getTime();
                        // 获取目标表时间字段名
                        String targetTimeField = getTargetTimeField(fieldMappings, timeField);
                        if (targetTimeField == null) {
                            LOGGER.error("无法找到源时间字段[{}]对应的目标表字段，跳过同步", timeField);
                        }
                        // 按时间范围删除
                        for (String bkgs : bkgsArr) {
                            String deleteSql = "DELETE FROM " + hzb +
                                    " WHERE BKGS = '"+bkgs+"' AND " + targetTimeField + " >= ? ";
                            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
                            String startDateStr = simpleDateFormat.format(startDate)+" 00:00:00";
                            int deletedCount = DBSql.update(deleteSql, new Object[]{startDateStr});
                            LOGGER.info("已删除目标表["+hzb+"]中{}条数据（时间范围: {} ）",
                                    deletedCount, startDateStr);
                        }
                        // 根据时间范围增加数据分页查询数据存储到BO_EU_BNBM_DATALINKUP_XS_XSL_HZ
                        // 按时间范围分页迁移数据到汇总表
                        if (tablename.equals("应收表")){
                            summarizeScopeDataYs(targetTable, startDate, endDate, targetTimeField, hzb);
                        }else {
                            summarizeScopeData(targetTable, startDate, endDate, targetTimeField, hzb);
                        }
                    }
                } catch (Exception e) {
                    LOGGER.error("处理配置失败 [板块={}, BindID={}]: {}",
                            plate, mainConfig.getString("BINDID"), e.getMessage(), e);
                }
            }
        }
    }

    /**
     * 汇总各板块销售数据汇总
     * @param targetTable 落地表字段
     * @param startDated 开始时间
     * @param endDated 结束时间
     * @param targetTimeField 时间范围字段
     * @param hzb 汇总表
     */
    @Override
    public void summarizeScopeData(String targetTable, Date startDated, Date endDated, String targetTimeField, String hzb) {
//        LOGGER.info("targetTable:{},startDated:{},endDated:{},targetTimeField:{},hzb:{}",targetTable,startDated,endDated,targetTimeField,hzb);
        int pageNo = 1;
        boolean hasMore;
        String pageSql = "";
        List<RowMap> pageData = null;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String startDate = "";
        String endDate = "";
        // 查询梦牌区域
        List<BO> mpqyList = SDK.getBOAPI().query("BO_EU_DATALINKUP_QYGSED").addQuery("BKGS = ", "梦牌").list();
        // 查询龙牌区域
        List<BO> lpqyList = SDK.getBOAPI().query("BO_EU_QYGX").list();
        try {
            do {
                if (startDated == null || endDated == null) {
                    pageSql = "SELECT * FROM " + targetTable +
                            " ORDER BY ID LIMIT " + PAGE_SIZE + " OFFSET " + (pageNo - 1) * PAGE_SIZE;
                    pageData = DBSql.getMaps(pageSql);
                } else {
                    startDate = simpleDateFormat.format(startDated) + " 00:00:00";
                    endDate = simpleDateFormat.format(endDated) + " 23:59:59";
                    pageSql = "SELECT * FROM " + targetTable +
                            " WHERE " + targetTimeField + " >= '" + startDate + "' " +
                            " ORDER BY ID LIMIT " + PAGE_SIZE + " OFFSET " + (pageNo - 1) * PAGE_SIZE;
                    LOGGER.info("执行查询的sql：{}", pageSql);
                    pageData = DBSql.getMaps(pageSql);
                }

                if (pageData.isEmpty()) break;

                List<BO> bos = new ArrayList<>();
                for (RowMap map : pageData) {
                    BO bo = new BO();
                    if ("梦牌".equals(map.getString("BKGS")) && "梦牌新材料（平邑）有限公司".equals(map.getString("XSZZ"))){
                        continue;
                    }
                    if ("北新防水".equals(map.getString("BKGS")) && "否".equals(map.getString("F_SFNBJY"))
                            && !map.getString("XSZZ").equals("北新防水工程（辽宁）有限公司")
                            && !map.getString("XSZZ").equals("北新防水工程（四川）有限公司")
                            && StringUtils.isBlank(map.getString("QYGS").trim())){
                        continue;
                    }
                    // 复制所有字段（排除系统字段）
                    for (String key : map.keySet()) {
                        if (!key.equalsIgnoreCase("ID") &&
                                !key.equalsIgnoreCase("ORGID") &&
                                !key.equalsIgnoreCase("CREATEDATE") &&
                                !key.equalsIgnoreCase("CREATEUSER") &&
                                !key.equalsIgnoreCase("UPDATEDATE") &&
                                !key.equalsIgnoreCase("UPDATEUSER") &&
                                !key.equalsIgnoreCase("ISEND") &&
                                !key.equalsIgnoreCase("BINDID")) {
                            if (StringUtils.isNotBlank(targetTimeField)) {
                                String targetTimeField1 = map.getString(targetTimeField);
                                Date parse = UtilDate.parse(targetTimeField1);
                                int year = UtilDate.getYear(parse);
                                String monthFormat = UtilDate.monthFormat(parse);
                                int day = UtilDate.getDay(parse);
                                bo.set("YEARMONTH", year + monthFormat);
                                bo.set("YEAR", year);
                                bo.set("MONTH", monthFormat);
                                bo.set("DAY", day);
                            }
                            bo.set(key, map.get(key));
                        }
                    }
                    //销售汇总表修改区域公司
                    if ("梦牌".equals(bo.getString("BKGS"))) {
                        String cs = "";
                        String sq = "";
                        if (hzb.equals("BO_EU_BNBM_DATALINKUP_XS_XSL_HZ")){
                            cs = StringUtils.isBlank(bo.getString("CS"))?"":bo.getString("CS");
                            sq = StringUtils.isBlank(bo.getString("SQ"))?"":bo.getString("SQ");
                        }else {
                            cs = StringUtils.isBlank(bo.getString("SHIQU"))?"":bo.getString("SHIQU");
                            sq = StringUtils.isBlank(bo.getString("SHENGQU"))?"":bo.getString("SHENGQU");
                        }
                        boolean found = false; // 标记是否找到匹配
                        for (BO bo1 : mpqyList) {
                            String ss = bo1.getString("SS");
                            if (ss.contains(cs)) {
                                bo.set("QYGS", bo1.getString("QYGS"));
                                found = true;
                                break; // 找到后立即跳出循环
                            } else if (ss.contains(sq)) {
                                bo.set("QYGS", bo1.getString("QYGS"));
                                found = true;
                                break; // 找到后立即跳出循环
                            }
                        }
                        if (!found) {
                            bo.set("QYGS", "其他"); // 循环结束后仍未找到则设為"其他"
                        }
                    }else if ("龙牌".equals(bo.getString("BKGS"))){
                        String xszz = StringUtils.isBlank(bo.getString("XSZZ"))?"":bo.getString("XSZZ");
                        boolean found = false; // 标记是否找到匹配
                        // 修改龙牌的区域公司
                        for (BO bo1 : lpqyList) {
                            String swfb = bo1.getString("SWFB");
                            String[] split = swfb.split(",");
                            String qygs = bo1.getString("QYGS");
                            for (String s : split) {
                                if (xszz.equals(s)){
                                    bo.set("QYGS",qygs);
                                    found = true;
                                }
                            }
                        }
                        if (!found) {
                            bo.set("QYGS", "其他"); // 循环结束后仍未找到则设為"其他"
                        }
                    }
                    // 匹配公装家装类别，如果未匹配到，则默认未其他
                    if (hzb.equals("BO_EU_BNBM_DATALINKUP_XS_XSL_HZ")) {
                        List<BO> gzjzList = SDK.getBOAPI().query("BO_EU_DATALINKUP_CPJZGZJCMC").desc().list();
                        String[] gzjzStrings = SaleUtil.matchSingleField(bo.getString("BKGS"), bo.getString("LB_1"),
                                bo.getString("LB_2"), bo.getString("LB_3"), gzjzList, "其他", "其他", "其他");
                        bo.set("JCMC", gzjzStrings[1]);//面材/基材
                        bo.set("JZGZ", gzjzStrings[0]);//家装/工装
                        bo.set("TYPE_PP", gzjzStrings[2]);//产品类型
                        bo.set("PBGNB", gzjzStrings[3]);//普板/功能板
                        bo.set("ISOEM", gzjzStrings[4]);//是否OEM板
                    }
                    bos.add(bo);
                }

                SDK.getBOAPI().createDataBO(hzb, bos, UserContext.fromUID("admin"));
                LOGGER.info("已迁移{}条数据到汇总表（页号: {}，时间范围: {} - {}）",
                        bos.size(), pageNo, startDate, endDate);

                hasMore = pageData.size() == PAGE_SIZE;
                pageNo++;
            } while (hasMore);
        }catch (Exception e){
            LOGGER.error("汇总数据失败 [汇总表={}, 第几页={}]: {}",
                    hzb, PAGE_SIZE, e.getMessage(), e);
        }
    }

    /**
     * 汇总各板块销售数据汇总
     * @param targetTable 落地表字段
     * @param startDated 开始时间
     * @param endDated 结束时间
     * @param targetTimeField 时间范围字段
     * @param hzb 汇总表
     */
    @Override
    public void summarizeScopeDataYs(String targetTable, Date startDated, Date endDated, String targetTimeField, String hzb) {
//        LOGGER.info("targetTable:{},startDated:{},endDated:{},targetTimeField:{},hzb:{}",targetTable,startDated,endDated,targetTimeField,hzb);
        int pageNo = 1;
        boolean hasMore;
        String pageSql = "";
        List<RowMap> pageData = null;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String startDate = "";
        String endDate = "";
        // 查询梦牌区域
        List<BO> mpqyList = SDK.getBOAPI().query("BO_EU_DATALINKUP_QYGSED").addQuery("SS IS NOT NULL",null).addQuery("BKGS = ", "梦牌").list();
        // 查询龙牌区域
        List<BO> lpqyList = SDK.getBOAPI().query("BO_EU_QYGX").list();
        try {
            do {
                // 构建查询每月最大日期的SQL
                String maxDateSubQuery = "SELECT MAX(DATE(" + targetTimeField + ")) as max_date, " +
                        "DATE_FORMAT(" + targetTimeField + ", '%Y-%m') as month " +
                        "FROM " + targetTable;
                // 根据是否传入时间范围添加条件
                if (startDated != null && endDated != null) {
                    startDate = simpleDateFormat.format(startDated);
                    endDate = simpleDateFormat.format(endDated);
                    maxDateSubQuery += " WHERE " + targetTimeField + " BETWEEN '" + startDate + "' AND '" + endDate + "'";
                }
                maxDateSubQuery += " GROUP BY DATE_FORMAT(" + targetTimeField + ", '%Y-%m')";
                // 主查询：通过连接查询获取每月最大日期的全部数据
                pageSql = "SELECT t.* FROM " + targetTable + " t " +
                        "INNER JOIN (" + maxDateSubQuery + ") tmp " +
                        "ON DATE(t." + targetTimeField + ") = tmp.max_date " +
                        "ORDER BY t.ID LIMIT " + PAGE_SIZE + " OFFSET " + (pageNo - 1) * PAGE_SIZE;

                pageData = DBSql.getMaps(pageSql);

                if (pageData.isEmpty()) break;

                List<BO> bos = new ArrayList<>();
                for (RowMap map : pageData) {
                    BO bo = new BO();
                    if ("梦牌".equals(map.getString("BKGS")) && "梦牌新材料（平邑）有限公司".equals(map.getString("XSZZ"))){
                        continue;
                    }
                    if ("北新防水".equals(map.getString("BKGS")) && "否".equals(map.getString("F_SFNBJY"))
                            && !map.getString("XSZZ").equals("北新防水工程（辽宁）有限公司")
                            && !map.getString("XSZZ").equals("北新防水工程（四川）有限公司")
                            && StringUtils.isBlank(map.getString("QYGS").trim())){
                        continue;
                    }
                    // 复制所有字段（排除系统字段）
                    for (String key : map.keySet()) {
                        if (!key.equalsIgnoreCase("ID") &&
                                !key.equalsIgnoreCase("ORGID") &&
                                !key.equalsIgnoreCase("CREATEDATE") &&
                                !key.equalsIgnoreCase("CREATEUSER") &&
                                !key.equalsIgnoreCase("UPDATEDATE") &&
                                !key.equalsIgnoreCase("UPDATEUSER") &&
                                !key.equalsIgnoreCase("ISEND") &&
                                !key.equalsIgnoreCase("BINDID")) {
                            if (StringUtils.isNotBlank(targetTimeField)) {
                                String targetTimeField1 = map.getString(targetTimeField);
                                Date parse = UtilDate.parse(targetTimeField1);
                                int year = UtilDate.getYear(parse);
                                String monthFormat = UtilDate.monthFormat(parse);
                                int day = UtilDate.getDay(parse);
                                bo.set("YEARMONTH", year + monthFormat);
                                bo.set("YEAR", year);
                                bo.set("MONTH", monthFormat);
                                bo.set("DAY", day);
                            }
                            bo.set(key, map.get(key));
                        }
                    }
                    //销售汇总表修改区域公司
                    if ("梦牌".equals(bo.getString("BKGS"))) {
                        String cs = "";
                        String sq = "";
                        if (hzb.equals("BO_EU_BNBM_DATALINKUP_XS_XSL_HZ")){
                            cs = StringUtils.isBlank(bo.getString("CS"))?"":bo.getString("CS");
                            sq = StringUtils.isBlank(bo.getString("SQ"))?"":bo.getString("SQ");
                        }else {
                            cs = StringUtils.isBlank(bo.getString("SHIQU"))?"":bo.getString("SHIQU");
                            sq = StringUtils.isBlank(bo.getString("SHENGQU"))?"":bo.getString("SHENGQU");
                        }
                        boolean found = false; // 标记是否找到匹配
                        for (BO bo1 : mpqyList) {
                            String ss = bo1.getString("SS");
                            if (ss.contains(cs)) {
                                bo.set("QYGS", bo1.getString("QYGS"));
                                found = true;
                                break; // 找到后立即跳出循环
                            } else if (ss.contains(sq)) {
                                bo.set("QYGS", bo1.getString("QYGS"));
                                found = true;
                                break; // 找到后立即跳出循环
                            }
                        }
                        if (!found) {
                            bo.set("QYGS", "其他"); // 循环结束后仍未找到则设為"其他"
                        }
                    }else if ("龙牌".equals(bo.getString("BKGS"))){
                        String xszz = StringUtils.isBlank(bo.getString("XSZZ"))?"":bo.getString("XSZZ");
                        boolean found = false; // 标记是否找到匹配
                        // 修改龙牌的区域公司
                        for (BO bo1 : lpqyList) {
                            String swfb = bo1.getString("SWFB");
                            String[] split = swfb.split(",");
                            String qygs = bo1.getString("QYGS");
                            for (String s : split) {
                                if (xszz.equals(s)){
                                    bo.set("QYGS",qygs);
                                    found = true;
                                }
                            }
                        }
                        if (!found) {
                            bo.set("QYGS", "其他"); // 循环结束后仍未找到则设為"其他"
                        }
                    }
                    bos.add(bo);
                }

                SDK.getBOAPI().createDataBO(hzb, bos, UserContext.fromUID("admin"));
                LOGGER.info("已迁移{}条数据到汇总表（页号: {}，时间范围: {} - {}）",
                        bos.size(), pageNo, startDate, endDate);

                hasMore = pageData.size() == PAGE_SIZE;
                pageNo++;
            } while (hasMore);
            //每月1号删除上月去年同期去除每月最后一天的数据，删除当月去年同期全量数据后新增当月去年同期全量数据，
            // 每月1号执行的任务
            LocalDate now = LocalDate.now();
            int dayOfMonth = now.getDayOfMonth();

//            if (dayOfMonth == 1) {
//                try {
//                    LOGGER.info("开始执行每月1号的特殊数据处理任务");
//
//                    // 计算时间范围
//                    LocalDate lastYearMonthDate = now.minusYears(1).minusMonths(1);
//                    int lastYearMonthValue = lastYearMonthDate.getMonthValue();
//                    int lastyear = lastYearMonthDate.getYear();
//
//                    // 1. 删除当月去年同期全量数据
//                    String deleteCurrentMonthLastYearSql = "DELETE FROM " + hzb +
//                            " WHERE YEAR = " + lastyear +
//                            " AND MONTH = " + lastYearMonthValue;
//                    int update = DBSql.update(deleteCurrentMonthLastYearSql);
//                    LOGGER.info("已删除去年同期({}-{})的全量数据，删除了{}条数据", lastyear, lastYearMonthValue,update);
//
//                    // 2. 新增当月去年同期数据
//                    // 去年日期
//                    LocalDate minusYears = now.minusYears(1);
//                    // 计算去年同期第一天（当月第一天）
//                    LocalDate firstDayOfLastYearMonth = minusYears.withDayOfMonth(1);
//                    // 计算去年下个月第一天
//                    LocalDate firstDayOfNextMonthLastYear = minusYears.plusMonths(1).withDayOfMonth(1);
//                    // 格式化日期为字符串（根据数据库格式要求调整）
//                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
//                    String lastYearFirstDayStr = firstDayOfLastYearMonth.format(formatter);
//                    String lastYearNextMonthFirstDayStr = firstDayOfNextMonthLastYear.format(formatter);
//                    //删除清空去年同期数据
//                    int update1 = DBSql.update("DELETE FROM " + hzb +
//                            " WHERE YEAR = " + firstDayOfLastYearMonth.getYear() +
//                            " AND MONTH = " + firstDayOfLastYearMonth.getMonthValue());
//                    LOGGER.info("先清空{}去年同期数{}",lastYearFirstDayStr,update1);
//                    // 构建插入SQL（注意：需确保字段匹配且处理可能的主键冲突）
//
////                    String insertCurrentMonthLastYearSql = "INSERT INTO " + targetTable +
////                            " SELECT * FROM " + targetTable +
////                            " WHERE " + targetTimeField + " >= '" + lastYearFirstDayStr +
////                            "' AND " + targetTimeField + " < '" + lastYearNextMonthFirstDayStr + "'";
////                    DBSql.update(insertCurrentMonthLastYearSql);
//
//                    String CurrentMonthLastYearSql = "SELECT * FROM " + targetTable +
//                            " WHERE " +targetTimeField+">='"+lastYearFirstDayStr+"' AND "+
//                            targetTimeField+"<'"+lastYearNextMonthFirstDayStr+"'";
//                    LOGGER.info("查询去年同期sql：{}",CurrentMonthLastYearSql);
//                    ArrayList<BO> bos = new ArrayList<>();
//                    List<RowMap> maps = DBSql.getMaps(CurrentMonthLastYearSql);
//                    for (RowMap map : maps) {
//                        map.remove("ID");
//                        map.remove("ORGID");
//                        map.remove("CREATEDATE");
//                        map.remove("CREATEUSER");
//                        map.remove("UPDATEDATE");
//                        map.remove("UPDATEUSER");
//                        map.remove("ISEND");
//                        map.remove("BINDID");
//                        BO bo = new BO().setAll(map);
//                        bos.add(bo);
//                    }
//                    // 执行插入操作
//                    SDK.getBOAPI().createDataBO(hzb, bos, UserContext.fromUID("admin"));
//                    LOGGER.info("{}已新增当月去年同期数据",lastYearFirstDayStr);
//
//                } catch (Exception e) {
//                    LOGGER.error("每月1号任务执行失败: {}", e.getMessage(), e);
//                }
//            }


            // 修改为每天执行的任务：删除endDated的去年同期上一天的数据，新增endDated的去年同期当天数据
            if (endDated != null) {
                try {
                    LOGGER.info("开始执行每天的特殊数据处理任务");

                    // 计算endDated的去年同期日期
                    Calendar cal = Calendar.getInstance();
                    cal.setTime(endDated);
                    cal.add(Calendar.YEAR, -1);
                    Date lastYearEndDate = cal.getTime();

                    // 计算去年同期的上一天
                    cal.setTime(lastYearEndDate);
                    cal.add(Calendar.DAY_OF_MONTH, -1);
                    Date lastYearEndDateMinusOneDay = cal.getTime();

                    // 格式化日期
                    String endDateStr = simpleDateFormat.format(endDated);
                    String lastYearEndDateStr = simpleDateFormat.format(lastYearEndDate);
                    String lastYearEndDateMinusOneDayStr = simpleDateFormat.format(lastYearEndDateMinusOneDay);

                    // 1. 删除去年同期上一天的数据
                    String deleteLastYearMinusOneDaySql = "DELETE FROM " + hzb +
                            " WHERE " + targetTimeField + " = '" + lastYearEndDateMinusOneDayStr + "'";
                    int deleteCount = DBSql.update(deleteLastYearMinusOneDaySql);
                    LOGGER.info("已删除去年同期上一天({})的数据，删除了{}条数据", lastYearEndDateMinusOneDayStr, deleteCount);

                    // 2. 新增去年同期当天的数据
                    // 查询原表中去年同期的数据
                    String selectLastYearSql = "SELECT * FROM " + targetTable +
                            " WHERE " + targetTimeField + " = '" + lastYearEndDateStr + "'";
                    List<RowMap> lastYearData = DBSql.getMaps(selectLastYearSql);

                    List<BO> lastYearBos = new ArrayList<>();
                    for (RowMap map : lastYearData) {
                        BO bo = new BO();
                        // 复制所有字段（排除系统字段）
                        for (String key : map.keySet()) {
                            if (!key.equalsIgnoreCase("ID") &&
                                    !key.equalsIgnoreCase("ORGID") &&
                                    !key.equalsIgnoreCase("CREATEDATE") &&
                                    !key.equalsIgnoreCase("CREATEUSER") &&
                                    !key.equalsIgnoreCase("UPDATEDATE") &&
                                    !key.equalsIgnoreCase("UPDATEUSER") &&
                                    !key.equalsIgnoreCase("ISEND") &&
                                    !key.equalsIgnoreCase("BINDID")) {
                                if (StringUtils.isNotBlank(targetTimeField)) {
                                    String targetTimeField1 = map.getString(targetTimeField);
                                    Date parse = UtilDate.parse(targetTimeField1);
                                    int year = UtilDate.getYear(parse);
                                    String monthFormat = UtilDate.monthFormat(parse);
                                    int day = UtilDate.getDay(parse);
                                    bo.set("YEARMONTH", year + monthFormat);
                                    bo.set("YEAR", year);
                                    bo.set("MONTH", monthFormat);
                                    bo.set("DAY", day);
                                }
                                bo.set(key, map.get(key));
                            }
                        }
                        lastYearBos.add(bo);
                    }

                    // 执行插入操作
                    SDK.getBOAPI().createDataBO(hzb, lastYearBos, UserContext.fromUID("admin"));
                    LOGGER.info("已新增去年同期当天({})的数据，新增了{}条数据", lastYearEndDateStr, lastYearBos.size());
                } catch (Exception e) {
                    LOGGER.error("每天任务执行失败: {}", e.getMessage(), e);
                }
            }
        }catch (Exception e){
            LOGGER.error("汇总数据失败 [汇总表={}, 第几页={}]: {}",
                    hzb, PAGE_SIZE, e.getMessage(), e);
        }
    }

    /**
     * 跨库查询源表数据（增加每月最大日期查询功能）
     * @param ccId 跨库连接ID
     * @param tableName 源表名
     * @param timeField 源表时间字段名
     * @param startDated 开始时间
     * @param endDated 结束时间
     * @return 查询结果数据集
     * @throws RuntimeException 查询失败或参数无效时抛出
     */
    @Override
    public void querySourceDataYS(String ccId, String tableName, String timeField, Date startDated, Date endDated, String partitionField, List<BO> fieldMappings, String targetTable, String jezd) {
//        LOGGER.info("ccId:{},tableName:{},timeField:{},startDated:{},endDated:{},partitionField:{},fieldMappings:{},targetTable:{},jezd:{}",ccId,tableName,timeField,startDated,endDated,partitionField,Arrays.asList(fieldMappings.toArray()),targetTable,jezd);
        int totalRows = 0;      // 总查询行数
        int totalSuccess = 0;   // 总成功插入行数
        int pageNo = 1;
        boolean hasMore;
        RDSAPI rdsapi = null;
        DBUtils.SUPPLY supply = null;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd");
        // 重试配置
        int maxRetries = 3;
        int retryDelayMs = 2000; // 重试延迟2秒
        try {
            // 带重试机制的连接获取
            for (int retry = 0; retry < maxRetries; retry++) {
                try {
                    rdsapi = SDK.getCCAPI().getRDSAPI(ccId);
                    supply = rdsapi.getSupply();
                    if (rdsapi != null && supply!=null) {
                        break; // 连接成功，跳出重试循环
                    }
                } catch (Exception e) {
                    if (retry == maxRetries - 1) {
                        String result = fieldMappings.stream()
                                .filter(bo -> "BKGS".equals(bo.getString("LDBZD")))   // 条件1: LDBZD 为 "BKGS"
                                .filter(bo -> bo.getString("LDZDMRZ") != null)        // 条件2: LDZDMRZ 不为空
                                .map(bo -> bo.getString("LDZDMRZ"))                          // 提取 LDZDMRZ 的值
                                .collect(Collectors.joining());                // 收集到 List
                        // 最后一次重试仍然失败
                        String errorMsg = String.format("获取数据库连接失败(CC_ID: %s)，板块为：%s，已重试%d次，同步时间为：%s\n错误信息为：%s",
                                ccId, result, maxRetries, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), e.getMessage());
                        LOGGER.error(errorMsg, e);
                        // 发送邮件通知
//                        boolean eMailSync = SDK.getNotificationAPI().sendEMailSync(SEND_EMAIL_ACCOUNT_NUMBER, RECEIVE_EMAIL_ACCOUNT, null, "数据贯通数据库连接通知", errorMsg);
//                        if (eMailSync){
//                            LOGGER.info("短信发送成功");
//                        }else {
//                            LOGGER.info("短信发送失败");
//                        }
                        throw new RuntimeException(errorMsg, e);
                    }
                    LOGGER.warn("获取数据库连接失败(CC_ID: {})，第{}次重试...", ccId, retry + 1);
                    Thread.sleep(retryDelayMs);
                }
            }
            String DBname = supply.getName();
            LOGGER.info("数据库为：{}", DBname);

            String maxPartition = "";
            if (partitionField != null && !partitionField.isEmpty()) {
                // 查询最大分区值
                String maxPartitionSql = "SELECT MAX(" + partitionField + ") AS max_partition FROM " + tableName;
                List<RowMap> maxPartitionResult = rdsapi.getMaps(maxPartitionSql);

                if (maxPartitionResult.isEmpty() || maxPartitionResult.get(0).get("max_partition") == null) {
                    LOGGER.warn("表[{}]没有找到分区字段[{}]的数据", tableName, partitionField);
                    return;
                }

                maxPartition = maxPartitionResult.get(0).getString("max_partition");
                LOGGER.info("表[{}]的最大分区为: {}", tableName, maxPartition);
            }

            // 构建基础查询条件
            StringBuilder conditionBuilder = new StringBuilder();
            StringBuilder orderByBuilder = new StringBuilder();
            List<Object> params = new ArrayList<>();

            // 分区字段条件
            if (partitionField != null && !partitionField.isEmpty()) {
                conditionBuilder.append(partitionField)
                        .append(" = '")
                        .append(maxPartition)
                        .append("'");
            }else {
                conditionBuilder.append("1=1");
            }

            // 分页查询数据
            do {
                String startDate = simpleDateFormat2.format(startDated);
                String endDate = simpleDateFormat2.format(endDated);
                // 修改后的querySourceDataYS方法中的SQL查询部分
                String querySql;
                if ("ORACLE".equalsIgnoreCase(DBname)) {
                    querySql = "SELECT t1.* FROM " + tableName + " t1 " +
                            " JOIN (SELECT TRUNC(TO_DATE(" + timeField + ", 'YYYY-MM-DD'), 'MONTH') AS month_start, " +
                            " MAX(TO_DATE(" + timeField + ", 'YYYY-MM-DD')) AS max_date " +
                            " FROM " + tableName + " WHERE " + conditionBuilder.toString() +
                            " AND TO_DATE(" + timeField + ", 'YYYY-MM-DD') BETWEEN TO_DATE('" + startDate + "', 'YYYY-MM-DD') AND TO_DATE('" + endDate + "', 'YYYY-MM-DD') " +
                            " GROUP BY TRUNC(TO_DATE(" + timeField + ", 'YYYY-MM-DD'), 'MONTH')) t2 " +
                            " ON TO_DATE(t1." + timeField + ", 'YYYY-MM-DD') = t2.max_date " +
                            " WHERE " + conditionBuilder.toString() +
                            " AND TO_DATE(t1." + timeField + ", 'YYYY-MM-DD') BETWEEN TO_DATE('" + startDate + "', 'YYYY-MM-DD') AND TO_DATE('" + endDate + "', 'YYYY-MM-DD')";
                } else if ("POSTGRESQL".equalsIgnoreCase(DBname) || "GAUSS".equalsIgnoreCase(DBname)) {
                    querySql = "SELECT t1.* FROM " + tableName + " t1 " +
                            " JOIN (SELECT DATE_TRUNC('MONTH', " + timeField + "::DATE) AS month_start, " +
                            " MAX(" + timeField + "::DATE) AS max_date " +
                            " FROM " + tableName + " WHERE "+conditionBuilder.toString()+" AND "+timeField+"::DATE BETWEEN '"+startDate+"' AND '"+endDate+"' " +
                            " GROUP BY DATE_TRUNC('MONTH', " + timeField + "::DATE)) t2 " +
                            " ON t1." + timeField + "::DATE = t2.max_date " +
                            " WHERE " +conditionBuilder.toString()+
                            "    AND t1."+timeField+"::DATE BETWEEN '"+startDate+"' AND '"+endDate+"'";
                } else if ("SQLSERVER".equalsIgnoreCase(DBname)) {
                    querySql = "SELECT t1.* FROM " + tableName + " t1 " +
                            " JOIN (SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, CONVERT(DATE, " + timeField + ")), 0) AS month_start, " +
                            " MAX(CONVERT(DATE, " + timeField + ")) AS max_date " +
                            " FROM " + tableName + " WHERE " + conditionBuilder.toString() +
                            " AND CONVERT(DATE, " + timeField + ") BETWEEN CONVERT(DATE, '" + startDate + "') AND CONVERT(DATE, '" + endDate + "') " +
                            " GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, CONVERT(DATE, " + timeField + ")), 0)) t2 " +
                            " ON CONVERT(DATE, t1." + timeField + ") = t2.max_date " +
                            " WHERE " + conditionBuilder.toString() +
                            " AND CONVERT(DATE, t1." + timeField + ") BETWEEN CONVERT(DATE, '" + startDate + "') AND CONVERT(DATE, '" + endDate + "')";
                } else {
                    // 默认处理（如MySQL）
                    querySql = "SELECT t1.* FROM " + tableName + " t1 " +
                            " JOIN (SELECT DATE_FORMAT(STR_TO_DATE(" + timeField + ", '%Y-%m-%d'), '%Y-%m-01') AS month_start, " +
                            " MAX(STR_TO_DATE(" + timeField + ", '%Y-%m-%d')) AS max_date " +
                            " FROM " + tableName + " WHERE " + conditionBuilder.toString() +
                            " AND STR_TO_DATE(" + timeField + ", '%Y-%m-%d') BETWEEN STR_TO_DATE('" + startDate + "', '%Y-%m-%d') AND STR_TO_DATE('" + endDate + "', '%Y-%m-%d') " +
                            " GROUP BY DATE_FORMAT(STR_TO_DATE(" + timeField + ", '%Y-%m-%d'), '%Y-%m-01')) t2 " +
                            " ON STR_TO_DATE(t1." + timeField + ", '%Y-%m-%d') = t2.max_date " +
                            " WHERE " + conditionBuilder.toString() +
                            " AND STR_TO_DATE(t1." + timeField + ", '%Y-%m-%d') BETWEEN STR_TO_DATE('" + startDate + "', '%Y-%m-%d') AND STR_TO_DATE('" + endDate + "', '%Y-%m-%d')";
                }
                LOGGER.info("执行查询: {}", querySql);

                List<RowMap> pageData;
                if (params.isEmpty()) {
                    pageData = rdsapi.getMaps(querySql);
                } else {
                    pageData = rdsapi.getMaps(querySql);
                }

                if (pageData != null && !pageData.isEmpty()) {
                    int successCount = this.processAndInsertData(pageData, fieldMappings, targetTable);
                    totalRows += pageData.size();
                    totalSuccess += successCount;
                    hasMore = pageData.size() == PAGE_SIZE;
                    pageNo++;
                } else {
                    hasMore = false;
                }
            } while (hasMore);

            // 每天执行的任务：删除去年同期上一天的数据，新增去年同期当天的数据
            try {
                LOGGER.info("开始执行每天的特殊数据处理任务");
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

                // 获取结束日期
                String currentDateStr = simpleDateFormat2.format(endDated);
                LocalDate currentDate = LocalDate.parse(currentDateStr, formatter);

                // 计算去年同期日期
                LocalDate lastYearSameDay = currentDate.minusYears(1);
                // 计算去年同期前一天
                LocalDate lastYearPreviousDay = lastYearSameDay.minusDays(1);

                String lastYearSameDayStr = lastYearSameDay.format(formatter);
                String lastYearPreviousDayStr = lastYearPreviousDay.format(formatter);

                // 1. 删除去年同期前一天的数据
                String deleteLastYearPreviousDaySql = "DELETE FROM " + targetTable +
                        " WHERE RQ = '" + lastYearPreviousDayStr + "'";
                int deletedCount = DBSql.update(deleteLastYearPreviousDaySql);
                LOGGER.info("已删除去年同期前一天({})的数据，删除了{}条数据", lastYearPreviousDayStr, deletedCount);

                // 2. 新增去年同期当天数据
                // 构建查询去年同期当天数据的SQL
                String lastYearQuerySql;
                List<Object> lastYearParams = new ArrayList<>();

                // 根据不同的数据库类型构建查询语句
                if ("ORACLE".equalsIgnoreCase(DBname)) {
                    lastYearQuerySql = "SELECT * FROM " + tableName +
                            " WHERE TO_DATE(" + timeField + ", 'YYYY-MM-DD') = TO_DATE(?, 'YYYY-MM-DD')";
                    lastYearParams.add(lastYearSameDayStr);
                } else if ("POSTGRESQL".equalsIgnoreCase(DBname) || "GAUSS".equalsIgnoreCase(DBname)) {
                    lastYearQuerySql = "SELECT * FROM " + tableName +
                            " WHERE " + timeField + "::DATE = ?::DATE";
                    lastYearParams.add(lastYearSameDayStr);
                } else if ("SQLSERVER".equalsIgnoreCase(DBname)) {
                    lastYearQuerySql = "SELECT * FROM " + tableName +
                            " WHERE CONVERT(DATE, " + timeField + ") = CONVERT(DATE, ?)";
                    lastYearParams.add(lastYearSameDayStr);
                } else {
                    // 默认处理（如MySQL）
                    lastYearQuerySql = "SELECT * FROM " + tableName +
                            " WHERE STR_TO_DATE(" + timeField + ", '%Y-%m-%d') = STR_TO_DATE(?, '%Y-%m-%d')";
                    lastYearParams.add(lastYearSameDayStr);
                }

                // 添加分区条件（如果存在）
                if (partitionField != null && !partitionField.isEmpty() && !maxPartition.isEmpty()) {
                    if ("ORACLE".equalsIgnoreCase(DBname)) {
                        lastYearQuerySql += " AND " + partitionField + " = '" + maxPartition + "'";
                    } else {
                        lastYearQuerySql += " AND " + partitionField + " = '" + maxPartition + "'";
                    }
                }

                LOGGER.info("查询去年同期当天数据SQL: {}", lastYearQuerySql);
                LOGGER.info("查询参数: 日期={}", lastYearSameDayStr);

                // 执行查询去年同期当天数据
                List<RowMap> lastYearData;
                if (lastYearParams.isEmpty()) {
                    lastYearData = rdsapi.getMaps(lastYearQuerySql);
                } else {
                    // 这里需要根据您的rdsapi接口调整参数传递方式
                    lastYearData = rdsapi.getMaps(lastYearQuerySql, lastYearSameDayStr);
                }

                if (lastYearData != null && !lastYearData.isEmpty()) {
                    // 批量插入去年同期当天数据
                    int lastYearSuccessCount = this.processAndInsertData(lastYearData, fieldMappings, targetTable);
                    LOGGER.info("成功插入去年同期当天数据{}条，日期: {}",
                            lastYearSuccessCount, lastYearSameDayStr);
                } else {
                    LOGGER.warn("未查询到去年同期当天数据，日期: {}", lastYearSameDayStr);
                }

            } catch (Exception e) {
                LOGGER.error("每天任务执行失败: {}", e.getMessage(), e);
            }

            LOGGER.info("从表[{}]共查询到{}条数据，成功同步{}条数据",
                    tableName, totalRows, totalSuccess);
        } catch (Exception e) {
            throw new RuntimeException("查询源表[" + tableName + "]数据失败: " + e.getMessage(), e);
        }
    }
}