package org.example.Impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.spire.xls.collections.IList;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.ArrayUtils;
import org.bytedeco.opencv.presets.opencv_core;
import org.example.Api.LoggerHandler;
import org.example.Auth.utils.SafetyUtil;
import org.example.Entity.BaseResponse.BaseResponse;
import org.example.Entity.Control.Base.Component;
import org.example.Entity.Control.Com.Button;
import org.example.Entity.Control.Com.SysPoPupMenuBtn;
import org.example.Entity.Control.Container.ChartContainer;
import org.example.Entity.Control.Container.MContainer;
import org.example.Entity.Control.Data.DataStore;
import org.example.Entity.Control.Fields.*;
import org.example.Entity.Control.Container.RowColumn;
import org.example.Entity.Control.Panel.GridPanel;
import org.example.Entity.Control.Panel.Panel;
import org.example.Entity.Control.Panel.TreePanel;
import org.example.Entity.CusException.CusException;
import org.example.Entity.System.Audit.AuditStep;
import org.example.Entity.System.BaseModule;
import org.example.Entity.System.BillModule;
import org.example.Entity.System.ModuleEntity;
import org.example.Entity.System.*;
import org.example.Enums.*;
import org.example.Impl.Sql.factory.AllInOneSqlFactory;
import org.example.Impl.Sql.provider.AllInOneSqlProvider;
import org.example.ModuleApi.ModuleAjaxApi.dto.module.ModuleIdFieldDTO;
import org.example.ModuleApi.ModuleAjaxApi.mapper.CRMapper;
import org.example.Service.ModuleImplService;
import org.example.Utils.*;
import org.example.Utils.DataTableUtil;
import org.example.Utils.Ref;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.*;
import org.springframework.stereotype.Service;
import org.example.Entity.Control.Container.Column;
import org.example.Entity.Control.Panel.Chart;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.example.Utils.PrintHelper;

import static org.apache.commons.lang3.StringUtils.join;
import static org.apache.commons.lang3.StringUtils.stripEnd;
import static org.apache.commons.lang3.math.NumberUtils.toInt;
import static org.example.Utils.DataTableUtil.get;
import static org.example.Utils.DataTableUtil.toHashTable;
import static org.example.Utils.NativeExtensionUtils.isNullOrEmpty;
import static org.example.Utils.NativeExtensionUtils.*;

@SuppressWarnings("LanguageDetectionInspection")
@Service
public class ModuleImpl extends OptBaseImpl implements ModuleImplService {
    // 定义统一入口，接收所有请求
    @Autowired
    private CRMapper crmapper;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private DataImpl DataImpl;
    @Autowired
    private IPublicUtil createControl;
    @Autowired
    private DbOperator dbOperator;

    @Value("${custom.database.type}")
    private String databaseType;

    //    时间定义声明
    SimpleDateFormat timestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    @Autowired
    AllInOneSqlFactory allInOneSqlFactory; // 注入Spring管理的实例

    public AllInOneSqlProvider getDetailJDBC() {
        return allInOneSqlFactory.createProvider(databaseType);
    }

    private static ModuleEventImpl getEventHandler() {

        return ModuleEventImpl.getInstance();

    }

    private int[] tot = new int[1];

    @Autowired
    // 构造方法，在依赖注入完成后初始化DbOperator
    public ModuleImpl(JdbcTemplate jdbcTemplate) {
        this.createControl = new IPublicUtil(jdbcTemplate);// 此时jdbcTemplate已注入
    }

    private PushImpl _pushImpl;

    private PushImpl getpushImpl() {
        if (_pushImpl == null) {
            return _pushImpl = new PushImpl();
        }
        return _pushImpl;
    }

    /// <summary>
    /// 【=1=】显示，会读取两个表的 inner join，获得用户 operatorId的 显示配置
    /// </summary>
    /// <param name="theUser"></param>
    /// <returns></returns
    public BaseResponse GetBSDesktopData() {
        BaseResponse response = new BaseResponse();
        //【=0=】检查是否有关于默认项的完整 个人配置，无的话就加入和生产 完整个人配置。
        DataImpl.CheckAndFillBSDesktop(getUser().UserId);
        //【=1=】自身OperateId的 item cfg，应该是唯一，不用考虑复杂了
        List<Map<String, Object>> itemsDT = DataImpl.GetBSDesktopItemList(getUser().UserId, getUser().UserName);
        //【=2=】item的 data 数据解析
        List<Map<String, Object>> result = AnalyseBSDesktopData(itemsDT);
        response.setSuccess(true);
        response.setData(result);

        return response;
    }

    List<Map<String, Object>> AnalyseBSDesktopData(List<Map<String, Object>> itemsDT) {
        StringBuilder tempStr = new StringBuilder();
        return AnalyseBSDesktopData(itemsDT, tempStr, "");
    }

    /**
     * 解析桌面数据并返回处理后的结果列表
     *
     * @param itemsList 元素数据列表
     * @param queryVal  查询值
     * @return 包含处理后数据的哈希表列表
     */
    public List<Map<String, Object>> AnalyseBSDesktopData(
            List<Map<String, Object>> itemsList,
            StringBuilder msg,
            String queryVal) {

        if (msg == null) {
            msg = new StringBuilder();
        } else {
            msg.setLength(0);
        }

        List<Map<String, Object>> result = new ArrayList<>();

        for (Map<String, Object> oneItem : itemsList) {
            Map<String, Object> item = toHashTable(oneItem, false, true, false);
            Map<String, Object> oneItemHS = new HashMap<>(item);

            String itemCode = Objects.toString(oneItem.get("itemCode"), "");
            String sourceString = Objects.toString(oneItem.get("itemDataSource"), "").trim();
            int ifEnable = convertToInt(oneItem.get("enableflag"), 0);
            int ifLinked = convertToInt(oneItem.get("itemLinked"), 0);
            String itemTypeCode = Objects.toString(oneItem.get("itemTypeFull"), "");
            int cardId = convertToInt(oneItem.get("id"), 0);

            if (ifEnable != 1) {
                continue;
            }
            // 处理特定类型的数据
            if ("0103".equals(itemTypeCode)) {
                oneItemHS.put("sourceData", GetDeskTopCommonUse(cardId));
            }
            // 处理链接数据
            else if (ifLinked == 1 && !itemCode.isEmpty() && !sourceString.isEmpty()) {
                String dataStr = PublicUtil.ReqSqlPmsByRow(oneItem, null, sourceString);
                try {
                    // 处理SQL语句
                    if (sourceString.startsWith("@")) {
                        String sql = dataStr.substring(1);
                        List<Map<String, Object>> data = jdbcTemplate.queryForList(sql);
                        oneItemHS.put("sourceData", ConversionUtils.toLowerColumnName(data));
                    }
                    // 处理存储过程
                    else if (sourceString.startsWith("!")) {
                        String procCall = "exec " + dataStr.replace("!", "").replace("(", " ").replace(")", "");
                        List<Map<String, Object>> data = jdbcTemplate.queryForList(procCall);
                        oneItemHS.put("sourceData", data);
                    }
                    // 处理模块编号
                    else if (sourceString.length() < 20) {
                        BaseResponse moduleResponse = GetModuleData(sourceString, null, null, null, null, false, false, 0, 0);
                        if (moduleResponse.isSuccess()) {
                            oneItemHS.put("sourceData", moduleResponse.getData());
                        }
                    }
                    // 直接使用源字符串
                    else {
                        oneItemHS.put("sourceData", sourceString);
                    }
                } catch (Exception ex) {
                    oneItemHS.put("sourceData", "数据执行过程失败：" + ex.getMessage());
                }

                // 处理查询过滤
                if (queryVal != null && !queryVal.isEmpty()) {
                    Object sourceData = oneItemHS.get("sourceData");


                    if (sourceData instanceof List) {
                        @SuppressWarnings("unchecked")
                        List<Map<String, Object>> theDT = (List<Map<String, Object>>) sourceData;

                        if (!theDT.isEmpty()) {
                            String cond = Objects.toString(oneItem.get("condition"), "").trim().toLowerCase();
                            String queryField = Objects.toString(oneItem.get("queryField"), "").trim();

                            // 优先处理条件过滤
                            if (!cond.isEmpty()) {
                                // 处理WHERE/AND/OR前缀
                                if (cond.startsWith("where")) {
                                    cond = cond.substring(5).trim();
                                } else if (cond.startsWith("and")) {
                                    cond = cond.substring(3).trim();
                                } else if (cond.startsWith("or")) {
                                    cond = cond.substring(2).trim();
                                }

                                // 替换参数
                                Hashtable<String, Object> params = new Hashtable<>();
                                params.put("value", queryVal);
                                cond = PublicUtil.ReqSqlPms(params, null, cond, SystemTypeEnums.PmType.sql, getUser());

                                try {
                                    // 执行过滤
                                    List<Map<String, Object>> queryResult = DataFilterUtil.select(theDT, cond);
                                    oneItemHS.put("sourceData", queryResult);
                                } catch (Exception er) {
                                    msg.append("; ").append(er.getMessage());
                                }
                            }
                            // 处理字段查询
                            else if (!queryField.isEmpty()) {
                                if (hasColumn(theDT, queryField)) {
                                    List<Map<String, Object>> queryResult = theDT.stream()
                                            .filter(row -> {
                                                String value = Objects.toString(row.get(queryField), "");
                                                return value.contains(queryVal);
                                            })
                                            .collect(Collectors.toList());

                                    oneItemHS.put("sourceData", queryResult);
                                }
                            }
                        }
                    }
                }
            }

            // 移除不需要的字段
            oneItemHS.remove("itemdatasource");
            result.add(oneItemHS);
        }
        // 按itemOrder排序
        result.sort((item1, item2) -> {
            int order1 = convertToInt(item1.get("itemorder"), 0);
            int order2 = convertToInt(item2.get("itemorder"), 0);
            return Integer.compare(order1, order2);
        });

        return result;
    }

    // 辅助方法：将值转换为整数
    private int convertToInt(Object value, int defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Number) {
            return ((Number) value).intValue();
        }
        try {
            return Integer.parseInt(value.toString().trim());
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }

    // 辅助方法：检查列表中是否包含指定列
    private boolean hasColumn(List<Map<String, Object>> dataList, String column) {
        if (dataList.isEmpty()) {
            return false;
        }
        return dataList.get(0).containsKey(column);
    }

    /**
     * 获取桌面快捷模块个人快捷功能
     *
     * @param cardId 卡片ID
     * @return 快捷功能列表
     */
    public List<?> GetDeskTopCommonUse(int cardId) {
        // 获取数据列表，对应C#的DataTable
//        List<Map<String, Object>> dtVal = DataImpl.GetDeskTopCommonUse(cardId, getUser().UserId);
        List<Map<String, Object>> dtVal = DataImpl.GetDeskTopCommonUse(cardId, getUser().UserId);
        // 处理并转换数据
        return dtVal.stream()
                // 过滤条件：cardId=-99 或 权限校验通过（复刻原where逻辑）
                .filter(row -> {
                    // 处理menuid：转为字符串，避免null（对应C# row["menuid"] + ""）
                    String menuId = toStringSafe(row.get("menuid"));
                    // 权限校验：PublicUtil.CheckPurview（参数需与C#一致）
                    String purviewResult = PublicUtil.CheckPurview(getUser(), getUser().PurviewStr, menuId);
                    // 逻辑：cardId=-99 跳过权限校验；否则需权限结果非空
                    return cardId == -99 || !isNullOrEmpty(purviewResult);
                })
                // 转换：用Map.of()封装字段（对应C# select new 匿名对象）
                .map(row -> {
                    // ------------------------------
                    // 1. 处理所有字段值（先解决null问题，避免Map.of()抛空指针）
                    // ------------------------------
                    // dllfilename：对应C# row["dllfilename"]（转为字符串，null→空串）
                    String dllfilename = toStringSafe(row.get("dllfilename"));
                    // href：复刻原逻辑（#→#，含http→原href，否则加http://）
                    String hrefRaw = toStringSafe(row.get("href"));
                    String href = "#".equals(hrefRaw)
                            ? "#"
                            : hrefRaw.contains("http")
                              ? hrefRaw
                              : "http://" + hrefRaw;
                    // isa：对应C# (row["href"] + "") != "#"
                    boolean isa = !"#".equals(hrefRaw);
                    // text：对应C# row["text"]（null→空串）
                    String text = toStringSafe(row.get("text"));
                    // UrlParams：对应C# row["dllcoid"]（null→空串）
                    String urlParams = toStringSafe(row.get("dllcoid"));
                    // dllType：对应C# row["dlltype"]（null→空串，后续转int用）
                    String dllTypeStr = toStringSafe(row.get("dlltype"));
                    // MenuId：对应C# row["menuid"]（null→空串）
                    String menuId = toStringSafe(row.get("menuid"));
                    // serverId：对应C# row.Get("serverId")（null→空串，避免Map.of()报错）
                    String serverId = toStringSafe(row.get("serverId"));
                    // ShowCount：对应C# row["needcount"].ToBoolean()（0→false，1→true，null→false）
                    boolean showCount = toBoolean(row.get("needcount"));

                    // ------------------------------
                    // 2. 处理out参数isUrl（Java无out，用数组模拟）
                    // ------------------------------
                    boolean[] isUrlHolder = new boolean[1]; // 数组长度1，存储isUrl结果
                    // 调用SystemMenu.ConvertToModuleName（复刻原逻辑，dllType转int）
                    String pluginName = SystemMenu.convertToModuleName(
                            dllfilename,          // 对应C# row["dllfilename"] + ""
                            ToInt32(dllTypeStr), // 对应C# row["dlltype"].ToInt32()
                            isUrlHolder           // 模拟out参数，结果存在isUrlHolder[0]
                    );

                    // ------------------------------
                    // 3. 用Map.of()封装键值对（注意：键/值均不能为null）
                    // ------------------------------
                    return Map.of(
                            "dllfilename", dllfilename,
                            "isa", isa,
                            "href", href,
                            "text", text,
                            "UrlParams", urlParams,
                            "dllType", dllTypeStr, // 若需int类型，可改为toInt32Safe(dllTypeStr)
                            "MenuId", menuId,
                            "serverId", serverId,
                            "ShowCount", showCount,
                            "pluginName", pluginName
                    );
                })
                // 收集为List<Map>（对应C# .ToList()）
                .collect(Collectors.toList());
    }

    /**
     * 安全转为字符串：null→空串，非null→调用toString()
     * 对应C#的 row["xxx"] + ""
     **/
    private String toStringSafe(Object value) {
        return value == null ? "" : value.toString();
    }

    /**
     * 计算href属性值
     */
    private String calculateHref(Map<String, Object> row) {
        String href = String.valueOf(row.get("href"));
        if ("#".equals(href)) {
            return "#";
        }
        if (href.contains("http")) {
            return href;
        }
        return "http://" + href;
    }

    /**
     * 计算插件名称
     */
    private String calculatePluginName(Map<String, Object> row) {
        String dllfilename = String.valueOf(row.get("dllfilename"));
        int dlltype = Integer.parseInt(String.valueOf(row.get("dlltype")));
        boolean[] isUrl = new boolean[1]; // 使用数组存储引用类型的结果
        return SystemMenu.convertToModuleName(dllfilename, dlltype, isUrl);
    }

    /**
     * 获取添加或更新字段
     *
     * @param moduleId   模块标识符
     * @param idValue    id值
     * @param leftRecord 左侧记录（JSON字符串，默认空字符串）
     * @return BaseResponse
     */
    public BaseResponse GetBillMasterFields(String moduleId, String idValue, String leftRecord) {
        // 处理默认参数（leftRecord默认空字符串）
        if (leftRecord == null) {
            leftRecord = "";
        }

        BaseResponse response = new BaseResponse();

        // 获取账单模块信息（假设BillModule对应实体类，getBillModule方法已实现）
        BillModule module = GetBillModule(moduleId, getMenuId());
        if (module == null) {
            return response;
        }

        // 设置模块属性
        module.IdValue = (idValue);
        module.SLeftRecord = (leftRecord);  // 假设实体类中有setSLeftRecord方法

        // 获取主表字段数据并设置响应
        response.setData(GetBillMasterFields(module, ""));  // 假设存在重载方法处理BillModuleEntity参数
        response.setOther(module);  // 存储模块信息到响应的other字段
        response.setSuccess(true);

        return response;
    }

    /**
     * 获取单据主表字段
     *
     * @param module 单据模块实体
     * @param rowSql 行数据SQL（可选，默认空字符串）
     * @return 字段列表
     */
    public List<Field> GetBillMasterFields(BillModule module, String rowSql) {
        // 处理默认参数
        if (rowSql == null) {
            rowSql = "";
        }

        if (module == null) {
            return null;
        }

        // 设置更新行数据
        SetUpdateRow(module, rowSql, module.IdValue);
        // 获取主表列配置数据（从DataImpl中获取）
        List<Map<String, Object>> dtVal = DataImpl.GetBillMasterRows(
                module.getModuleId(),
                getUser().UserName,
                0
        );

        // 创建控件字段列表
        List<Field> cols = createControl.createControl(dtVal, module);
        // 处理字段属性（只读状态、默认值等）
        return cols.stream().map(field -> {
            // 处理主键字段的只读属性和默认值
            if (!field.getName().equalsIgnoreCase(module.getIdField())) return field;
            field.setReadOnly(true);
            // 如果没有ID值，生成新单据编号
            if (!isNullOrEmpty(module.IdValue)) return field;
            String newBillNo = GetNewBillNo(module.getBillSeq());
            module.IdValue = (newBillNo);
            field.setDefaultval(newBillNo);
            return field;
        }).collect(Collectors.toList());
    }

    /**
     * 获取新单据编号
     *
     * @param moduleId 模块ID
     * @return 新单据编号
     */
    public String GetNewBillNo(String moduleId) {
        String billCode = "";
        String sql = "{call P_create_billdocumentPr(?, ?)}"; // 两个参数：moduleId、userId（输入+输出）

        try {
            // 获取当前用户ID（从用户上下文获取，需确保UserContext正确实现）
            String userId = getUser().UserId;
            // 使用JdbcTemplate的execute方法执行存储过程，通过回调获取输出参数
            jdbcTemplate.execute(sql, (CallableStatementCallback<String>) callableStatement -> {
                // 设置输入参数
                callableStatement.setString(1, moduleId); // 第一个参数：moduleId
                callableStatement.setString(2, userId);   // 第二个参数：userId（输入值）
                // 注册输出参数（第二个参数同时作为输出）
                callableStatement.registerOutParameter(2, Types.VARCHAR);
                // 执行存储过程
                callableStatement.execute();
                // 获取输出参数值（第二个参数）
                return callableStatement.getString(2);
            });
        } catch (Exception e) {
            log.error("Exception caught", e);
        }
        return billCode;
    }

    /**
     * 检查单据编号是否已存在
     *
     * @param module 单据模块实体
     * @param billno 单据编号
     * @return 存在返回true，否则返回false
     */
    public boolean ExistBillNo(BillModule module, String billno) {
        try {
            String sql = String.format(
                    "select count(1) from %s where %s = %s",
                    module.getMasterTable(),
                    module.getIdField(),
                    billno
            );

            // 执行查询并获取结果
            Object result = jdbcTemplate.queryForObject(sql, Object.class);

            // 转换结果为整数并判断是否大于0  // 构建查询SQL，使用参数占位符
            return ToInt32(result) > 0;
        } catch (Exception e) {
            sysLog("单据编号为空，直接创建新单据编号", "ExistBillNo");
            return false;
        }
    }

    /**
     * 获取单据新增或更新信息
     *
     * @param idVal         主键值
     * @param moduleId      模块ID
     * @param menuid        菜单ID
     * @param contextMenuId 上下文菜单ID（可选，默认空字符串）
     * @param leftRecord    左侧记录数据（可选，默认空字符串）
     * @return 单据模块信息对象
     */
    public Object GetBillAddOrUpdInfo(String idVal, String moduleId, String menuid, String contextMenuId, String leftRecord) {
        // 处理默认参数
        if (contextMenuId == null) {
            contextMenuId = "";
        }
        if (leftRecord == null) {
            leftRecord = "";
        }

        // 获取单据模块信息
        BillModule module = GetBillModule(moduleId, menuid);
        if (module == null) {
            return null;
        }

        // 设置上下文菜单ID
        module.ContextMenuId = (contextMenuId);

        // 解析左侧记录数据（JSON格式转换为Hashtable）
        if (!isNullOrEmpty(leftRecord) && leftRecord.startsWith("{")) {
            try {
                Map<String, Object> leftRecordMap = (Map<String, Object>) JSON.Decode(leftRecord);
                leftRecordMap = toLowerColumnName(leftRecordMap);
                module.setLeftRecord(leftRecordMap);
            } catch (Exception e) {
                // 解析失败时可记录日志或做其他处理
                log.error("解析leftRecord失败: {}", leftRecord, e);
            }
        }

        // 处理上下文菜单按钮信息
        if (!isNullOrEmpty(module.ContextMenuId)) {
            int menuBtnId = NativeExtensionUtils.parseInt(module.ContextMenuId);
            SysPoPupMenuBtn popBtn = GetContextMenuBtn(menuBtnId, module.getLeftRecord());
            module.PopBtn = (popBtn);
            if (popBtn != null) {
                // 设置单据来源ID（对应原C#的Pm7属性）
                module.setBillSourceIds(popBtn.getPm7());
            }
        }
        module.SourceTab = DataImpl.GetBillSourceItem(moduleId, module.getBillSourceIds(), "0,1");//module.BillFlag == 2 ? "0,1" : module.BillFlag + ""
        // 获取单据来源项
//        String billFlagStr = (module.getBillFlag() == 0) ? "0,1" : String.valueOf(module.getBillFlag());
//        List<Map<String, Object>> sourceTab = DataImpl.GetBillSourceItem(moduleId, module.getBillSourceIds(), billFlagStr);
//        module.SourceTab = (sourceTab);
        // 判断是否有来源数据
        module.HasSource = (module.SourceTab != null && !module.SourceTab.isEmpty());

        // 设置是否可申请（根据审核步骤数量判断）
        module.setApplyAble(DataImpl.GetAuditStepCount(moduleId, false) > 0);
        // 设置主键值并获取主表字段
        module.IdValue = (idVal);
        module.Main = (GetBillMasterFields(module, ""));
        // 获取单据明细表格
        if (!isNullOrEmpty(module.getPopupUnionCode())) {
            module.PopupFields = DataImpl.GetBillPopUpFields(module.getPopupUnionCode());
        }
        GridPanel detail = GetBillDetail(module, idVal);
        module.Detail = (detail);

        // 处理明细表格的数据源参数（当存在主键值时）
        if (!isNullOrEmpty(idVal) && module.Detail != null) {
            DataStore store = detail.getStore();
            // 构建额外参数
            Map<String, Object> extraParams = new HashMap<>();
            extraParams.put("moduleId", moduleId);
            extraParams.put("idValue", idVal);

            if (store != null) {
                store.extraParams = (extraParams);
            } else {
                // 若数据源不存在则创建新的并设置参数
                DataStore newStore = new DataStore();
                newStore.extraParams = (extraParams);
                detail.setStore(newStore);
            }
        }

        return module;
    }

    /**
     * 获取单据明细表格
     *
     * @param module        单据模块实体
     * @param selfEdit      是否允许自行编辑（默认true）
     * @param editFields    可编辑字段（逗号分隔）
     * @param requireFields 必输字段（逗号分隔）
     * @return 明细表格组件
     */
    public GridPanel GetBillDetail(BillModule module, String idVal, boolean selfEdit, String editFields, String requireFields) {
        // 处理默认参数
        if (editFields == null) {
            editFields = "";
        }
        if (requireFields == null) {
            requireFields = "";
        }

        // 获取当前登录用户信息
        String userId = getUser().UserId;
        String userName = getUser().UserName;

        // 查询明细列配置数据
        List<Map<String, Object>> detailColsTab = DataImpl.GetBillDetailColumns(
                module.getModuleId(),
                userId,
                userName,
                0
        );
        // 处理可编辑字段和必输字段
        if (!isNullOrEmpty(editFields) && detailColsTab.stream().anyMatch(row -> row.containsKey("Edit") || row.containsKey("edit"))) {
            // 复制列配置（创建新列表避免修改原数据）
            List<Map<String, Object>> copiedCols = new ArrayList<>();
            for (Map<String, Object> row : detailColsTab) {
                copiedCols.add(new HashMap<>(row));
            }
            detailColsTab = copiedCols;

            // 统一转为小写便于匹配
            String lowerEditFields = "," + editFields.toLowerCase() + ",";
            String lowerRequireFields = "," + requireFields.toLowerCase() + ",";

            for (Map<String, Object> row : detailColsTab) {
                String fieldName = (String) row.getOrDefault("FieldName", "");
                String lowerFieldName = fieldName.toLowerCase();

                // 默认设置为禁编（1表示禁编）
                row.put("Edit", 1);
                // 如果字段在可编辑列表中，设置为可编辑（0表示可编辑）
                if (lowerEditFields.contains("," + lowerFieldName + ",")) {
                    row.put("Edit", 0);
                }

                // 设置必输字段（1表示不能为空）
                if (lowerRequireFields.contains("," + lowerFieldName + ",")) {
                    row.put("nullable", 1);
                }
            }
        }

        // 转换列配置为组件列表
        List<Component> columns = ToColumns(detailColsTab, selfEdit, module);

        // 获取右键菜单
        List<Object> bbItems = null;
        String menuKey = PublicUtil.GetBillConMenuKey(SystemEnums.BillMenuEnum.BillDetail) + module.getModuleId();
        Object rightMenu = GetRightMenu(menuKey, 0, new Ref(bbItems));

        // 创建表格面板
        GridPanel grid = new GridPanel();
        grid.setColumns(columns);
        grid.RightMenu = (rightMenu);

        // 处理明细SQL和数据
        String detailSql = "";
        if (!isNullOrEmpty(module.IdValue) && !isNullOrEmpty(idVal)) {
            // 处理带主键的查询SQL
            String paramJson = String.format("{\"%s\":\"%s\"}", module.getIdField(), module.IdValue);
            detailSql = dealQuerySql(
                    module.getDetailSql(),
                    paramJson,
                    null,
                    null,
                    "",
                    "", false, false, false
            );
        } else if (module.PopBtn != null) {
            // 处理弹出按钮关联的SQL
            detailSql = module.PopBtn.dllpar9;
            String newSql = detailSql;
            if (!isNullOrEmpty(detailSql)) {
                List<Map<String, Object>> data = jdbcTemplate.queryForList(newSql);
                grid.data = DataTableUtil.toLowerColumnName(data);
            }
        }
        return grid;
    }

    public GridPanel GetBillDetail(BillModule module, String idVal) {
        return GetBillDetail(module, idVal, true, "", "");
    }

    public BaseResponse GetBSDesktopModuleData(String dllcoid, String queryVal) {
        BaseResponse response = new BaseResponse();

        //【=0=】检查是否有关于默认项的完整 模板配置，无的话就加入和生产 完整模板配置。
        DataImpl.CheckAndFillDesktopModuleOper(dllcoid, getUser().UserId);

        //【=1=】自身dllcoid的 item cfg，应该是唯一，不用考虑复杂了
        List<Map<String, Object>> itemsDT = DataImpl.GetBSDesktopModuleItemList(dllcoid, getUser().UserId);

        //【=2=】item的 data 数据解析
        StringBuilder errMsg = new StringBuilder();
        List<Map<String, Object>> result = AnalyseBSDesktopData(itemsDT, errMsg, queryVal);

        response.setSuccess(true);
        response.setMsg(errMsg.toString());
        response.setData(result);

        return response;
    }

    public BaseResponse GetBSDesktopAdminBase() {
        BaseResponse response = new BaseResponse();
        List<Map<String, Object>> itemsDT = DataImpl.GetBSDesktopAdminBase();
        response.setData(DataTableUtil.toLowerColumnName(itemsDT));
        response.setTot(itemsDT.size());
        response.setSuccess(true);
        return response;
    }

    /**
     * 获取桌面扩展配置
     */
    public BaseResponse GetBSDesktopExtend() {
        BaseResponse response = new BaseResponse();
        // 调用DataImpl获取数据（假设返回List<Map>模拟DataTable）
        List<Map<String, Object>> items = DataImpl.GetBSDesktopExtend(
                getUser().UserId,
                getUser().UserName
        );

        List<Map<String, Object>> resultList = new ArrayList<>();
        for (Map<String, Object> item : items) {
            // 复制原数据并添加operatorid字段
            Map<String, Object> itemMap = new HashMap<>(item);
            itemMap.put("operatorid", getUser().UserId);
            resultList.add(itemMap);
        }

        response.setData(DataTableUtil.toLowerColumnName(resultList));
        response.setTot(resultList.size());
        response.setSuccess(true);
        return response;
    }

    /**
     * 获取桌面模块操作配置
     */
    public BaseResponse GetDesktopModuleOper(String dllcoid) {
        BaseResponse response = new BaseResponse();
        // 调用DataImpl获取数据（假设返回List<Map>模拟DataTable）
        List<Map<String, Object>> items = DataImpl.GetDesktopModuleOper(
                getUser().UserId,
                dllcoid
        );

        List<Map<String, Object>> resultList = new ArrayList<>();
        for (Map<String, Object> item : items) {
            // 复制原数据并添加operatorid字段
            Map<String, Object> itemMap = new HashMap<>(item);
            itemMap.put("operatorid", getUser().UserId);
            resultList.add(itemMap);
        }

        response.setData(resultList);
        response.setTot(resultList.size());
        response.setSuccess(true);
        return response;
    }

    public String GetPrimaryKeys(String tabname) {
        return DataImpl.GetPrimaryKeys(tabname);
    }

    public List<String> GetPrimaryKeysArray(String tabname) {
        return DataImpl.GetPrimaryKeysArray(tabname);
    }

    /**
     * 获得近似的结果，前50条即可
     *
     * @param queryText 查询文本
     * @return 基础响应对象（包含查询结果、成功状态和总数）
     */
    public BaseResponse GetDeskQueryResult(String queryText) {
        BaseResponse response = new BaseResponse();
        // 获取原始查询结果（使用List<Map>模拟DataTable）
        List<Map<String, Object>> rawResultList = DataImpl.GetDeskQueryResult(queryText);
        response.setSuccess(true);

        if (rawResultList != null && !rawResultList.isEmpty()) {
            // 过滤有权限的记录（对应C#的LINQ查询）
            List<Map<String, Object>> filteredList = new ArrayList<>();
            String purviewStr = getUser().PurviewStr;

            for (Map<String, Object> row : rawResultList) {
                // 检查权限，非空则保留当前行
                String id = row.get("id") != null ? row.get("id").toString() : "";
                if (createControl.CheckPurview(purviewStr, id) != null && !createControl.CheckPurview(purviewStr, id).isEmpty()) {
                    filteredList.add(row);
                }
            }

            // 设置响应数据和总数
            response.setData(filteredList.isEmpty() ? null : filteredList);
            response.setTot(filteredList.size());
        } else {
            response.setData(null);
            response.setTot(0);
        }

        return response;
    }

    public BaseResponse GetClientSaveConds(String moduleId) {
        BaseResponse response = new BaseResponse();
        response.setSuccess(true);
        if (isNullOrEmpty(moduleId)) {
            response.setData(new ArrayList());
            return response;
        }

        String tableExistsSql = """
                SELECT COUNT(1)
                FROM ALL_TABLES
                WHERE OWNER = 'LSERP_JTCS'
                  AND TABLE_NAME = 'P_SYSTEMCLIENTCONDTAB'
                """;

        Integer count = jdbcTemplate.queryForObject(tableExistsSql, Integer.class);

        if (count == null || count == 0) {
            String emptySql = """
                    SELECT
                        CAST(NULL AS INT) id,
                        CAST(NULL AS VARCHAR(50)) tab,
                        CAST(NULL AS VARCHAR(200)) "condition",
                        CAST(NULL AS VARCHAR(100)) hintmsg,
                        CAST(NULL AS INT) orderid
                    FROM dual
                    WHERE 1 = 0
                    """;
            response.setData(jdbcTemplate.queryForList(emptySql));
            return response;
        }

        String sql = """
                SELECT 
                    id,
                    tab,
                    "condition",
                    hintmsg,
                    orderid
                FROM P_SystemClientCondTab
                WHERE tab = ?
                  AND NVL(disableflag, 0) = 0
                ORDER BY NVL(orderid, 0), id
                """;

        response.setData(jdbcTemplate.queryForList(sql, moduleId));
        return response;
    }

    protected static class ColInfos {
        public String colname;
        public Map<String, Object> colrow;
        public Field field;
        public int length;
        public String colCnName;
        public Boolean cfgNullAble;
        public Boolean isnullable;
        public Type dataType;
        public int dbType;
    }


    @Override
    public BaseModule GetBaseModule(String moduleCode, String menuId) {
        BaseModule module = null;
        List<Map<String, Object>> dtvalue = DataImpl.GetBaseModule(moduleCode, menuId);

        if (dtvalue != null && !dtvalue.isEmpty()) {
            module = new BaseModule(dtvalue.get(0));

            String formkey = "";

            ModuleIdFieldDTO result = DataImpl.GetModuleIdField(formkey, module.getModuleId(), module.getMasterTable());
            module.setIdField(isNullOrEmpty(module.getIdField()) ? result.getParmaryKey() : module.getIdField());
            Boolean isSpec = result.getSpecModule();
            module.IsSpecModule = isSpec;
            module.LeftUnionField = result.getLeftUnionField();
            module.LeftUnionFieldSql = result.getSpecSql();
            module.setApplyAble(getDetailJDBC().GetAuditStepCount(moduleCode, true) > 0);

            if (!module.OperAble) {
                module.OperAble = Objects.equals(createControl.CheckPurview(getUser().PurviewStr, ToInt32(menuId) + ""), "AllPurview");
            }
        } else {
            log.debug(String.valueOf("无效参数,返回值为null"));
            return null;
        }
        return module;
    }

    //   2026.2.5  新增Mrp
    @Override
    public BaseModule getModuleIniParams(String moduleId, String menuId, String targetModuleId, String detailId, boolean isCard, boolean isChart, String mFields, boolean atts, Boolean loadDetail, Boolean loadLeft, boolean isAttc) {
        return getModuleIniParams(moduleId, menuId, targetModuleId, detailId, isCard, isChart, mFields, atts, loadDetail, loadLeft, isAttc, true);
    }

    //    重新拿到处理好的resultList
    @Override
    public BaseModule getModuleIniParams(String moduleId, String menuId, String targetModuleId, String detailId, boolean isCard, boolean isChart, String mFields, boolean atts, Boolean loadDetail, Boolean loadLeft, boolean isAttc, boolean isMrp) {

        BaseModule module = GetBaseModule(moduleId, menuId);
//        out.println("getModuleIniParams " + module);
        if (module != null) {
            module.IsCard = isCard;
            module.IsChart = isChart;
            module.MainModifyFields = mFields;
            module.detailId = detailId;

            if (!isNullOrEmpty(detailId)) {
                module.DetailModule = GetBaseDetailModuel(ToInt32(detailId), isAttc);
                if (module.DetailModule != null && isNullOrEmpty(module.getUnionParentField())) {
//                    this.info("存在明细Moduleid=" + moduleId);
                    module.ParentModule = GetBaseModule(module.DetailModule.getUnionKey(), menuId);
                    if (module.ParentModule != null) {
                        module.DetailModule.setUnionParentField(module.ParentModule.getIdField());
                    }
                }
                if (module.DetailModule != null) {
                    module.setNoGridLine(module.DetailModule.getNoGridLine());
                    module.setNoRownumber(module.DetailModule.getNoRownumber());
                    module.setHideColumnHeader(module.DetailModule.getHideColumnHeader());
                }
            }
            if (menuId == "2005_1") {
                ModuleBaseEntity targModule = GetBaseModule(targetModuleId, menuId) != null
                        ? GetBaseModule(targetModuleId, menuId)
                        : GetBillModule(targetModuleId, menuId);
                if (targModule != null) {
                    module.setFileSpeciesNo(targModule.getFileSpeciesNo());
                    module.setDirId(targModule.getDirId());
                    module.setAttatchModifyCond(targModule.getAttatchModifyCond());
                    module.setAttcLeafOnly(targModule.getAttcLeafOnly());
                    module.setAccInfow(targModule.getAccInfow());
                    module.setFType(targModule.getFType());
                    int viewType = DataImpl.GetAttcViewModule(targetModuleId, getUser().UserId);
                    if (viewType > 0) {
                        module.AttcViewType = viewType;
                    }
                }
            }
            if (atts) {
                module.AttCfgs = GetAttachModuleCfgs(moduleId);
            }
//            out.println("SetMain start");
            setMain(module);
//            out.println("SetMain end");
            if (loadLeft) {
                SetLeft(module);
//                out.println("SetLeft end");
            }
            if (loadDetail)//string.IsNullOrEmpty(detailId)||
            {
                module.Details = GetModelDetails(module);
//                out.println("GetModelDetails end");
            }
            if (moduleId != "2005_1") {
//                暂未补充,日志功能
                sysLog(String.format("%s进入%s模块", getUser().UserName, module.Title), "进入模块");
            }
        }
        log.debug(String.valueOf("mro : " + isMrp));
        if (isMrp) {
            module.mrpType = DataImpl.GetMrpType();
        }
        return module;
    }

    //    注：因为不能重名，所以将C#的CheckAttcAuthory -> CheckAttcAuthoryInt
    public int CheckAttcAuthory(String moduleId, String idValue) {
        ModuleBaseEntity module = GetModule(moduleId);
        return CheckAttcAuthoryInt(module, idValue);
    }

    public ModuleBaseEntity GetModule(String moduleCode) {
        ModuleBaseEntity module = null;
        if (DataImpl.IsBaseModule(moduleCode) && (module = GetBaseModule(moduleCode, getMenuId())) != null) {
            return module;
        } else {
            return GetBillModule(moduleCode, getMenuId());
        }
    }

    //    public boolean CheckAttcAuthory(ModuleBaseEntity module, String idValue) {
//        if (module == null || isNullOrEmpty(module.getAttatchModifyCond())) return true;
//        module.IdValue = idValue;
//        SetUpdateRow(module, "", module.IdValue);
//        if (isNullOrEmpty(module.getAttatchModifyCond())) return true;

    /// /        IPublicUtil util = new IPublicUtil();
//        return createControl.evalCond(module.getAttatchModifyCond(), module.Updrow, null);
//    }
    public int CheckAttcAuthoryInt(ModuleBaseEntity module, String idValue) {
        if (module == null || isNullOrEmpty(module.getAttatchModifyCond())) {
            return 1;
        }
        module.IdValue = idValue;
        SetUpdateRow(module, "", module.IdValue);
        if (isNullOrEmpty(module.getAttatchModifyCond())) {
            return 1;
        }
        Object ok = getUtil().evalCond(module.getAttatchModifyCond(), module.Updrow, null);
        return ToInt32(ok.toString()) == 2 ? 2 : toBoolean(ok.toString()) ? 1 : 0;
    }

    private Object GetAttachModuleCfgs(String moduleId) {
        return DataImpl.GetAttcModules(moduleId).stream()
                .map(row -> {
                    // 直接获取并转换字段值，使用三目运算符处理空值
                    String unionModule = row.getOrDefault("unionmodule", "").toString();
                    String title = row.getOrDefault("attachname", "").toString();
                    String id = row.getOrDefault("id", "").toString();
                    String lib = row.getOrDefault("library", "").toString();
                    String pms = row.getOrDefault("params", "").toString();

                    if (!unionModule.isEmpty()) {
                        Map<String, String> moduleMap = new HashMap<>();
                        moduleMap.put("moduleId", unionModule);
                        moduleMap.put("attcId", id);
                        moduleMap.put("title", title);
                        moduleMap.put("lib", lib);
                        moduleMap.put("Pms", pms);
                        return moduleMap;
                    }
                    return null;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    /**
     * 设置左侧面板（树或表格）
     *
     * @param processedresultnew 基础模块实体
     */
    private void SetLeft(BaseModule processedresultnew) {
        List<Map<String, Object>> dtval = DataImpl.GetBaesModuleLeft(processedresultnew.getModuleId());
        if (dtval != null && !dtval.isEmpty()) {
            Map<String, Object> firstRow = dtval.get(0); // 获取第一行数据
//            out.println("firstRow:" + firstRow);

            String fieldLabel = String.valueOf(firstRow.get("fieldcaption"));
            String fieldname = String.valueOf(firstRow.get("fieldname")).toLowerCase();
            String fromkey = String.valueOf(firstRow.getOrDefault("fromkey", ""));
            String displaymember = String.valueOf(firstRow.getOrDefault("displaymember", "")).toLowerCase();
            String valuemember = String.valueOf(firstRow.getOrDefault("valuemember", "")).toLowerCase();
            String detailid = String.valueOf(firstRow.getOrDefault("detailid", ""));

            processedresultnew.LeftUnionField = fieldname;
            //左边是颗树
            if (processedresultnew.getMenuType() == 1 || processedresultnew.getMenuType() == 3) {
                TreePanel treepanel = new TreePanel();
                treepanel.title = fieldLabel;
                treepanel.displayField = displaymember;
                treepanel.valueField = valuemember;
                treepanel.parentValueField = "pid";
                treepanel.width = processedresultnew.getLeftWidth() != null ? processedresultnew.getLeftWidth() : 200;

                DataStore store = new DataStore();
                Map<String, Object> extraParams = new HashMap<>();
                extraParams.put("id", detailid);
                store.extraParams = (extraParams);
                treepanel.store = store;
                processedresultnew.Left = treepanel;

            } else if (processedresultnew.getMenuType() == 2) {
                //左边是grid
                Ref bbItems = new Ref(null);
                GridPanel gridpanel = new GridPanel();
                gridpanel.title = fieldLabel;
                gridpanel.width = processedresultnew.getLeftWidth() != null ? processedresultnew.getLeftWidth() : 200;
//                gridpanel.IdField = displaymember ; // 这里应该是valuemember
                gridpanel.IdField = valuemember;
                gridpanel.displayField = displaymember;
//                gridpanel.PageAble = processedresultnew.getPageAble();
                gridpanel.TbarItems = GetCondition(fromkey + "_Cond");
                gridpanel.RightMenu = GetRightMenu(fromkey, 0, bbItems);
                gridpanel.setColumns(GetFieldColumns(processedresultnew.getModuleId(), detailid));

                DataStore store = new DataStore();
                Map<String, Object> extraParams = new HashMap<>();
                extraParams.put("id", detailid);
                store.extraParams = (extraParams);
                gridpanel.setStore(store);

                processedresultnew.Left = gridpanel;
            }
        }
    }

    public void setMain(BaseModule module) {

        Integer sheight = 0;
        Object MobileCards = null;
        String ModuleId = module.getModuleId();
        boolean SelfEdit = module.isSelfEdit();
        List<Component> Columns = GetColumns(ModuleId, SelfEdit);
//        out.println("Columns " + Columns);
        List<Component> CardColumns = null;
        boolean IsWindowsDirver = toBoolean(Request("windowsDirver", ""));
//        if (module.IsCard || (module.getCellTplflag() != null && module.getCellTplflag())) {
//        2026.2.5修改
        if (module.IsCard && !IsWindowsDirver && !module.getDisMobileTpl() || isWindowsDirver()) {
//2026.2.24 修改
//            if (!IsWindowsDirver && module.getDisMobileTpl() || (module.getCellTplflag() != null && module.getCellTplflag())) {
//                //            以下是手机端模块,2026.2.5
//                MobileCards = GetModuleCardGroup(module.getModuleId(), true, true, 0, (module.getCellTplflag() != null && module.getCellTplflag()));
//            } else {
//                List<Map<String, Object>> dtVal = GetCardColumnRows(module.getModuleId(), getUser().UserId, getUser().UserName);
////                IPublicUtil util = new IPublicUtil();
//                if (dtVal != null) {
//                    CardColumns = dtVal.stream()
//                            .map(rowMap -> new RowColumn(rowMap, false, createControl))
//                            .collect(Collectors.toList());
//                }
//            }
            MobileCards = GetModuleCardGroup(module.getModuleId(), true, true, 0, isWindowsDirver() && module.getWebCardTpl() != true);
            if (!isWindowsDirver() && (MobileCards == null || ((IList) MobileCards).size() == 0)) {
                MobileCards = ConvertOldMobileCard(DataImpl.getMobileColumns(module.getModuleId()));
            }
            List<Map<String, Object>> dtVal = DataImpl.GetCardColumnRows(module.getModuleId(), getUser().getUserId(), getUser().getUserName());
            CardColumns = dtVal.stream()
                    .map(rowMap -> new RowColumn(rowMap, false, getUtil()))
                    .collect(Collectors.toList());

        }
//        out.println("MainModifyFields+"+module.MainModifyFields + "DetailModule:"+module.DetailModule);
        if (module.MainModifyFields != null
                && !module.MainModifyFields.trim().isEmpty()
                && module.DetailModule != null) {
//            this.Info("存在明细Moduleid=" + module.ModuleId.ToString() + "deailid:" + module.detailId.ToString() + "modify:" + module.MainModifyFields.ToString());
            for (Component cmp : Columns) {
                // 判断组件是否为RowColumn类型
                if (cmp instanceof RowColumn) {
                    // 强制转换为RowColumn
                    RowColumn rowColumn = (RowColumn) cmp;
                    // 先设置editable为false
                    rowColumn.setEditable(false);

                    // 获取dataIndex字段值
                    String name = rowColumn.getDataIndex();

                    // 拼接字符串并判断是否包含指定字段（与C#逻辑一致）
                    String mainModifyFields = module.MainModifyFields != null ? module.MainModifyFields.toLowerCase() : "";
                    if ((",".concat(mainModifyFields).concat(",")).indexOf("," + name + ",") > -1) {
                        // 包含则设置editable为true
                        rowColumn.setEditable(true);
                    }
                }
            }
        }

        List<Object> bbItems = new ArrayList<>();
        Object rightm = GetRightMenu(module.getModuleId(), 0, new Ref(bbItems), "toolClick");
        List<Component> btns = GetMainGridBtns(module);
        List<Object> allbtns = new ArrayList<>();
        allbtns.addAll(btns);
        allbtns.addAll(bbItems);
//        out.println("allbtns " + allbtns);
        // 对应C#的GridPanel初始化
//        初始化赋值2026.2.24
        List<Component> finalCardColumns = CardColumns;
        Object finalMobileCards = MobileCards;

        GridPanel maingrid = new GridPanel() {{
            PageAble = module.getPageAble();
            TbarItems = GetCondition(module.getCondKey());
            SHeight = sheight;
            RightMenu = rightm;
            BbarItems = allbtns;
            CardColumns = finalCardColumns;
            MobileCards = finalMobileCards;
            NoGridLine = module.NoGridLine;
            setHideColumnHeader(module.HideColumnHeader);
            setRowNumberer(!(module.getNoRownumber() != null ? module.getNoRownumber() : false));
            setStore(new DataStore() {{
                extraParams = new HashMap<String, Object>() {{
                    put("moduleId", module.getModuleId());
                    put("detailId", module.detailId);
                }};

                ApiSuccVal = module.getApiSuccVal();
                ApiSuccNode = module.getApiSuccNode();
                ApiDataNode = module.getApiDataNode();
            }});
        }};

//        ObjectMapper mapper = new ObjectMapper();
//        try {
//            String json = mapper.writeValueAsString(Columns); // 手动序列化
//            out.println(json); // 若报错，堆栈会显示具体出错的字段
//        } catch (Exception e) {
//            e.printStackTrace(); // 查看异常堆栈，定位到具体类和字段
//        }

        maingrid.setColumns(Columns);
        if (module.getMasterSql() != null && module.getMasterSql().trim().startsWith("http")) {
            maingrid.getStore().url = module.getMasterSql();
        }
        if (module.IsChart || maingrid.getColumns() == null || maingrid.getColumns().isEmpty() || module.getMasterTable() == null || module.getMasterTable().isEmpty()) {
            List<Map<String, Object>> _chartrows = DataImpl.GetChartCfg(module.getFromkey());
            maingrid.ChartCfg = (_chartrows.stream().map(Chart::new).collect(Collectors.toList()));
            if (!_chartrows.isEmpty()) {
                module.IsChart = true;
            }
        }
        module.Main = maingrid;
    }

    private List<Component> GetMainGridBtns(BaseModule module) {
        List<Component> btnlist = new ArrayList<>();
        Boolean isWindowsDirver = toBoolean(Request("windowsDirver", ""));
        // 添加新增按钮
        if (module.isAddAble() && !module.isIsReport()) {
            Button addBtn = new Button();
            addBtn.setRecordCond(module.getAddCond());
            addBtn.setText(module.getAddText() != null ? module.getAddText() : "新增");
            addBtn.setHandler("Add");
            addBtn.setCls("toolbar-add");
            addBtn.setIconCls("icon-add");
            btnlist.add(addBtn);
        }
        // 添加修改按钮
        if (module.isUpdateAble() && !module.isIsReport()) {
            Button updateBtn = new Button();
            updateBtn.setRecordCond(module.getUpdateCond());
            updateBtn.setText(module.getModifyText() != null ? module.getModifyText() : "修改");
            updateBtn.setHandler("Update");
            updateBtn.setCls("toolbar-edit");
            updateBtn.setIconCls("icon-edit");
            btnlist.add(updateBtn);
        }

        // 添加删除按钮
        if (module.getDeleteAble() && !module.isIsReport()) {
            Button deleteBtn = new Button();
            deleteBtn.setRecordCond(module.getDeleteCond());
            deleteBtn.setText(module.getDelText() != null ? module.getDelText() : "删除");
            deleteBtn.setHandler("Delete");
            deleteBtn.setCls("toolbar-remove");
            deleteBtn.setIconCls("icon-remove");
            btnlist.add(deleteBtn);
        }

        // 添加保存按钮
        if ((module.isSelfEdit() || module.getImportAble()) && !module.isIsReport()) {
            Button saveBtn = new Button();
//            saveBtn.setText("保存");
            saveBtn.setText(module.getSaveText() != null ? module.getSaveText() : "保存");
            saveBtn.setHandler("Save");
            saveBtn.setCls("toolbar-save");
            saveBtn.setIconCls("icon-save");
            btnlist.add(saveBtn);
        }

        // 添加提交按钮
        if (module.getApplyAble() && !module.isIsReport()) {
            Button applyBtn = new Button();
            applyBtn.setText(module.getApplyText() != null ? module.getApplyText() : "提交");
            applyBtn.setRecordCond("{" + module.getMenuPrefix() + "affirmer}=='0'");
            applyBtn.setHandler("Apply");
            applyBtn.setCls("toolbar-save");
            applyBtn.setIconCls("icon-save");
            btnlist.add(applyBtn);
        }

        // 添加导入按钮
        if (module.getImportAble() && isWindowsDirver) {
            Button importBtn = new Button();
            importBtn.setText("导入");
            importBtn.setHandler("Import");
            importBtn.setCls("toolbar-undo");
            importBtn.setIconCls("icon-undo");
            btnlist.add(importBtn);
        }

        // 添加导出按钮
        if (module.getExportAble() && isWindowsDirver) {
            Button exportBtn = new Button();
            exportBtn.setText("导出");
            exportBtn.setHandler("Export");
            exportBtn.setCls("toolbar-redo");
            exportBtn.setIconCls("icon-redo");
            btnlist.add(exportBtn);
        }

        if (module.getPrintFile() != null && !module.getPrintFile().trim().isEmpty() && isWindowsDirver) {
            // 完全保持变量名与C#一致
            btnlist.add(new Button() {{
                List<Map<String, Object>> menulist = new ArrayList<>();
                setText("打印");
                String[] printFiles = module.getPrintFile().split("\\|");
                for (String pf : printFiles) {
                    String text = pf.split("\\.")[0];
                    Map<String, Object> item = new HashMap<>();
                    item.put("fname", pf);
                    item.put("text", text);
                    item.put("handler", "Print");
                    menulist.add(item);
                }
                setMenu(menulist);
                setIconCls("icon-print");
            }});
        }

        // 在按钮列表首位插入tbfill组件
        if (!btnlist.isEmpty()) {
            Button tbfillBtn = new Button();
            tbfillBtn.setXtype("tbfill");
            btnlist.add(0, tbfillBtn);
        }

        return btnlist;
    }

    private Object GetRightMenu(String fromkey, int menutype, Ref bbarItmes, String menuhandler) {
        List<Object> menusitems = new ArrayList<>();
        List<Map<String, Object>> rightMenuRows = GetRightMenuRows(fromkey, menutype);
        for (Map<String, Object> row : rightMenuRows) {
            Map<String, Object> menu = new HashMap<>();
            menu.put("orderid", row.get("orderid"));
            menu.put("mid", ((Number) row.get("id")).intValue()); // 对应ToInt32()
            menu.put("text", row.get("menucaption") + ""); // 转为字符串
            menu.put("menuCond", ConvertRightMenuSql(Objects.toString(row.get("menucond"), "")));
            menu.put("actiontype", ((Number) row.get("actiontype")).intValue());
            menu.put("handler", menuhandler);
            menu.put("beforeMsg", PublicUtil.ReqSqlPms(null, null, Objects.toString(row.get("beforemsg"), ""), SystemTypeEnums.PmType.ignorenull, getUser())); // 注意Java方法名小写开头
            menu.put("linkEvent", toBoolean(row.get("dbclick")) ? "rowdbclick" : null);
            menu.put("ifRefresh", ((Number) row.get("refresh")).intValue());
//            menu.put("multi", ((Boolean) row.get("multi")).booleanValue());
            menu.put("multi", toBoolean(row.get("multi")));
//            menu.put("toBar", ((Number) row.get("toBar")).intValue());
            menu.put("toBar", ToInt32(row.get("tobar")));
//            menu.put("merge", ((Boolean) row.get("merge")).booleanValue());
            menu.put("merge", toBoolean(row.get("merge")));
            menu.put("icon", row.get("icon") == null ? "" : row.get("icon"));
            menu.put("beforeModule", Objects.toString(row.get("beforetab"), ""));
//            menu.put("needRec", !((Boolean) row.get("notRec")).booleanValue());
            menu.put("needRec", !toBoolean(row.get("notrec")));
            Boolean[] hasAddress = new Boolean[1];
            menu.put("mergePms", getRightMenuMergePms(row, hasAddress)); // 调用方法，注意hasAddress的处理
            menu.put("address", hasAddress[0]);
            menu.put("disabletype", ((Number) row.get("disabletype")).intValue());
//            menu.put("copy", ((Boolean) row.get("isCopy")).booleanValue());
//            按钮是否用作copy？
            menu.put("copy", toBoolean(row.get("iscopy")));
//            2026.2.5新增
            menu.put("countAble", !isNullOrEmpty(row.get("countSql")));
            menu.put("isMrpClickBtn", toBoolean(row.get("isMrpClickBtn")));

            menusitems.add(menu);
            if (bbarItmes != null && bbarItmes.getList() != null
                    && (ToInt32(row.get("tobar"))) == 2) {
                // 先获取Ref内部的List，再调用add方法
                bbarItmes.getList().add(menu);
            }
        }
        return menusitems;
    }

    public Object GetRightMenu(String fromkey, int menutype, Ref bbarItmes) {
        String menuhandler = "rightclick";
        return GetRightMenu(fromkey, menutype, bbarItmes, menuhandler);
    }

    private List<String> getRightMenuMergePms(Map<String, Object> menuRow, Boolean[] hasAddress) {
        // 用数组传递hasAddress的引用（模拟C#的out参数）
        hasAddress[0] = false;
        if (menuRow == null) {
            return null;
        }

        // 解析merge字段（默认"0"）
        Boolean merge = toBoolean(get(menuRow, "merge", "0"));

        // 拼接参数字符串
        String pmsBuder = String.valueOf(menuRow.get("action")) +
                menuRow.get("param1") +
                menuRow.get("param2") +
                menuRow.get("param3") +
                menuRow.get("param4") +
                menuRow.get("param5") +
                menuRow.get("param6") +
                menuRow.get("param7") +
                menuRow.get("param8") +
                menuRow.get("param9") +
                menuRow.get("param10");

        // 获取参数名称列表
        List<String> paramNames = PublicUtil.getParamValue(pmsBuder.toLowerCase());

        // 判断是否包含地址相关参数
        hasAddress[0] = paramNames.contains("{mapaddress}")
                || paramNames.contains("{latitude}")
                || paramNames.contains("{longitude}");

        // 移除不需要的参数
        paramNames.remove("{loginname}");
        paramNames.remove("{loginid}");
        paramNames.remove("{logintype}");
        paramNames.remove("{serverid}");
        paramNames.remove("{loginclientid}");
        paramNames.remove("{password}");
        paramNames.remove("{mapaddress}");
        paramNames.remove("{latitude}");
        paramNames.remove("{longitude}");

        // 去除参数中的{}
        List<String> result = new ArrayList<>();
        for (String nm : paramNames) {
            result.add(nm.replace("{", "").replace("}", ""));
        }
        return result;
    }

    private String ConvertRightMenuSql(String res) {
        res = PublicUtil.ReqSqlPms(null, null, res, SystemTypeEnums.PmType.ignorenull, getUser());

        // 检查是否以@开头且不含{...}占位符
        if (res.startsWith("@") && !Pattern.compile("\\{(.*?)\\}").matcher(res).find()) {
            // 获取默认值并转换为布尔值
//            IPublicUtil util = new IPublicUtil();
            boolean result = toBoolean(createControl.GetDefaultValue(res, null, SystemTypeEnums.PmType.ignorenull));
            return result ? "1=1" : "1!=1";
        }
        // 转换SQL并返回
        return PublicUtil.SqlToCode(res) + "";
    }

    public List<Map<String, Object>> GetRightMenuRows(String fromKey, int menuType, int menuId) {
        return DataImpl.GetRightMenuRows(fromKey, menuType, getUser().UserName, menuId);
    }

    /**
     * 重载方法，处理menuId默认值为0的情况
     */
    public List<Map<String, Object>> GetRightMenuRows(String fromKey, int menuType) {
        return DataImpl.GetRightMenuRows(fromKey, menuType, getUser().UserName, 0);
    }

    private List<Map<String, Object>> GetCardColumnRows(String moduleId, String userId, String userName) {
        StringBuilder colSql = new StringBuilder("select id, FieldName, isnull(ISNULL(username1, sysname), FieldName) FieldCaption, bs_field defaultvalue, bs_color FontColor, bs_fontsize fontsize from p_systemwordbooktab");

// 拼接条件（当moduleId有效时）
        if (moduleId != null && !moduleId.trim().isEmpty()) {
            colSql.append(" where tab='")
                    .append(moduleId)
                    .append("' and bs_field<>'' and bs_order>0 order by bs_order");
        }
        if (colSql != null && !colSql.isEmpty()) {
            List<Map<String, Object>> dtVal = ConversionUtils.toLowerColumnName(jdbcTemplate.queryForList(colSql.toString()));
            return dtVal;
        }
        return null;
    }

    /**
     * 获取模块卡片分组信息
     *
     * @param moduleId 模块ID
     * @param isMain   是否为主卡片
     * @param isBase   是否为基础模块
     * @param mxId     明细ID
     * @param cellTpl  是否为单元格模板
     * @return 分组后的卡片信息
     */
    public Object GetModuleCardGroup(String moduleId, Boolean isMain, Boolean isBase, int mxId, boolean cellTpl) {
        // 处理isBase为空的情况
        if (isBase == null) {
            isBase = DataImpl.IsBaseModule(moduleId);
        }
        // 获取移动卡片列数据
        List<Map<String, Object>> cardTab = DataImpl.GetMobileCardColumn(moduleId, isMain, mxId, isBase, cellTpl);

        // 转换为MobileCard列表
        List<MobileCard> mobileCards = cardTab.stream()
                .map(row -> new MobileCard(row))
                .collect(Collectors.toList());

        if (cellTpl) {
            // 按ColName分组并转换为字典
            return mobileCards.stream()
                    .collect(Collectors.groupingBy(
                            MobileCard::getColName,
                            Collectors.toList()
                    ));
        } else {
            // 按GroupName分组并构建返回对象
            return mobileCards.stream()
                    .collect(Collectors.groupingBy(MobileCard::getGroupName))
                    .values().stream()
                    .map(group -> {
                        MobileCard firstCard = group.stream().findFirst().orElse(null);
                        if (firstCard == null) {
                            return null;
                        }
                        // 构建匿名对象（实际项目中建议使用实体类）
                        Map<String, Object> groupInfo = new HashMap<>();
                        groupInfo.put("name", group.stream().findFirst().get().getGroupName());
                        groupInfo.put("showText", firstCard.isGroupVisible());
                        groupInfo.put("mxId", firstCard.getMxId());
                        groupInfo.put("items", group);
                        return groupInfo;
                    })
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
        }
    }

    // 重载方法，处理默认参数
    public Object GetModuleCardGroup(String moduleId, Boolean isMain, Boolean isBase, int mxId) {
        return GetModuleCardGroup(moduleId, isMain, isBase, mxId, false);
    }

    public Object GetModuleCardGroup(String moduleId, Boolean isMain, Boolean isBase) {
        return GetModuleCardGroup(moduleId, isMain, isBase, 0, false);
    }


    //    setMian辅助方法
    public List<Component> GetColumns(String moduleId, boolean selfEdit) {
        List<Map<String, Object>> dtval = GetColumnRows(moduleId);
//        out.println("GetColumnRows ");
        if (dtval != null && !dtval.isEmpty()) {
            dtval = dtval.stream().map(HashMap::new) // 复制每一行
                    .collect(Collectors.toList());
        }

        // 查找并修改特定行
        if (dtval != null) {
            dtval.stream().filter(row -> SystemEnums.ControlType.LabTreeType.getValue() == NativeExtensionUtils.parseInt((row.get("fieldtype")).toString())).findFirst().ifPresent(treeRow -> treeRow.put("fieldtype", SystemEnums.ControlType.LabComboxValue.getValue()));
        }
//        out.println("GetColumns -> ToColumns");
        return ToColumns(dtval, selfEdit);
    }

    //    主方法
    public List<Component> ToColumns(List<Map<String, Object>> dtval, boolean selfEdit, ModuleEntity module) {
        AtomicInteger ti = new AtomicInteger();
        List<Component> columns = dtval.stream().map(row -> {
            RowColumn column = new RowColumn(row, selfEdit, createControl);
            column.setTabIndex(ti.getAndIncrement());
            String defaultValue = DataTableUtil.getRowVal(row, "defaultValue", "") + "";
            column.setDefaultval(createControl.GetDefaultValue(defaultValue, null, SystemTypeEnums.PmType.store));
            return column;
        }).collect(Collectors.toList());

        List<Field> hiddens = columns.stream()
                .filter(com -> {
                    RowColumn rowCol = (RowColumn) com;
                    Boolean hidden = rowCol.getHidden();
                    return hidden != null && hidden; // 替代 C# 的？？运算符
                })
                .map(com -> {
                    RowColumn rowCol = (RowColumn) com;
                    rowCol.ControlTabIndex = (-1); // 对应原代码的 columns.Count
                    return (Field) com;
                })
                .collect(Collectors.toList());

        List<Field> notHiddens = columns.stream()
                .filter(com -> {
                    RowColumn rowCol = (RowColumn) com;
                    Boolean hidden = rowCol.getHidden();
                    return hidden == null || !hidden; // 替代 C# 的？？运算符
                })
                .map(com -> (Field) com)
                .collect(Collectors.toList());

        return columns;
    }

    //    方法重载
    public List<Component> ToColumns(List<Map<String, Object>> dtval, boolean selfEdit) {
        return ToColumns(dtval, selfEdit, null); // 调用完整参数版本，传递默认值
    }

    /**
     * 获取行数据
     */
    public List<Map<String, Object>> GetColumnRows(String moduleId) {
        return DataImpl.GetColumnRows(moduleId, getUser().UserId, getUser().UserName, 0);
    }

    public List<Map<String, Object>> GetColumnRows(String moduleId, Integer id) {
        return DataImpl.GetColumnRows(moduleId, getUser().UserId, getUser().UserName, id);
    }

    private static final Pattern REGEX_PATTERN = Pattern.compile("[^\\d.\\-,]");

    /**
     * 公共函数接口，根据查询参数，分发具体查询sql的方法
     */
    @Override
    public BaseResponse getFieldData(String moduleId, Integer fieldId, String record, String leftRecord, String pams, String keyField, String keyValue, Integer fdtype, String contextMenuId, Boolean readOnly, String userId, String userName, String baseMainGridViewPrefix, Boolean windowsDirver, String ModuleCode, String MenuId) {
        BaseResponse response = new BaseResponse();
        boolean convertStr = false;
        BillModule module;
        List<Map<String, Object>> ColumnRows = null;
        String dataSource = "";
        if (fieldId > 0) {
            switch (fdtype) {
                case 1:
                    ColumnRows = DataImpl.GetCondition(null, fieldId, windowsDirver);
                    break;
                case 2:
                    ColumnRows = DataImpl.GetBillDetailColumns(null, userId, userName, fieldId);
                    break;
                case 3:
                    module = GetBillModule(ModuleCode, MenuId);
                    String Fromkey = module.getFromkey();
                    ColumnRows = DataImpl.GetControlRows(Fromkey, userName, null, fieldId);

                    break;
                case 4:
                    ColumnRows = DataImpl.GetBillMasterRows(null, userName, fieldId);
                    break;
                case 5:
                    ColumnRows = DataImpl.getDynamicSchemeFields(0, fieldId);
                    dataSource = DataImpl.getSchemeFieldDataSourceSql(fieldId);
                    break;
                case 99:
                    break; //用户下拉框
                default:
                    ColumnRows = GetColumnRows(null, fieldId);
                    break;
            }
        } else {
            switch (fieldId) {
                case -99:
                    try {
                        // 直接使用 JdbcTemplate 执行 SQL 查询
                        List<Map<String, Object>> data = jdbcTemplate.queryForList("select speciesno,speciesname,substring(speciesno,1,len(speciesno)-2) as pid from P_EmployeeSpecTab");

                        response.setSuccess(true);
                        response.setData(data);
                    } catch (Exception e) {
                        // 处理异常
                        response.setSuccess(false);
                        response.setMsg("执行 SQL 查询时出错: " + e.getMessage());
                    }
                    break;
            }
            return response;
        }
        if (ColumnRows != null && !ColumnRows.isEmpty()) {
            Map<String, Object> row = ColumnRows.get(0);
            String fromkey = row.get("fromkey") + "_Cond";
            IPublicUtil util = new IPublicUtil(jdbcTemplate);
            ComboBox field = null;
//          ComboBox field = (ComboBox) util.createControl(row, null, false);// new ComboBox(fieldRow, util);
//            安全转换
            Object control = util.createControl(row, null, false);
            if (control instanceof ComboBox) {
                field = (ComboBox) control;
            } else {
                // 类型不匹配时的处理（可选）
                field = null;
            }
            if (field == null) field = new ComboBox(row, util);
            if (field != null && !isNullOrEmpty(dataSource)) {
                field.setDataSource(dataSource);
            }
            if (keyValue != null && !keyValue.isEmpty() && !readOnly) {
                ComboBox _field = new ComboBox(row, util);
                keyField = keyField == field.getDisplayField() || keyField == field.getValueField() ? _field.getDisplayField() == _field.getValueField() ? field.getDisplayField() : field.getDisplayField() + "," + field.getValueField() : keyField;
                if (field.multiSelect) {
                    keyValue = keyValue.replace("%2C", ",");
                }
                // 对应C#: if (_field.columns != null)
                if (_field.getColumns() != null) {
                    keyField = "";
                    StringBuilder keyFieldBuilder = new StringBuilder();
                    List<Column> columns = _field.getColumns();
                    for (Column col : columns) {
                        // 关键：C#是把新列名拼在前面，所以这里要插入到builder头部
                        keyFieldBuilder.insert(0, col.dataIndex + ",");
                    }
                    // 2. 拼接displayField（拼在最前面）
                    // 对应C#: keyField = $"{_field.displayField},{keyField}";
                    if (_field.getDisplayField() != null) {
                        keyFieldBuilder.insert(0, _field.getDisplayField() + ",");
                    }

                    // 3. 拼接valueField（拼在最前面）
                    // 对应C#: keyField = $"{_field.valueField},{keyField}";
                    if (_field.getValueField() != null) {
                        keyFieldBuilder.insert(0, _field.getValueField() + ",");
                    }

                    // 4. 正则校验：判断是否需要转换字符串
                    // 对应C#: if (field.displayField != field.valueField && new Regex(@"[^\d\.\-\,]").IsMatch(keyValue))
                    if (field != null
                            && !field.getDisplayField().equals(field.getValueField())
                            && keyValue != null
                            && REGEX_PATTERN.matcher(keyValue).find()) {
                        convertStr = true; // Java用数组传递布尔值（模拟引用传递）
                    }

                    // 5. 去除末尾逗号（对应C#: keyField = keyField.Trim(',');）
                    keyField = keyFieldBuilder.toString().replaceAll(",$", "");
                }
            } else if (keyValue != null && !keyValue.isEmpty() && readOnly) {
                keyField = keyField != null && !keyField.isEmpty() ? field.getDisplayField() == field.getValueField() ? field.getDisplayField() : field.getValueField() : keyField;
            }
            Boolean doNotSpelling = field.isDoNotSpelling();
            if (field.getDataSource() != null && !field.getDataSource().isEmpty()) {
                String querySql = field.getDataSource();
                String deledSql;
                if (readOnly && !field.getDisplayField().equals(field.getValueField()) && Pattern.compile("[^\\d\\.\\-,]").matcher(keyValue).find()) {
                    String _testSql = new SqlAnalyzer(querySql.replace("#", "")).InsertWhere(new HashMap<String, String>() {{
                        put("a", "and 1!=1");
                    }}, false, false, false);
                    // List<Map<String, Object>> _tbVal = jdbcTemplate.queryForList(PublicUtil.ReqSqlPmsByRow(null, null, _testSql, SystemTypeEnums.PmType.sql, null));
//                    String keyColName = null;
//                    // 查找目标字段名（忽略大小写）
//                    if (!_tbVal.isEmpty()) {
//                        Map<String, Object> firstRow = _tbVal.get(0);
//                        for (String colName : firstRow.keySet()) {
//                            if (colName.toLowerCase().equals(keyField.toLowerCase())) {
//                                keyColName = colName;
//                                break;
//                            }
//                        }
//                    } else {
//                        // 结果集为空时，通过SQL解析表名并查询元数据获取字段名
//                        keyColName = getKeyColNameFromMetaData(_testSql, keyField);
//                    }
//
//                    // 判断字段是否为整数类型（兼容结果集有值和空结果集的情况）
//                    if (keyColName != null) {
//                        // 1. 若结果集有值，直接判断值的类型
//                        if (!_tbVal.isEmpty()) {
//                            Object value = _tbVal.get(0).get(keyColName);
//                            if (value instanceof Integer || value instanceof Long) {
//                                convertStr = true;
//                            }
//                        } else {
//                            // 2. 结果集为空时，通过元数据判断字段类型
//                            if (isIntegerColumnFromMetaData(_testSql, keyColName)) {
//                                convertStr = true;
//                            }
//                        }
//                    }

                    convertStr = checkIfIntColumn(_testSql, keyField);
                }
                Hashtable<String, Object> _leftRecord = null;
                Hashtable<String, Object> pms = null;

                if (field.getFieldType() == SystemEnums.ControlType.LabTreeType.getValue() && !"_Cond".equals(fromkey)) {
                    if (leftRecord != null && !leftRecord.isEmpty()) {
                        _leftRecord = new Hashtable<>();
                        // 假设这里有 JSON 解析方法
                        _leftRecord = (Hashtable) JSON.Decode(leftRecord);
                    }
                    if (pams != null && !pams.isEmpty()) {
                        pms = new Hashtable<>();
                        // 假设这里有 JSON 解析方法
                        pms = (Hashtable) JSON.Decode(pams);
                    }
                    querySql = reqSearchCondition(fromkey, field.getDataSource(), pms, _leftRecord, false);
                }

                if (contextMenuId != null && !contextMenuId.isEmpty()) {
                    List<Map<String, Object>> dtval = GetRightMenuRows("", Integer.parseInt(contextMenuId), 0, true);
                    SysPoPupMenuBtn btn = null;
                    for (Map<String, Object> _row : dtval) {
                        btn = new SysPoPupMenuBtn();
                        btn.dllpar3 = PublicUtil.ReqSqlPms(_leftRecord, null, row.get("param3").toString(), SystemTypeEnums.PmType.sql, null);
                        if (btn.dllpar3 != null && !btn.dllpar3.isEmpty()) {
                            break;
                        }
                    }
                    if (btn != null && btn.dllpar3 != null && !btn.dllpar3.isEmpty()) {
                        ComboBox finalField = field;
                        SysPoPupMenuBtn finalBtn = btn;
                        querySql = new SqlAnalyzer(querySql).InsertWhere(new HashMap<String, String>() {{
                            put(finalField.getName(), finalBtn.dllpar3);
                        }}, false, false, false);
                    }
                }

                if (convertStr && keyValue != null && !keyValue.isEmpty()) {
                    if (field.getColumns() != null) {
                        StringBuilder keyFieldBuilder = new StringBuilder();
                        for (int i = 0; i < field.getColumns().size(); i++) {
                            keyFieldBuilder.append(field.getColumns().get(i).dataIndex);
                            if (i < field.getColumns().size() - 1) {
                                keyFieldBuilder.append(",");
                            }
                        }
                        keyField = keyFieldBuilder.toString();
                    } else {
                        keyField = field.getDisplayField().equals(field.getValueField()) ? field.getDisplayField() : field.getDisplayField() + "," + field.getValueField();

                    }
                }
                deledSql = dealQuerySql(querySql, record, leftRecord, pams, keyField, keyValue, readOnly, convertStr, doNotSpelling);
                try {
                    if (keyValue != null && (!keyValue.contains(",") || !deledSql.contains(";"))) {
                        List<Map<String, Object>> dtVal = dbOperator.executeDataTable(deledSql, getStartsize(), getPageSize(), tot);
                        if (dtVal.size() > 1 && keyValue != null && !keyValue.isEmpty() && !keyValue.contains(",") && readOnly) {
                            deledSql = dealQuerySql(querySql, record, leftRecord, pams, keyField, keyValue, false, convertStr, false);
                            dtVal = dbOperator.executeDataTable(deledSql, getStartsize(), getPageSize(), tot);
                        }
                        response.setData(toHashTable(dtVal));
                        response.setSuccess(true);
                        response.setTot(tot[0] != 0 ? tot[0] : dtVal.size());
                    }
                } catch (Exception e) {
                    response.setMsg(e.getMessage());
                    LoggerHandler.error(this, String.format("解析字典数据出错=>%s:%d=>%s", moduleId, fieldId, e.getMessage()));
//                    throw new RuntimeException(String.format("解析字典数据出错=>%s:%d=>%s", moduleId, fieldId, e.getMessage()), e);
                }
            }
        } else if (fdtype == 99) {
            response.setData(new SysUserImpl().GetUserByName(keyValue, ""));
        }
        response.setSuccess(true);
        return response;
    }

    /**
     * 从请求参数列表中获取属性,传参给公共的接口，查询数据库数据
     * 获取请求的参数
     * 修改为baseimpl统一获取参数
     */
    @Override
    public BaseResponse getFieldDataPam() {
        Integer fieldId = toInt32(Request("id", ""));
        Integer fdtype = toInt32(Request("fdtype", ""));
        String fieldValue = Request("fieldValue", "");
        String record = Request("record", ""),
                leftRecord = Request("leftRecord", ""),
                poppms = Request("poppms", ""),
                pms = Request("pms", ""),
                contextMenuId = Request("contextMenuId", ""),
                keyField = Request("textField", ""),
                keyValue = Request(keyField, "");
        Boolean windowsDirver = toBoolean(Request("windowsDirver", ""));
        Boolean readOnly = toBoolean(Request("readOnly", ""));
        String baseMainGridViewPrefix = Request("baseMainGridViewPrefix", "BaseMainGridView_");
        String ModuleId = Request("moduleId", "");
        String MenuCode = Request("menucode", "");
        String MenuId = Request("menuId", "");
        String ModuleCode = isNullOrEmpty(ModuleId) ? ModuleId : MenuCode;

        String userId = getUser().UserId;
        String userName = getUser().UserName;
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            if (poppms.startsWith("{")) {
                if (!record.startsWith("{")) {
                    record = poppms;
                } else {
                    Map<String, Object> r = objectMapper.readValue(record, HashMap.class);
                    if (r != null) {
                        Map<String, Object> poppmsMap = objectMapper.readValue(poppms, HashMap.class);
                        for (Map.Entry<String, Object> entry : poppmsMap.entrySet()) {
                            r.put(entry.getKey(), entry.getValue());
                        }
                        record = objectMapper.writeValueAsString(r);
                    } else {
                        record = poppms;
                    }
                }
            }
        } catch (Exception e) {
            log.error("Exception caught", e);
        }
        return getFieldData(ModuleId, fieldId, record, leftRecord, pms, keyField, keyValue, fdtype, contextMenuId, readOnly, userId, userName, baseMainGridViewPrefix, windowsDirver, ModuleCode, MenuId);
    }

    /**
     * getModuleData：Api请求方法（获取请求参数），具体交给GetModuleData处理
     */
    @Override
    public BaseResponse getModuleData() {
        BaseResponse response = new BaseResponse();
        String record = Request("record", ""),
                leftRecord = Request("leftRecord", ""),
                pms = Request("pms", ""),
                detailId = Request("detailId", ""),
                contextMenuId = Objects.toString(Request("contextMenuId"), ""),
                sourceId = Request("sourceId", "");
        Integer attcId = toInt32(Request("attcId", ""));
        Boolean other = toBoolean(Request("other", "1"));
        String ModuleId = Request("moduleId", "").split(",")[0];
        if (!isNullOrEmpty(sourceId)) {
            //单据来源数据
            response = GetBillSourceData(ModuleId, sourceId, pms, Request("w", ""), leftRecord, toBoolean(Request("detail", "")));
        } else if (isNullOrEmpty(detailId) || NativeExtensionUtils.parseInt(detailId) <= 0) {
            //基础档案
            response = GetModuleData(ModuleId, record, leftRecord, pms, Request("w", ""), toBoolean(Request("multi", "")), toBoolean(Request("grid", "")), ToInt32(contextMenuId), attcId);
            if (!other) {
                response.setOther(null);
            }
        } else {
            //基础档案明细
            response = GetModuleDetailData();
        }
        return response;
    }

    /**
     * /// <summary>
     * /// Gets the source data.
     * /// </summary>
     * /// <param name="moduleId">The module identifier.</param>
     * /// <param name="sourceId">The source identifier.</param>
     * /// <param name="pams">The _pams.</param>
     * /// <param name="leftRecords">The left records.</param>
     * /// <param name="isDetail">if set to <c>true</c> [is detail].</param>
     * /// <returns>BaseResponse.</returns>
     */
    public List<Map<String, Object>> GetCondition(String fromkey, Integer id) {
        if ((fromkey == null || fromkey.isEmpty()) && id == 0) return null;
        return DataImpl.GetCondition(fromkey, id, isWindowsDirver());
    }


    public BaseResponse GetBillSourceData(String moduleId, String sourceId, String pams, String w, String leftRecords, Boolean isDetail) {
        BaseResponse response = new BaseResponse();
        Map<String, Object> pms = null;
        List<Map<String, Object>> leftRcs = new ArrayList<>();

        // 获取账单源模块
        List<BillSourceModule> modules = GetBillSourceModule(moduleId, sourceId, "", true);
        BillSourceModule module = modules.isEmpty() ? null : modules.get(0);

        if (module != null) {
            // 解析参数
            if (pams != null && !pams.isEmpty()) {
                pms = (Map<String, Object>) JSON.Decode(pams);
            }

            // 解析左侧记录
            if (leftRecords != null && !leftRecords.isEmpty()) {
                if (!leftRecords.startsWith("[")) {
                    leftRecords = "[" + leftRecords + "]";
                }
                leftRcs = (ArrayList) JSON.Decode(leftRecords);
            }

            // 处理SQL
            String sourceSql = (isDetail ? module.getDetailSql() : module.getMasterSql());
            if (sourceSql == null) sourceSql = "";
            sourceSql = sourceSql.toLowerCase();

            List<Map<String, Object>> styles = null;
            Function<Map<String, Object>, Map<String, Object>> fillStyles = null;
            int sourceType = module.getSourceType();
            if (!isDetail) {
                if (sourceType != 1) {
                    sourceSql = reqSearchCondition(module.getFromkey(), sourceSql, pms, null, false);
                }
//                out.println(" sourceSql = reqSearchCondition(module.getFromkey(), sourceSql, pms, null, false)");
                // 替换#...#为1=1
                sourceSql = sourceSql.replaceAll("(#)(.|\\n)*?(#)", "1=1");
                sourceSql = PublicUtil.ReqSqlPms(null, null, sourceSql, SystemTypeEnums.PmType.sql, getUser());
                // 获取样式
//                out.println("获取样式 1");
                styles = GetStyles(PublicUtil.GetBillConMenuKey(SystemEnums.BillMenuEnum.BillSource) + module.getFromkey());
//                out.println("获取样式 2");
                if (styles != null) {
                    List<Map<String, Object>> finalStyles = styles;
                    fillStyles = row -> FillStyles(row, finalStyles);
//                    out.println("fillStyles ENd");
                }
            } else {
                switch (sourceType) {
                    case 1:
                        Map<String, String> where = new HashMap<>();
                        List<Map<String, Object>> condition = DataImpl.GetCondition(module.getFromkey(), 0, toBoolean(Request("windowsDirver", "")));
                        styles = GetStyles(PublicUtil.GetBillConMenuKey(SystemEnums.BillMenuEnum.BillSourceDetail) + module.getFromkey());
                        if (styles != null) {
                            List<Map<String, Object>> finalStyles1 = styles;
                            fillStyles = row -> FillStyles(row, finalStyles1);
                        }

                        if (condition != null && !condition.isEmpty()) {
                            sourceSql = reqSearchCondition(condition, sourceSql, pms, null, false);
                        } else if (pms != null) {
                            for (Map.Entry<String, Object> entry : pms.entrySet()) {
                                where.put(entry.getKey(),
                                        String.format(" and %s like '%s%%'",
                                                entry.getKey(),
                                                entry.getValue()));
                            }
                        }

                        // 处理左侧记录条件
                        if (leftRcs != null && !leftRcs.isEmpty()) {
                            Pattern consReg = Pattern.compile(
                                    String.format("%s\\s+like\\s+'\\{%s\\}%%'",
                                            module.getIdField(), module.getIdField()),
                                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                            );

                            // 收集左侧记录ID值
                            List<String> vals = new ArrayList<>();
                            for (Map<String, Object> rec : leftRcs) {
                                Object idValue = rec.get(module.getIdField());
                                vals.add(idValue != null ? idValue.toString() : "");
                            }


                            String condStr = String.format(
                                    leftRcs.size() > 1 ? "%s in ('%s')" : "%s like '%s%%' ",
                                    module.getIdField(),
                                    String.join("','", vals)
                            );

                            Matcher matcher = consReg.matcher(sourceSql);
                            sourceSql = matcher.replaceAll(condStr);
                        }

                        // 插入where条件
                        if (!where.isEmpty()) {
                            sourceSql = new SqlAnalyzer(sourceSql).InsertWhere(where, false, false, false);
                        }

                        // 处理SQL参数
                        Map<String, Object> firstLeftRec = null;
                        firstLeftRec = (leftRcs != null && !leftRcs.isEmpty()) ? leftRcs.get(0) : null;
                        sourceSql = PublicUtil.ReqSqlPms(firstLeftRec, null, sourceSql, SystemTypeEnums.PmType.sql, null);
                        break;

                    default:
                        // 处理默认类型
                        Map<String, Object> defaultLeftRec = null;
                        defaultLeftRec = leftRcs != null && leftRcs.size() > 0 ? leftRcs.get(0) : null;
                        sourceSql = PublicUtil.ReqSqlPms(defaultLeftRec, null, sourceSql, SystemTypeEnums.PmType.sql, getUser());
                        break;
                }
            }

            // 处理额外where条件
            if (w != null && !w.isEmpty()) {
                Map<String, String> whereDict = new HashMap<>();
                whereDict.put("$where", w);
                sourceSql = new SqlAnalyzer(sourceSql).InsertWhere(whereDict, false, false, true);
            }

            // 处理查询SQL
            Map<String, Object> dealLeftRec = (leftRcs != null && !leftRcs.isEmpty()) ? leftRcs.get(0) : null;
            sourceSql = dealQuerySql(sourceSql, null, dealLeftRec, null, null, null, false, false, false);
            sourceSql = PublicUtil.fixSqlCompatibility(sourceSql);
//            out.println(sourceSql + "sourceSql");
            // 执行查询
            List<Map<String, Object>> resultTable = dbOperator.executeDataTable(sourceSql, getStartsize(), getPageSize(), tot);
            int tot1 = tot[0] != 0 ? tot[0] : resultTable.size();
//            out.println("执行查询 end");

            // 转换结果
            response.setData(toHashTable(resultTable, null, null, styles != null && !styles.isEmpty() ? fillStyles : null, true));
            response.setTot(tot1);
            response.setSuccess(true);
        }
        return response;
    }

    /**
     * 获取账单模块信息
     *
     * @param moduleCode 模块代码
     * @param menuId     菜单 ID
     * @return 账单模块信息
     */
    public BillModule GetBillModule(String moduleCode, String menuId) {
        BillModule module = null;
        List<Map<String, Object>> dtValue = DataImpl.GetBillModule(moduleCode, menuId);
        if (dtValue != null && !dtValue.isEmpty()) {
            module = new BillModule(dtValue.get(0));
        }
        if (module != null && !isNullOrEmpty(menuId) && NativeExtensionUtils.parseInt(menuId) > 0) {
            module.OperAble = Objects.equals(createControl.CheckPurview(getUser().PurviewStr, menuId), "AllPurview");
        }
        return module;
    }


    /**
     * /// <summary>
     * /// 执行条件替换
     * /// </summary>
     * /// <param name="condkey">The condkey</param>
     * /// <param name="sourceSql">The source SQL</param>
     * /// <param name="pms">The PMS</param>
     * /// <param name="leftRecord">The left record</param>
     * /// <param name="repCond">是否只执行变量替换</param>
     * /// <returns>System.String</returns>
     * /// <remarks>
     * /// </remarks>
     */
    public String reqSearchCondition(String condkey, String sourceSql, Map<String, Object> pms, Map<String, Object> leftRecord, boolean repCond) {
        if (condkey == null || condkey.isEmpty()) {
            return sourceSql;
        }
        List<Map<String, Object>> dt = DataImpl.GetCondition(condkey, 0, true);
        return reqSearchCondition(dt, sourceSql, pms, leftRecord, repCond);
    }

    public String reqSearchCondition(List<Map<String, Object>> dt, String sourceSql, Map<String, Object> pms, Map<String, Object> leftRecord, boolean repCond) {
        Map<String, String> where = new HashMap<>();
        Hashtable<String, Object> record = new Hashtable<>();
        List<String> Pms = new PmAnalyzer(getUser(), sourceSql).getPms();
//        out.println(dt.toString() + Pms.toString() + "reqSearchCondition");
        for (Map<String, Object> row : dt) {
            Field field = new Field(row);
            field.setIssearchcontrol(true);
            if (field.isDisabled2()) {
                continue;
            }
            String controlName = field.getName();
            int condId = field.getFieldId();
            String cond = Objects.toString(field.getWhereCond(), "").trim().toLowerCase();
            if (cond.startsWith("where")) {
                cond = " and " + cond.substring(5);
            }
            String fieldTypeStr = Objects.toString(field.getFieldType(), "");
            SystemEnums.ControlType fieldType = null;
            try {
                int typeValue = Integer.parseInt(fieldTypeStr);
                fieldType = SystemEnums.ControlType.fromValue(typeValue);
            } catch (IllegalArgumentException e) {
                // 处理无法解析的枚举值
                log.warn(String.valueOf("无法解析的 ControlType: " + fieldTypeStr));
            }
//            out.println("fieldTypeStr " + fieldType + "pms" + pms);
            Pattern inReg = Pattern.compile(" +in ?\\(");
            String pmValue = "";
//            out.println(!pms.containsKey(controlName) + "!pms.containsKey(controlName)" + pms + pms.get(controlName));
            if (pms == null || pms.isEmpty() || !pms.containsKey(controlName)) {
                if (fieldType != null && fieldType.toString().contains("LabCheck")) {
                    continue;
                }
//                IPublicUtil control = new IPublicUtil();
                pmValue = createControl.GetDefaultValue(field.getDefaultsource() != null ? field.getDefaultsource() : ""
                        , new ModuleEntity() {{
                            setLeftRecord(leftRecord);
                        }}, SystemTypeEnums.PmType.sql);
//                out.println(" pmValue = createControl.GetDefaultValue " + pmValue + field.getDefaultsource());
                if ("''".equals(pmValue)) {
                    pmValue = "";
                }
            } else if ((pms.containsKey(controlName) && !(pmValue = pms.get(controlName) + "").isEmpty()) || (pms.containsKey(condId + "") && !(pmValue = pms.get(condId + "") + "").isEmpty()) || (pms.containsKey(condId + "") && !(pmValue = pms.get(condId + "") + "").isEmpty())) {
                if (pmValue.contains(",") && fieldTypeStr.toLowerCase().contains("multi") && inReg.matcher(cond.toLowerCase()).find()) {
                    pmValue = pmValue.replace(",", "','");
                }
                if (sourceSql.contains("{&" + controlName + "}")) {
                    sourceSql = sourceSql.replace("{&" + controlName + "}", "{" + controlName + "}");
                    Hashtable<String, Object> valueMap = new Hashtable<>();
                    valueMap.put("value", pmValue);
                    pmValue = PublicUtil.ReqSqlPms(valueMap, leftRecord, cond, SystemTypeEnums.PmType.sql, getUser());
                }
            }

            if (Pms.contains(controlName.toLowerCase())) {
                record.put(controlName.toLowerCase(), pmValue);
                cond = "";
            }
            if (!cond.isEmpty()) {
                Hashtable<String, Object> valueMap = new Hashtable<>();
                valueMap.put("value", pmValue);
//                cond = PublicUtil.ReqSqlPms(valueMap, leftRecord, cond, SystemTypeEnums.PmType.sql, getUser());
                cond = cond.toLowerCase().replace("{value}", pmValue);
                where.put(controlName, cond);
//                out.println(cond + where + "cond+where");
            }
        }

        // 对where进行排序，与C#逻辑保持一致
        List<Map.Entry<String, String>> whereList = new ArrayList<>(where.entrySet());
        whereList.sort((kv1, kv2) -> {
            boolean kv1HasOrderBy = kv1.getValue().contains("order by");
            boolean kv2HasOrderBy = kv2.getValue().contains("order by");

            if (kv1HasOrderBy && !kv2HasOrderBy) {
                return 1; // 含order by的放后面
            } else if (!kv1HasOrderBy && kv2HasOrderBy) {
                return -1; // 不含order by的放前面
            } else {
                return 0; // 相同类型保持原有顺序
            }
        });
        // 将排序后的列表转换为LinkedHashMap（保持顺序）
        where = new LinkedHashMap<>();
        for (Map.Entry<String, String> entry : whereList) {
            where.put(entry.getKey(), entry.getValue());
        }
        sourceSql = PublicUtil.ReqSqlPms(record, leftRecord, sourceSql, SystemTypeEnums.PmType.ignorenull, getUser());
        if (!repCond) {
            sourceSql = new SqlAnalyzer(sourceSql).InsertWhere(where, false, false, false);
        }
        return sourceSql;
    }

    /**
     * /// <summary>
     * /// Gets the right menu rows.
     * /// </summary>
     * /// <param name="fromkey">The fromkey.</param>
     * /// <param name="menutype">The menutype.</param>
     * /// <param name="menuid">The menuid.</param>
     * /// <returns>List<Map<String,Object>>.</returns>
     * /// <remarks>
     * /// </remarks>
     */
    public List<Map<String, Object>> GetRightMenuRows(String fromkey, int menutype, int menuid, boolean windowsDirver) {
        if (menuid > 0) {
//            return crmapper.selectSystemPopupMenuById(menuid, fromkey, getUser().UserName);
            return getDetailJDBC().selectSystemPopupMenuById(menuid, fromkey, getUser().UserName);
        } else {
//            return crmapper.selectSystemPopupMenuByType(menutype, fromkey, getUser().UserName, windowsDirver);
            return getDetailJDBC().selectSystemPopupMenuByType(menutype, fromkey, getUser().UserName, windowsDirver);
        }
    }

    /**
     * /// <summary>
     * /// Deals the query SQL.
     * /// </summary>
     * /// <param name="querySql">The query SQL</param>
     * /// <param name="_record">The _record</param>
     * /// <param name="_leftRecord">The _left record</param>
     * /// <param name="_pams">The _pams</param>
     * /// <param name="keyField">The key field</param>
     * /// <param name="keyValue">The key value</param>
     * /// <param name="readOnly">true 精确匹配，去掉带#号条件，前端下拉框在readonly情况下</param>
     * /// <param name="convertStr">是否转换字段进行匹配convert(varchar(1000),name)</param>
     * /// <param name="doNotSpelling">匹配时是否搜索拼音（影响查询速度）</param>
     * /// <returns>System.String</returns>
     */
    public String dealQuerySql(String querySql, String _record, String _leftRecord, String _pams, String keyField, String keyValue, boolean readOnly, boolean convertStr, boolean doNotSpelling) {
        Map<String, Object> record = null, leftRecord = null, pms = null;

        if (_record != null && !_record.isEmpty()) {
            record = (Map<String, Object>) JSON.Decode(_record);
        }
        if (_leftRecord != null && !_leftRecord.isEmpty()) {
            leftRecord = (Map<String, Object>) JSON.Decode(_leftRecord);
        }
        if (_pams != null && !_pams.isEmpty()) {
            pms = (Map<String, Object>) JSON.Decode(_pams);
        }

        return createControl.dealQuerySql(querySql, record, leftRecord, pms, keyField, keyValue, false, readOnly, convertStr, doNotSpelling);
    }

    public String dealQuerySql(String querySql, Map<String, Object> record, Map<String, Object> leftRecord, Map<String, Object> pams, String keyField, String keyValue, Boolean precise, Boolean readOnly, Boolean convertStr) {
        return createControl.dealQuerySql(querySql, record, leftRecord, pams, keyField, keyValue, precise, readOnly, convertStr, false);
    }

    //专门给附件的
    public String DealQuerySqlfor(String querySql, Map<String, Object> record, Map<String, Object> leftRecord, Map<String, Object> pams, String keyField, String keyValue, Boolean precise, Boolean readOnly, Boolean convertStr) {
        return createControl.DealQuerySql(querySql, record, leftRecord, pams, keyField, keyValue, precise, readOnly, convertStr, false);
    }

    public String dealQuerySql(String querySql, Map<String, Object> record, Map<String, Object> leftRecord, Map<String, Object> pams, String keyField, String keyValue) {
        return dealQuerySql(querySql, record, leftRecord, pams, keyField, keyValue, false, false, false);
    }

    /**
     * 获取样式数据表格
     *
     * @param fromkey 表格标识
     * @return 转换为小写列名的DataTable
     */
    public List<Map<String, Object>> GetStyles(String fromkey) {
//        String sql = String.format("select condition cond,forcecolor FontColor,backcolor,dfcolor,ifBold Bold,ifItalic IncLine,ifStrickOut DeleteLine,ifUnderLine UnderLine,fontsize from  p_systemwordbookcolor where tab='{0}' and isnull(useflag,0)=0 order by orderid", fromkey);
        // 参数化SQL查询，避免SQL注入
        String sql = "select condition as cond, " +
                "forcecolor as FontColor, " +
                "backcolor, " +
                "dfcolor, " +
                "ifBold as Bold, " +
                "ifItalic as IncLine, " +
                "ifStrickOut as DeleteLine, " +
                "ifUnderLine as UnderLine, " +
                "fontsize " +
                "from p_systemwordbookcolor " +
                "where tab = ? " +
                "and isnull(useflag, 0) = 0 " +
                "order by orderid";

        // 执行查询并处理结果集列名小写转换
        return jdbcTemplate.query(sql, new Object[]{fromkey}, (rs, rowNum) -> {
            Map<String, Object> row = new HashMap<>();
            for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
                // 列名转换为小写
                row.put(rs.getMetaData().getColumnName(i).toLowerCase(), rs.getObject(i));
            }
            return row;
        });
    }

    /**
     * 填充样式信息
     *
     * @param row     数据行（Map形式）
     * @param colorDt 样式数据表（List<Map>形式）
     * @return 包含行样式和单元格样式的Hashtable
     */
    protected Map<String, Object> FillStyles(Map<String, Object> row, List<Map<String, Object>> colorDt) {
        if (colorDt == null || colorDt.isEmpty()) {
            return null;
        }

        Hashtable<String, Object> colorTab = new Hashtable<>();
        List<String> rowStyles = new ArrayList<>();
        Hashtable<String, String> cellStyles = new Hashtable<>();
        Pattern regex = Pattern.compile("\\[([^\\[])*?\\]");

        for (Map<String, Object> colorRow : colorDt) {
            ModuleColor color = createModuleColor(colorRow);
            String cond = color.Cond;
            String key = "rowcolor";
            boolean isCell = false;

            if (cond == null || cond.trim().isEmpty()) {
                continue;
            }

            // 处理条件中的[]占位符
            Matcher matcher = regex.matcher(cond);
            List<String> matches = new ArrayList<>();
            while (matcher.find()) {
                matches.add(matcher.group());
            }

            // 获取样式数组和样式字符串

            ArrayList<String> styles = color.ToSArray(); // 需在ModuleColorEntity中实现ToSArray方法
            String style = ModuleColor.ToStyleStr(styles); // 需实现静态方法toStyleStr
            if (style == null || style.trim().isEmpty()) {
                continue;
            }

            // 处理单元格样式标识
            if (!matches.isEmpty()) {
                cond = cond.replace("[", "{").replace("]", "}");
                key = matches.get(0).replace("[", "").replace("]", "").toLowerCase();
                isCell = true;
            }

            // 验证条件是否成立
            if (toBoolean(getUtil().evalCond(cond, row, null))) {
                if (isCell) {
                    cellStyles.put(key, style);
                } else {
                    rowStyles.addAll(styles);
                }
            }
        }

        // 组装单元格样式
        if (!cellStyles.isEmpty()) {
            colorTab.put("cellstyle", cellStyles);
        }

        // 组装行样式（去重处理）
        if (!rowStyles.isEmpty()) {
            rowStyles = removeDuplicates(rowStyles);
            colorTab.put("rowstyle", ModuleColor.ToStyleStr(rowStyles));
        }

        return colorTab;
    }

    /**
     * 创建ModuleColorEntity实例
     */
    private ModuleColor createModuleColor(Map<String, Object> row) {
        ModuleColor color = new ModuleColor();
        color.Cond = DataTableUtil.getStringValue(row, "cond", "");
        color.FontColor = DataTableUtil.getStringValue(row, "fontcolor", "");
        color.BackColor = DataTableUtil.getStringValue(row, "backcolor", "");
        color.FontSize = DataTableUtil.getIntValue(row, "fontsize", 0);
        color.Bold = DataTableUtil.getBooleanValue(row, "bold", false);
        color.IncLine = DataTableUtil.getBooleanValue(row, "inclline", false);
        color.DeleteLine = DataTableUtil.getBooleanValue(row, "deleteline", false);
        color.UnderLine = DataTableUtil.getBooleanValue(row, "underline", false);
        return color;
    }

    /**
     * 评估条件是否成立（使用PmAnalyzer处理条件表达式）
     */
    private boolean evalCond(String cond, Map<String, Object> row) {
        // 这里简化实现，实际应根据原有util.EvalCond逻辑实现
        PmAnalyzer analyzer = new PmAnalyzer(null, cond);
        String processedCond = analyzer.FillPms(row, null);
        // 假设条件处理后为"true"/"false"字符串，实际场景可能需要更复杂的表达式解析
        return "true".equalsIgnoreCase(processedCond);
    }

    /**
     * 移除列表中的重复对象
     */
    private List<String> removeDuplicates(List<String> list) {
        Set<String> set = new LinkedHashSet<>(list);
        return new ArrayList<>(set);
    }

    /**
     * 获取单据来源模块信息
     *
     * @param moduleId   模块ID
     * @param id         标识符
     * @param sourceType 来源类型（-1：所有，0：表格，1：树，2：单据管理）
     * @param moduleOnly 是否只返回模块基本信息
     * @return 单据来源模块列表
     */
    public List<BillSourceModule> GetBillSourceModule(String moduleId, String id, String sourceType, boolean moduleOnly) {
        // 假设dataImpl是已注入的数据源操作实例
        List<Map<String, Object>> dtVal = DataImpl.GetBillSource(moduleId, id, sourceType);
        List<BillSourceModule> modules = new ArrayList<>();
        List<Object> bbItems = new ArrayList<>();

        for (Map<String, Object> row : dtVal) {
            BillSourceModule sourceModule = new BillSourceModule(row);
            sourceModule.setModuleId(moduleId);

            if (!moduleOnly) {
                // 获取工具栏项目
                sourceModule.setTbarItems(GetCondition(sourceModule.getFromkey(), 0));
//                out.println(GetCondition(sourceModule.getFromkey(), 0) + " firstStep ");
                // 创建数据存储对象
                DataStore store = new DataStore();
                store.extraParams = (Map.of(
                        "moduleId", moduleId,
                        "sourceId", sourceModule.getSourceId()
                ));
//                out.println(store.extraParams + " store.extraParams " + sourceModule.getSourceType() + " sourceModule.getSourceType()");
                // 根据源类型创建不同的主面板
                if (sourceModule.getSourceType() == 1) {
                    TreePanel treePanel = new TreePanel();
                    treePanel.title = (sourceModule.getTitle());
                    treePanel.store = (store);
                    treePanel.valueField = (sourceModule.getIdField());
                    treePanel.displayField = (sourceModule.getDisplayField());
                    treePanel.TbarItems = (sourceModule.getTbarItems());
                    sourceModule.setMain(treePanel);
                } else {
                    GridPanel gridPanel = new GridPanel();
                    gridPanel.title = (sourceModule.getTitle());
                    gridPanel.PageAble = (true);
                    gridPanel.TbarItems = (sourceModule.getTbarItems());
                    gridPanel.RightMenu = (GetRightMenu(
                            PublicUtil.GetBillConMenuKey(SystemEnums.BillMenuEnum.BillSource) + sourceModule.getFromkey(),
                            0,
                            new Ref(bbItems),
                            "rightclick"
                    ));
                    // 获取列配置并转换
                    List<Map<String, Object>> columnsData = DataImpl.GetBillSourceColumns(
                            sourceModule.getSourceId() + "",
                            getUser().UserId
                    );
                    gridPanel.setColumns(ToColumns(columnsData, false));
                    gridPanel.setStore(store);
                    sourceModule.setMain(gridPanel);
                }

                // 处理详情面板
                List<Map<String, Object>> detailColumns = DataImpl.GetBillSourceDetailColumns(
                        sourceModule.getSourceId() + "",
                        getUser().UserId
                );

                GridPanel detailGrid = new GridPanel();
                detailGrid.PageAble = (true);
                detailGrid.RightMenu = (GetRightMenu(
                        PublicUtil.GetBillConMenuKey(SystemEnums.BillMenuEnum.BillSourceDetail) + sourceModule.getFromkey(),
                        0,
                        new Ref(bbItems),
                        "rightclick"
                ));
                detailGrid.setColumns(ToColumns(detailColumns, false));

                // 设置详情面板的IdField
                if (!detailColumns.isEmpty()) {
                    Map<String, Object> firstRow = detailColumns.get(0);
                    detailGrid.IdField = (firstRow.get("FieldName").toString());
                }

                // 设置详情面板的数据源
                DataStore detailStore = new DataStore();
                detailStore.extraParams = (Map.of(
                        "moduleId", moduleId,
                        "sourceId", sourceModule.getSourceId(),
                        "detail", 1
                ));
                detailGrid.setStore(detailStore);

                sourceModule.setDetails(new GridPanel[]{detailGrid});
            }

            modules.add(sourceModule);
        }
//        out.println(3);
        return modules;
    }

    public List<BillSourceModule> GetBillSourceModule(String moduleId, String id, String sourceType) {
        return GetBillSourceModule(moduleId, id, sourceType, false);
    }

    /**
     * /// <summary>
     * /// 获取查询条件
     * /// </summary>
     * /// <param name="conkey">The conkey.</param>
     * /// <param name="auditFlag"></param>
     * /// <returns>System.Object.</returns>
     */
    public Object GetCondition(String conkey, int auditFlag) {
        // 获取条件字段数据（审计标志为0时调用dataImpl，否则调用审计搜索行方法）
        List<Map<String, Object>> dtFields = (auditFlag == 0)
                ? DataImpl.GetCondition(conkey, 0, toBoolean(Request("windowsDirver", "")))
                : GetAuditSearchRows();
        if (dtFields != null && !dtFields.isEmpty()) {
            List<Component> components = new ArrayList<>();
            // 创建控件并过滤掉disabled2的字段
            List<Field> preFields = createControl.createControl(dtFields, null, toBoolean(Request("windowsDirver", "")), conkey).stream()
                    .filter(field -> !field.isDisabled2())
                    .collect(Collectors.toList());
            // 排除重复字段（根据name去重）
            List<Field> fields = new ArrayList<>();
            for (Field com : preFields) {
                boolean canAdd = true;
                for (Field alreadyF : fields) {
                    if (Objects.equals(alreadyF.getName(), com.getName())) {
                        canAdd = false;
                    }
                }
                // 只添加非隐藏且宽度大于0的字段
                if (canAdd && com.getHidden() == false && com.getWidth() != null && com.getWidth() > 0) {
                    fields.add(com);
                }
            }
//            out.println(fields + "fields");
            // 配置搜索框属性（不需要非空验证）
            for (Field com : fields) {
                com.setIssearchcontrol(true);
//                com.setAllowBlank(true);
//                2026.2.5 修改，可以设置必填
                com.setAllowBlank(com.isAllowBlank());
                // 宽度为0时不设置默认值（原代码注释逻辑）
                if (com.getWidth() == null || com.getWidth() == 0) {
//                     com.setWidth(175);
                }
                // 高度为0时设置默认21
                if (com.getHeight() == null || com.getHeight() == 0) {
                    com.setHeight(21);
                }
            }

            // 将字段添加到组件列表
            components.addAll(fields);

//            out.println(JSON.Encode(components));
            // 添加搜索按钮
            final int serBtnWidth = 70;
            Button searchButton = new Button(null);
            searchButton.setText("搜索");
            searchButton.width = serBtnWidth;
            searchButton.setHandler("OnSearch");
            searchButton.setFormBind(true);
            // 原代码中按钮位置计算逻辑（注释部分）
            // 可根据需要添加：计算top/left并设置
            // searchButton.setTop(btntop);
            // searchButton.setLeft(btnleft);
            components.add(searchButton);

            return components;
        }
        return null;
    }

    // 重载方法，处理auditFlag默认值为0的情况
    public Object GetCondition(String conkey) {
        return GetCondition(conkey, 0);
    }

    /**
     * 获取审核搜索条件的模拟数据（对应C#的DataTable）
     *
     * @return 包含搜索条件的列表，每个元素为一行数据的Map
     */
    public List<Map<String, Object>> GetAuditSearchRows() {
        // Java中没有DataTable，使用List<Map<String, Object>>模拟
        List<Map<String, Object>> resultList = new ArrayList<>();

        // 第一行数据：开始日期
        Map<String, Object> row1 = new HashMap<>();
        row1.put("id", 1);
        row1.put("fieldtype", 4);
        row1.put("fieldname", "begin");
        row1.put("fieldcaption", "开始日期");
        row1.put("dataformat", "yyyy-MM-dd");
        row1.put("wherecond", " and bv.CreateDate>='{value}'");
        row1.put("controltop", 5);
        row1.put("controlwidth", 170);
        resultList.add(row1);

        // 第二行数据：结束日期
        Map<String, Object> row2 = new HashMap<>();
        row2.put("id", 2);
        row2.put("fieldtype", 4);
        row2.put("fieldname", "end");
        row2.put("fieldcaption", "结束日期");
        row2.put("dataformat", "yyyy-MM-dd");
        row2.put("wherecond", " and bv.CreateDate<='{value}'");
        row2.put("controltop", 5);
        row2.put("controlwidth", 170);
        resultList.add(row2);

        // 第三行数据：模糊查询
        Map<String, Object> row3 = new HashMap<>();
        row3.put("id", 3);
        row3.put("fieldtype", 0);
        row3.put("fieldname", "billid");
        row3.put("fieldcaption", "模糊查询");
        row3.put("dataformat", "");
        row3.put("wherecond", " and bv.billid like '%{value}%'");
        row3.put("controltop", 5);
        row3.put("controlwidth", 170);
        resultList.add(row3);

        return resultList;
    }


    /**
     * 查询主表数据
     *
     * @param moduleId      模块ID
     * @param record        主表参数
     * @param leftRecord    父参数（parent.XX）
     * @param pams          条件参数
     * @param whereClause   查询条件
     * @param multi         是否返回多数据集
     * @param grid          是否为表格模式
     * @param contextMenuId 上下文菜单ID
     * @param attcId        附件ID
     * @return BaseResponse 响应结果
     * @remarks
     */
    public BaseResponse GetModuleData(String moduleId, String record, String leftRecord,
                                      String pams, String whereClause, boolean multi,
                                      boolean grid, int contextMenuId, int attcId) {
        BaseResponse response = new BaseResponse();
        BaseModule module = null;

        // 处理特殊模块ID "-99"
        if ("-99".equals(moduleId)) {
            module = new BaseModule();
            module.setModuleId(moduleId);
            module.setMasterTable("p_employeetab");
            module.setMasterSql("select employeeid,p_emp_no,employeename,departmentid,speciesno from p_employeetab where isnull(sign,0)=0 and UseFlag=1");
        } else {
            // 获取基础模块信息
            String menuId = Request("menuId", "");
            module = GetBaseModule(moduleId, menuId);
        }

        // 模块不存在时返回错误信息
        if (module == null) {
            response.setMsg(("获取模块数据时，未找到编号为" + moduleId + "的模块，请检查配置！"));
            return response;
        }

        // 调用重载方法处理具体逻辑
        return GetModuleData(module, record, leftRecord, pams, whereClause,
                multi, grid, contextMenuId, attcId, false);
    }

    //    2026.2.24新增
    protected Object FillFilePath(String[] picFieldNames, String name, Object v) throws UnsupportedEncodingException {
        String val = String.valueOf(v);
        if (!isNullOrEmpty(val) && val.indexOf("http") < 0 && Arrays.asList(picFieldNames).contains(name) && (toBoolean((WebConfigUtil_web.get("useDbAttc"))) && !DataImpl.isDefaultServer() || !isNullOrEmpty(getAppDomain()))) {
            String[] vals = val.split(","), newVals = new String[vals.length];
            String dbVerPath = WebConfigUtil_web.fileVPath + "_" + getUser().ServerId;
            for (int i = 0; i < vals.length; i++) {
                if (SiteUtil.containsVirtualDirectory(dbVerPath)) {
                    newVals[i] = vals[i].replace(WebConfigUtil_web.fileVPath, dbVerPath);
                } else {
                    newVals[i] = TrimEnd(getAppDomain(), '/') + vals[i];
                }
            }
            return String.join(",", newVals);
        }
        return v;
    }

    /**
     * 基于模块实体查询数据
     *
     * @param module        模块实体
     * @param recordStr     主表参数
     * @param leftRecordStr 父参数
     * @param pamsStr       条件参数
     * @param whereClause   查询条件
     * @param multi         是否多数据集
     * @param grid          是否表格模式
     * @param contextMenuId 右键菜单ID
     * @param attcId        附加ID
     * @param isTask        是否为待办
     * @return BaseResponse 响应结果
     */
    protected BaseResponse GetModuleData(BaseModule module, String recordStr, String leftRecordStr,
                                         String pamsStr, String whereClause, boolean multi, boolean grid,
                                         int contextMenuId, int attcId, boolean isTask) {
        BaseResponse response = new BaseResponse();
        Map<String, Object> record = null;
        Map<String, Object> leftRecord = null;
        List<Map<String, Object>> leftRecords = null;
        Map<String, Object> pms = null;

        String querySql = module.getMasterSql();
        // 解析主表参数
        if (recordStr != null && !recordStr.isEmpty()) {
            record = (Map<String, Object>) JSON.Decode(recordStr);
        }

        // 解析父参数（处理数组格式）
        if (leftRecordStr != null && !leftRecordStr.isEmpty()) {
            // 确保JSON数组格式
            if (!leftRecordStr.startsWith("[")) {
                leftRecordStr = "[" + leftRecordStr + "]";
            }
            leftRecords = (List<Map<String, Object>>) JSON.Decode(leftRecordStr);
            // 取第一个元素作为leftRecord
            if (leftRecords != null && !leftRecords.isEmpty()) {
                leftRecord = leftRecords.get(0);
            }
        }

        // 解析条件参数
        if (pamsStr != null && !pamsStr.isEmpty()) {
            pms = (Map<String, Object>) JSON.Decode(pamsStr);
        }

        String noPmsVal = "";

        // 处理右键菜单条件
        if (leftRecord != null && contextMenuId > 0) {
            SysPoPupMenuBtn btn = GetContextMenuBtn(contextMenuId, leftRecord);
            if (btn != null) {
                SqlAnalyzer sqlAnalyzer = new SqlAnalyzer(querySql);
                if (btn.getUnionCond() != null && !btn.getUnionCond().isEmpty()) {
                    Map<String, String> whereMap = new HashMap<>();
                    whereMap.put(btn.getUnionField(), btn.getUnionCond());
                    querySql = sqlAnalyzer.InsertWhere(whereMap, false, false, false);
                } else if ((btn.getUnionField() != null && !btn.getUnionField().isEmpty()) ||
                        (btn.getUnionValue() != null && !btn.getUnionValue().isEmpty())) {
                    if (!btn.getUnionField().equals(btn.getUnionValue())) {
                        String where = String.format(" and %s = '%s'", btn.getUnionField(), btn.getUnionValue());
                        Map<String, String> whereMap = new HashMap<>();
                        whereMap.put(btn.getUnionField(), where);
                        querySql = sqlAnalyzer.InsertWhere(whereMap, false, false, false);
                    } else {
                        noPmsVal = btn.getUnionValue().toLowerCase();
                    }
                }
            }
        }
        // 处理左侧关联条件
        else if (leftRecord != null) {
            Map<String, Object> dtVal = null;
            if ("-99".equals(module.getModuleId())) {
                dtVal = new HashMap<>();
                dtVal.put("fieldname", "speciesno");
                dtVal.put("valuemember", "speciesno");
            } else {
                // 获取基础模块左侧配置
                List<Map<String, Object>> leftRows = DataImpl.GetBaesModuleLeft(module.getModuleId());
                if (leftRows != null && !leftRows.isEmpty()) {
                    dtVal = leftRows.get(0);
                }
            }

            if (dtVal != null) {
                String fieldName = dtVal.get("fieldname").toString();
                String valueMember = dtVal.get("valuemember").toString().toLowerCase();

                // 检查SQL中是否包含父参数占位符
                if (querySql.toLowerCase().indexOf(String.format("{parent.%s}", valueMember)) == -1) {
                    // 获取字段类型
                    Class<?> fieldType = DataImpl.GetTableColumnType(module.getMasterTable(), fieldName);

                    // 构建条件匹配列表
                    List<String> matchs = new ArrayList<>();
                    for (Map<String, Object> rec : leftRecords) {
                        Object value = rec.get(valueMember);
                        if (value == null) continue;

                        if (fieldType != null && fieldType.isPrimitive()) {
                            // 数值类型：使用IN条件
                            matchs.add(value.toString());
                        } else {
                            // 字符串类型：使用LIKE或精确匹配
                            if (module.IsSpecModule) {
                                matchs.add(String.format(" %s like '%s__' or %s='%s' ",
                                        fieldName, value, fieldName, value));
                            } else {
                                String pattern = isTask ? " %s = '%s' " : " %s like '%s%%' ";
                                matchs.add(String.format(pattern, fieldName, value));
                            }
                        }
                    }

                    // 组装WHERE条件
                    if (!matchs.isEmpty()) {
                        String where;
                        if (fieldType != null && fieldType.isPrimitive()) {
                            where = String.format(" and %s in (%s)", fieldName, String.join(",", matchs));
                        } else {
                            where = String.format(" and (%s)", String.join("or", matchs));
                        }
                        // 插入条件到SQL
                        SqlAnalyzer sqlAnalyzer = new SqlAnalyzer(querySql);
                        Map<String, String> whereMap = new HashMap<>();
                        whereMap.put(fieldName, where);
                        querySql = sqlAnalyzer.InsertWhere(whereMap, false, false, false);
                    }
                }
            }
        }

        // 处理特殊模块ID "-99"
        if ("-99".equals(module.getModuleId())) {
            try {
//                2026.2.24
                String procedureName = "p_selectUser";
                List<Map<String, Object>> result = null;
                if (DataImpl.IsExitPro(procedureName)) {
                    //必须返回 employeeid,p_emp_no,employeename,departmentid,speciesno
                    String sepcVal = "";
                    if (leftRecords != null && leftRecords.size() > 0) {
                        sepcVal = (leftRecords.get(0)).get("speciesno").toString();
                    }
                    BaseResponse _response = DataImpl.excuteStore(procedureName, new Object[]{getUser().UserId, sepcVal});
                    if (_response.isSuccess() && _response.getData() != null) {
                        tot[0] = ((List<List<Map<String, Object>>>) _response.getData()).get(0).size();
                        response.setData(((((List<List<Map<String, Object>>>) _response.getData()).get(0)).stream().skip(getStartsize()).limit(getPageSize())).collect(Collectors.toList()));
                    } else {
                        response.setData(dbOperator.executeDataTable(querySql, getStartsize(), getPageSize(), tot));
                    }
                    //转发转交获取的数据
                    //response.setData(DataImpl.GetAuditRelayData(procedureName, module, querysql));
                } else {
                    // 执行查询并转换结果
                    result = dbOperator.executeDataTable(querySql, getStartsize(), getPageSize(), tot);
                }

                response.setData(toHashTable(result));
                response.setTot(tot[0] != 0 ? tot[0] : result.size());
                response.setSuccess(true);
                response.setOther(module);
            } catch (Exception e) {
                response.setSuccess(false);
                response.setMsg("查询失败：" + e.getCause().getMessage());
            }
            return response;
        }

        // 构建默认查询条件
        String defaultWhere = buildDefaultWhere(pms);
//        out.println(defaultWhere + "defaultWhere");
        // 处理搜索条件
        querySql = reqSearchCondition(module.getCondKey(), querySql, (Hashtable<String, Object>) pms, (Hashtable<String, Object>) leftRecord, false);
//        out.println("sql" + module.getCondKey());
        // 处理特殊参数替换
        if (!noPmsVal.isEmpty() && querySql.contains(noPmsVal)) {
            querySql = querySql.replace("%" + noPmsVal, noPmsVal)
                    .replace(noPmsVal + "%", noPmsVal);
        }

        // 处理查询SQL
        querySql = dealQuerySql(querySql, record != null ? record : leftRecord, leftRecord, null, null, null, false, false, false);
        querySql = HandleBmp(module, querySql);

        // 处理附件ID条件
        if (attcId > 0) {
            List<Map<String, Object>> attcModules = DataImpl.GetAttcModules("", attcId);
            if (attcModules != null && !attcModules.isEmpty()) {
                String cond = attcModules.get(0).get("unionCond").toString();
                if (cond != null && !cond.isEmpty()) {
                    cond = createControl.GetDefaultValue(cond, null, SystemTypeEnums.PmType.sql);
                    SqlAnalyzer sqlAnalyzer = new SqlAnalyzer(querySql);
                    Map<String, String> whereMap = new HashMap<>();
                    whereMap.put("$where", cond);
                    querySql = sqlAnalyzer.InsertWhere(whereMap, true, false, false);
                }
            }
        }

        // 处理外部传入的WHERE条件
        if (whereClause != null && !whereClause.isEmpty()) {
            SqlAnalyzer sqlAnalyzer = new SqlAnalyzer(querySql);
            Map<String, String> whereMap = new HashMap<>();
            whereMap.put("$where", whereClause);
            querySql = sqlAnalyzer.InsertWhere(whereMap, true, false, false);
        }

        // 处理默认WHERE条件
        if (defaultWhere != null && !defaultWhere.isEmpty()) {
            SqlAnalyzer sqlAnalyzer = new SqlAnalyzer(querySql);
            Map<String, String> whereMap = new HashMap<>();
            whereMap.put("$where", defaultWhere);
            querySql = sqlAnalyzer.InsertWhere(whereMap, true, false, false);
        }

        // 处理颜色和样式列
        Map<String, Object> formats = null; // 可通过GetFormatColumns方法获取，此处保留null
        Map<String, String> fontColors = null;
        List<Map<String, Object>> styles = null; // Java中用List<Map>模拟DataTable
        List<Map<String, Object>> colorAndboxCols = DataImpl.GetColorAndBoxColumns(module.getModuleId());
        // 处理颜色和下拉框列数据
        if (colorAndboxCols != null && !colorAndboxCols.isEmpty()) {
            // 过滤并转换树类型字段（对应C#的Linq查询）
            Optional<Map<String, Object>> treeRow = colorAndboxCols.stream()
                    .filter(row -> {
                        int fieldType = DataTableUtil.getIntValue(row, "fieldtype", 0);
                        return fieldType == SystemEnums.ControlType.LabTreeType.ordinal();
                    })
                    .findFirst();
            treeRow.ifPresent(row -> row.put("fieldtype", SystemEnums.ControlType.LabComboxValue.ordinal()));
        }
        // 构建下拉框列表（对应C#的ComboBox集合）
        List<ComboBox> boxs = null;
        if (colorAndboxCols != null) {
            boxs = colorAndboxCols.stream()
                    .filter(row -> {
                        String fieldsql = DataTableUtil.getStringValue(row, "fieldsql", "");
                        return !fieldsql.isEmpty();
                    })
                    .map(row -> {
                        // 调用工具方法创建控件
                        Object control = createControl.createControl(row, module, false);
                        // 检查是否为ComboBox类型
                        if (control instanceof ComboBox) {
                            return (ComboBox) control;
                        } else {
                            // 处理类型不匹配的情况：可以返回null或记录日志
                            //                        System.err.println("创建的控件不是ComboBox类型: " + control);
                            return null;
                        }
                    }) // 调用工具类创建控件
                    .filter(box -> box != null && !box.getValueField().equals(box.getDisplayField()))
                    .collect(Collectors.toList());
        }

        // 处理表格模式下的字体颜色和样式
        if (grid) {
            // 构建字体颜色映射（fieldname -> fontcolor）
            fontColors = colorAndboxCols.stream()
                    .filter(row -> {
                        String fontColor = DataTableUtil.getStringValue(row, "fontcolor", "");
                        return !fontColor.isEmpty();
                    })
                    .collect(Collectors.toMap(
                            row -> DataTableUtil.getStringValue(row, "fieldname", "").toLowerCase(),
                            row -> DataTableUtil.getStringValue(row, "fontcolor", "")
                    ));

            // 获取样式数据（模拟C#的GetStyles方法）
            styles = GetStyles(module.getModuleId());
        }
        Function<Map<String, Object>, Map<String, Object>> fillStyles = null;
        Function<Map<String, Object>, Map<String, Object>> fillColor = null;
//        2026.2.24
        String[] picFields = colorAndboxCols.stream()
                // 筛选：fieldtype 等于 LabPic 或 LabPicEx
                .filter(row -> {
                    int fieldType = row.get("fieldtype") == null ? -1 : (int) row.get("fieldtype");
                    return fieldType == SystemEnums.ControlType.LabPic.getValue() || fieldType == SystemEnums.ControlType.LabPicEx.getValue();
                })
                // 提取 fieldname 并转小写（空值兜底为""）
                .map(row -> {
                    String fieldName = row.get("fieldname") == null ? "" : row.get("fieldname").toString();
                    return fieldName.toLowerCase();
                })
                // 转为字符串数组
                .toArray(String[]::new);

        if (styles != null) {
            List<Map<String, Object>> finalStyles1 = styles;
            fillStyles = row -> FillStyles(row, finalStyles1);
        }
        if (fontColors != null) {
            Map<String, String> finalfontColors = fontColors;
            fillColor = row -> FillFontColor(row, finalfontColors);
        }

        try {
            if (multi) {
                // 多数据集处理（模拟DataSet）
                List<List<Map<String, Object>>> dataSet = executeDataSet(querySql);
                List<Object> tbs = new ArrayList<>();
                for (List<Map<String, Object>> table : dataSet) {
                    // 转换为Hashtable列表（应用格式、颜色、样式）
                    List<Map<String, Object>> hashList = toHashTable(table, formats, fillColor, fillStyles, true, true, false, (r, name, v) -> {
                        try {
                            return FillFilePath(picFields, name, v);
                        } catch (UnsupportedEncodingException e) {
                            throw new RuntimeException(e);
                        }
                    });
                    tbs.add(hashList);
                }
                response.setData(tbs);
                response.setTot(tbs.size());

            } else {
                // 单表数据处理
//                out.println("executeDataTable:querySql:" + getStartsize() + getPageSize());
                List<Map<String, Object>> dtVal = dbOperator.executeDataTable(querySql, getStartsize(), getPageSize(), tot);
//                    out.println("遍历键值对：");
//                    for (Map.Entry<String, Object> entry : dtVal.get(0).entrySet()) {
//                        String key = entry.getKey();
//                        Object value = entry.getValue();
//                        out.println("键：" + key + "，值：" + value);
//                    }
//                    out.println("结束遍历键值对");
                // 设置模块主数据列信息
                if (dtVal != null) {
                    Map<String, Class<?>> columnTypes = new HashMap<>();
                    if (dtVal.isEmpty()) {
                        columnTypes = getColumnTypes(dtVal, querySql);
                        module.MainDataColumns = (columnTypes.entrySet().stream()
                                .map(entry -> {
                                    String columnName = entry.getKey();
                                    Class<?> dataType = entry.getValue();
                                    // 转换为前端组件类型（即使字段值为null，仍按数据库定义的类型处理）
                                    String xtype = PublicUtil.TypeToColumnType(dataType);
                                    // 构建列信息对象（可替换为实际的DTO类）
                                    Map<String, String> columnInfo = new HashMap<>();
                                    columnInfo.put("dataIndex", columnName);
                                    columnInfo.put("xtype", xtype);
                                    return columnInfo;
                                })
                                .collect(Collectors.toList()));
                    } else {
                        List<Map<String, Object>> mainDataColumns = dtVal.get(0).keySet().stream()
                                .map(colName -> {
                                    Map<String, Object> colInfo = new HashMap<>();
                                    colInfo.put("dataIndex", colName);
                                    // 获取列的值
                                    Object value = dtVal.get(0).get(colName);
                                    // 根据值的类型推断Class（注意处理null值）
                                    Class<?> valueType = value != null ? value.getClass() : Object.class;
                                    log.debug(String.valueOf(valueType + " valueType " + value));
                                    // 传递正确的Class对象给工具方法
                                    colInfo.put("xtype", PublicUtil.TypeToColumnType(valueType));
                                    return colInfo;
                                })
                                .collect(Collectors.toList());
                        module.MainDataColumns = mainDataColumns;
                    }
                }

                // 转换为Hashtable并应用样式
                List<Map<String, Object>> tbs = toHashTable(dtVal, formats, fillColor, fillStyles, true, true, false, (r, name, v) -> {
                    try {
                        return FillFilePath(picFields, name, v);
                    } catch (UnsupportedEncodingException e) {
                        throw new RuntimeException(e);
                    }
                });
                // 转换下拉框值（对应C#的ConvertBoxVal）
                response.setData(convertBoxVal(tbs, boxs, module.getModuleId()));
                if (dtVal != null) {
                    response.setTot(tot[0] == 0 ? dtVal.size() : tot[0]);
                }
            }

        } catch (Exception e) {
            response.setSuccess(false);
            response.setMsg("查询失败：" + e.getMessage());
        }
        response.setSuccess(true);
        response.setOther(module);
        return response;
    }

    // 关键辅助方法：获取字段真实数据库类型（解决值为null时类型丢失问题）
    private Map<String, Class<?>> getColumnTypes(List<Map<String, Object>> resultList, String sql) {
        Map<String, Class<?>> columnTypes = new HashMap<>();

        // 针对SQL Server修改：使用TOP 0获取元数据，不返回实际数据
        String metaSql = RegexUtil.processDmServerSql(sql);
//        System.out.println("sql，开始处理元数据" + metaSql);
        try {
            jdbcTemplate.query(metaSql, new ResultSetExtractor<Void>() {
                @Override
                public Void extractData(ResultSet rs) throws SQLException {
//                    System.out.println("进入ResultSetExtractor，开始处理元数据");

                    ResultSetMetaData metaData = rs.getMetaData();
                    int columnCount = metaData.getColumnCount();

//                    System.out.println("发现 " + columnCount + " 个列");

                    for (int i = 1; i <= columnCount; i++) {
                        String columnName = metaData.getColumnName(i);
                        String columnLabel = metaData.getColumnLabel(i);
                        int sqlType = metaData.getColumnType(i);
                        String sqlTypeName = metaData.getColumnTypeName(i);

                        // 使用System.out确保输出可见
//                        System.out.println("列名: " + columnName +
//                                ", 别名: " + columnLabel +
//                                ", SQL类型: " + sqlType +
//                                " (" + sqlTypeName + ")");

                        Class<?> javaType = getJavaTypeBySqlType(sqlType);
                        columnTypes.put(columnName, javaType);
                    }
                    return null;
                }
            });
        } catch (Exception e) {
            // 捕获并打印所有异常
            log.warn(String.valueOf("获取元数据时发生错误: " + e.getMessage()));
            log.error("Exception caught", e);
        }

//        System.out.println("最终获取到的列类型数量: " + columnTypes.size());
        return columnTypes;
    }

    // 映射JDBC类型到Java类型（确保时间类型被正确识别）
    private Class<?> getJavaTypeBySqlType(int sqlType) {
        return switch (sqlType) {
            case Types.DATE, Types.TIME, Types.TIMESTAMP, Types.TIMESTAMP_WITH_TIMEZONE ->
                    LocalDateTime.class; // 统一使用LocalDateTime处理时间类型
            case Types.INTEGER -> Integer.class;
            case Types.BIGINT -> Long.class;
            case Types.VARCHAR, Types.CHAR -> String.class;
            case Types.NUMERIC, Types.DECIMAL -> BigDecimal.class;
            case Types.BOOLEAN -> Boolean.class;
            default -> Object.class;
        };
    }

    /**
     * 获取右键菜单按钮（重载方法）
     *
     * @param menuId 菜单ID
     * @param record 记录参数
     * @return 右键菜单按钮对象
     */
    public SysPoPupMenuBtn GetContextMenuBtn(int menuId, Map<String, Object> record) {
        return GetContextMenuBtn(menuId, record, null);
    }

    /**
     * 获取右键菜单按钮
     *
     * @param menuId     菜单ID
     * @param record     记录参数
     * @param leftRecord 左侧记录参数
     * @return 右键菜单按钮对象
     */
    public SysPoPupMenuBtn GetContextMenuBtn(int menuId, Map<
            String, Object> record, Map<String, Object> leftRecord) {
        SysPoPupMenuBtn btn = null;
        List<Map<String, Object>> menuRows = GetRightMenuRows("", 0, menuId);

        if (menuRows != null && !menuRows.isEmpty()) {
            // 转换第一条记录为SysPoPupMenuBtn对象
            Optional<SysPoPupMenuBtn> btnOptional = menuRows.stream()
                    .map(row -> {
                        SysPoPupMenuBtn menuBtn = new SysPoPupMenuBtn();
                        menuBtn.MenuBtnId = (menuId);
                        menuBtn.dllname = (row.get("library") != null ? row.get("library").toString() : "");
                        menuBtn.setText(row.get("menucaption") != null ? row.get("menucaption").toString() : "");
                        menuBtn.action = (row.get("action") != null ? row.get("action").toString() : "");
                        menuBtn.actiontype = (row.get("actiontype") != null ? Integer.parseInt(row.get("actiontype").toString()) : 0);
                        menuBtn.dllpar1 = (row.get("param1") != null ? row.get("param1").toString() : "");
                        menuBtn.dllpar2 = (row.get("param2") != null ? row.get("param2").toString() : "");
                        menuBtn.dllpar3 = (row.get("param3") != null ? row.get("param3").toString() : "");
                        menuBtn.dllpar4 = (row.get("param4") != null ? row.get("param4").toString() : "");
                        menuBtn.dllpar5 = (row.get("param5") != null ? row.get("param5").toString() : "");
                        menuBtn.dllpar6 = (row.get("param6") != null ? row.get("param6").toString() : "");
                        menuBtn.dllpar7 = (row.get("param7") != null ? row.get("param7").toString() : "");
                        menuBtn.dllpar8 = (row.get("param8") != null ? row.get("param8").toString() : "");
                        menuBtn.dllpar9 = (row.get("param9") != null ? row.get("param9").toString() : "");
                        menuBtn.dllpar10 = (row.get("param10") != null ? row.get("param10").toString() : "");
                        menuBtn.comfirm = ("{comfirm}");
                        menuBtn.selectConfirmFlag = ("{selectconfirmflag}");
                        menuBtn.nextSelectStepCode = ("{nextselectstepcode}");
                        menuBtn.nextSelectStepOper = ("{nextselectstepoper}");
                        menuBtn.remark = ("{remark}");
                        menuBtn.comfirmOpers = ("{comfirmopers}");
                        menuBtn.maxWindow = (row.get("maxwindow") != null && Boolean.parseBoolean(row.get("maxwindow").toString()));
                        menuBtn.showMode = (row.get("showMode") != null ? Integer.parseInt(row.get("showMode").toString()) : 0);
                        return menuBtn;
                    })
                    .findFirst();

            btn = btnOptional.get();
        }

        return DealContextMenuBtn(btn, record, leftRecord);
    }

    /**
     * 处理右键菜单按钮参数
     *
     * @param btn        右键菜单按钮对象
     * @param record     记录参数
     * @param leftRecord 左侧记录参数
     * @return 处理后的右键菜单按钮对象
     */
    private SysPoPupMenuBtn DealContextMenuBtn(SysPoPupMenuBtn
                                                       btn, Map<String, Object> record, Map<String, Object> leftRecord) {
        if (btn != null) {
            SystemTypeEnums.PmType pmType = SystemTypeEnums.PmType.sql;

            // 根据动作类型确定参数类型
            switch (btn.actiontype) {
                case 0:
                    pmType = SystemTypeEnums.PmType.sql;
                    break;
                case 1:
                    pmType = SystemTypeEnums.PmType.store;
                    break;
                case 2:
                case 3:
                case 4:
                    pmType = SystemTypeEnums.PmType.program;
                    break;
            }

            // 处理动态参数
            btn.dllname = createControl.GetDefaultValue(Objects.toString(btn.dllname, ""), record, leftRecord, SystemTypeEnums.PmType.store);

            // 处理动作参数
//            btn.action = (pmType == SystemTypeEnums.PmType.store)
//                    ? btn.action
//                    : createControl.GetDefaultValue(btn.action, record, leftRecord, pmType.equals(SystemTypeEnums.PmType.store) ? SystemTypeEnums.PmType.ignorenull : pmType);
            btn.action = (pmType == SystemTypeEnums.PmType.store)
                    ? btn.action
                    : createControl.GetDefaultValue(
                    btn.action,
                    record,
                    leftRecord,
                    pmType  // 直接使用pmType，因为此时pmType一定不是store
            );
            // 处理其他参数
            btn.dllpar1 = (createControl.GetDefaultValue(btn.dllpar1, record, leftRecord, pmType));
            btn.dllpar2 = (createControl.GetDefaultValue(btn.dllpar2, record, leftRecord, pmType));
            btn.dllpar3 = createControl.GetDefaultValue(btn.dllpar3, record, leftRecord, pmType);
            btn.dllpar4 = createControl.GetDefaultValue(btn.dllpar4, record, leftRecord, pmType);
            btn.dllpar5 = createControl.GetDefaultValue(btn.dllpar5, record, leftRecord, pmType);
            btn.dllpar6 = createControl.GetDefaultValue(btn.dllpar6, record, leftRecord, pmType);
            btn.dllpar7 = createControl.GetDefaultValue(btn.dllpar7, record, leftRecord, pmType);
            btn.dllpar8 = createControl.GetDefaultValue(btn.dllpar8, record, leftRecord, pmType);
            btn.dllpar9 = createControl.GetDefaultValue(btn.dllpar9, record, leftRecord, pmType);
            btn.dllpar10 = createControl.GetDefaultValue(btn.dllpar10, record, leftRecord, pmType);

            btn.comfirm = (createControl.GetDefaultValue(btn.comfirm, record, leftRecord, pmType));
            btn.selectConfirmFlag = createControl.GetDefaultValue(btn.selectConfirmFlag, record, leftRecord, pmType);
            btn.nextSelectStepCode = createControl.GetDefaultValue(btn.nextSelectStepCode, record, leftRecord, pmType);
            btn.nextSelectStepOper = createControl.GetDefaultValue(btn.nextSelectStepOper, record, leftRecord, pmType);
            btn.comfirmOpers = createControl.GetDefaultValue(btn.comfirmOpers, record, leftRecord, pmType);
            btn.remark = createControl.GetDefaultValue(btn.remark, record, leftRecord, pmType);

            // 处理参数字符串
            if (btn.getPmStr() != null && !btn.getPmStr().isEmpty()) {
                String pmSql = PublicUtil.ReqSqlPms(
                        record,
                        leftRecord,
                        Trim(btn.getPmStr()).replaceAll("^#", ""),
                        SystemTypeEnums.PmType.sql,
                        getUser()
                );

                try {
                    List<Map<String, Object>> result = jdbcTemplate.queryForList(pmSql);
                    if (result != null && !result.isEmpty()) {
                        btn.Pms = toHashTable(result.get(0));
                        btn.dllpar6 = "";
                    }
                } catch (Exception e) {
                    // 忽略异常
                }
            }
        }

        return btn;
    }

    /**
     * 构建查询匹配字符串
     */
    public String buildDefaultWhere(Map<String, Object> pms) {
        String defaultWhere = "";
        // 检查参数是否符合条件：非空、包含指定键且数量为4
        if (pms != null && pms.size() == 4
                && pms.containsKey("_colpm")
                && pms.containsKey("_colpmoper")
                && pms.containsKey("_colpmval")
                && pms.containsKey("_fid")) {

            // 提取参数值并转为字符串
            String name = pms.get("_colpm").toString();
            String oper = pms.get("_colpmoper").toString();
            String val = pms.get("_colpmval").toString();
            int fid = ToInt32(pms.get("_fid").toString());

            // 清空pms引用（与原逻辑保持一致）
            pms = null;

            // 检查操作符是否合法
            List<String> validOpers = Arrays.asList(">", "<", "=", ">=", "<=", "<>", "in", "like");
            if (!name.isEmpty() && !val.isEmpty() && validOpers.contains(oper)) {
                // SQL注入防护：过滤危险关键字
                Pattern sqlInjectPattern = Pattern.compile(
                        "select|insert|delete|from|count\\(|drop table|update|truncate|asc\\(|mid\\(|char\\(|xp_cmdshell|exec master|netlocalgroup administrators|:|net user|\"|or|and",
                        Pattern.CASE_INSENSITIVE
                );
                val = sqlInjectPattern.matcher(val).replaceAll("").replace("'", "");

                if (fid > 0) {
                    // 处理字段ID存在的情况
                    List<Map<String, Object>> fieldTab = GetColumnRows(null, fid);
                    if (fieldTab != null && !fieldTab.isEmpty()) {
                        Map<String, Object> fieldRow = fieldTab.get(0);
                        ComboBox field = new ComboBox(fieldRow, createControl);
                        String querySql = field.getDataSource();
                        // 构建子查询作为值
                        val = String.format(
                                "(select %s from (%s) a)",
                                field.getValueField(),
                                dealQuerySql(querySql, "", "", "", field.getDisplayField(), val, !"like".equals(oper), false, false)
                        );
                        oper = "in";
                    }
                } else {
                    // 处理不同操作符的值格式化
                    switch (oper) {
                        case "in":
                            val = String.format("('%s')", val.replace(",", "','"));
                            break;
                        case "like":
                            // 若值中不含%，自动添加前后%
                            val = val.contains("%") ? val : String.format("'%s'", "%" + val + "%");
                            break;
                        default:
                            val = String.format("'%s'", val);
                            break;
                    }
                }

                // 构建最终的where条件
                defaultWhere = String.format("and %s %s %s", name, oper, val);
            }
        }
        return defaultWhere;
    }


    public String DealQuerySql(String querySql, String _record, String _leftRecord, String _pams, String
            keyField, String keyValue, boolean readOnly, boolean convertStr, boolean doNotSpelling) {
        Map<String, Object> record = null, leftRecord = null, pms = null;
//        IPublicUtil util = new IPublicUtil();
        if (!isNullOrEmpty(_record)) {
            record = (Map<String, Object>) JSON.Decode(_record);
        }
        if (!isNullOrEmpty(_leftRecord)) {
            leftRecord = (Map<String, Object>) JSON.Decode(_leftRecord);
        }
        if (!isNullOrEmpty(_pams)) {
            pms = (Map<String, Object>) JSON.Decode(_pams);
        }
        return createControl.dealQuerySql(querySql, record, leftRecord, pms, keyField, keyValue, false, readOnly, convertStr, doNotSpelling);
    }

    //    重载DealQuerySql
    public String DealQuerySql(String querySql, String _record, String _leftRecord, String _pams, String
            keyField, String keyValue) {
        boolean readOnly = false;
        boolean convertStr = false;
        boolean doNotSpelling = false;
        return DealQuerySql(querySql, _record, _leftRecord, _pams, keyField, keyValue, readOnly, convertStr, doNotSpelling);
    }

    public String DealQuerySql(String
                                       querySql, Map<String, Object> record, Map<String, Object> leftRecord, Map<String, Object> pams, String
                                       keyField, String keyValue, boolean precise, boolean readOnly, boolean convertStr) {
//        IPublicUtil util = new IPublicUtil();
        return createControl.dealQuerySql(querySql, record, leftRecord, pams, keyField, keyValue, precise, readOnly, convertStr);
    }


    /**
     * 处理图片，在基础档案查询的时候，将二进制的图片字段去掉，并将*去掉
     *
     * @param module 模块实体
     * @param sql    SQL语句
     * @return 处理后的SQL语句
     */
    public String HandleBmp(BaseModule module, String sql) {
        String notBmpSql = sql.toLowerCase();
        // 获取图片字段列表（使用List<Map<String, Object>>替代DataTable）
        List<Map<String, Object>> bmpFieldList = DataImpl.GetBaesModuleBmpFields(module.getModuleId(), module.getMasterTable());
        String[] bmpFields = bmpFieldList.stream()
                .map(row -> row.get("fieldname").toString().toLowerCase())
                .toArray(String[]::new);

        SqlAnalyzer analyzer = new SqlAnalyzer(sql);
        analyzer.DoAnalyzeSelPms(); // 解析查询参数

        if (bmpFields.length > 0) {
            // 处理查询字段包含*的情况
            if (analyzer.QueryPart.indexOf("*") > -1) {
                List<String> hasNames = new ArrayList<>();
                // 获取表信息（使用List<Map<String, Object>>替代DataTable）
                List<Map<String, Object>> tableInfoList = DataImpl.GetTableInfo(module.getMasterTable());
                String[] tableInfos = tableInfoList.stream()
                        .map(row -> row.get("name").toString().toLowerCase())
                        .toArray(String[]::new);

                // 获取列名信息（使用List<Map<String, Object>>替代DataTable）
                List<Map<String, Object>> columnNameList = DataImpl.GetColumnNames(module.getModuleId());
                String[] dtColumns = columnNameList.stream()
                        .map(row -> row.get("fieldname").toString().toLowerCase())
                        .toArray(String[]::new);

                // 筛选符合条件的字段（排除图片字段和内部参数）
                for (String name : tableInfos) {
                    boolean isInDtColumns = Arrays.asList(dtColumns).contains(name);
                    boolean isInInnerPms = analyzer.InnerPms.contains(name);
                    boolean isInAliasPms = analyzer.AliasPms.contains(name);
                    boolean isInBmpFields = Arrays.asList(bmpFields).contains(name);

                    if (isInDtColumns && !isInInnerPms && !isInAliasPms && !isInBmpFields) {
                        hasNames.add(name);
                    }
                }

                // 替换*为筛选后的字段列表
                if (!hasNames.isEmpty()) {
                    String queryPart = analyzer.QueryPart.replace("*", String.join(",", hasNames));
                    notBmpSql = notBmpSql.replace(analyzer.QueryPart, queryPart);
                }
            }
            // 处理明确指定字段的情况（移除图片字段）
            else {
                for (String bmpName : bmpFields) {
                    if (analyzer.InnerPms.contains(bmpName)) {
                        // 标准化SQL格式（处理换行和空格）
                        notBmpSql = notBmpSql.replaceAll("\r\n", " ");
                        notBmpSql = notBmpSql.replaceAll(",\\s+", ",");
                        notBmpSql = notBmpSql.replaceAll("\\s+,", ",");

                        // 移除出现在不同位置的图片字段
                        // 1. 出现在第一个位置
                        notBmpSql = notBmpSql.replaceAll(
                                String.format("select\\s+%s\\s*,", bmpName),
                                "select "
                        );
                        // 2. 出现在中间位置
                        notBmpSql = notBmpSql.replaceAll(
                                String.format(",\\s+%s\\s*,", bmpName),
                                ","
                        );
                        // 3. 出现在末尾位置（紧邻from前）
                        notBmpSql = notBmpSql.replaceAll(
                                String.format(",\\s+%s\\s+from", bmpName),
                                " from"
                        );
                    }
                }
            }
        }
        return notBmpSql;
    }

    /**
     * 执行sql语句返回 C#中的DataSe
     */
    private List<List<Map<String, Object>>> executeDataSet(String querySql) {
        List<List<Map<String, Object>>> dataSets = new ArrayList<>();
        // 提升变量作用域到方法内，解决无法访问问题
        final List<Map<String, Object>>[] currentDataSet = new List[]{null};
        final ResultSet[] currentResultSet = {null};
        final ResultSetMetaData[] currentMetaData = {null};
        final int[] columnCount = {0};

        jdbcTemplate.query(querySql, new RowCallbackHandler() {
            @Override
            public void processRow(ResultSet rs) throws SQLException {
                // 使用外部方法的变量（需声明为final或有效final）
                ResultSet nonFinalCurrentResultSet = rs;
                // 首次处理或切换到新结果集时初始化
                if (currentResultSet[0] == null || !rs.equals(currentResultSet[0])) {
                    // 保存上一个结果集（如果存在）
                    if (currentDataSet[0] != null) {
                        dataSets.add(currentDataSet[0]);
                    }
                    // 初始化新结果集
                    currentResultSet[0] = rs;
                    currentDataSet[0] = new ArrayList<>();
                    currentMetaData[0] = rs.getMetaData();
                    columnCount[0] = currentMetaData[0].getColumnCount();
                }

                // 处理当前行数据
                Map<String, Object> rowMap = new HashMap<>();
                for (int i = 1; i <= columnCount[0]; i++) {
                    String columnName = currentMetaData[0].getColumnName(i);
                    // 处理重复列名
                    if (rowMap.containsKey(columnName)) {
                        columnName = columnName + "_" + i;
                    }
                    rowMap.put(columnName, rs.getObject(i));
                }
                currentDataSet[0].add(rowMap);
            }
        });

        // 添加最后一个结果集（此时currentDataSet在方法作用域内可访问）
        if (currentDataSet[0] != null && !currentDataSet[0].isEmpty()) {
            dataSets.add(currentDataSet[0]);
        }

        return dataSets;
    }

    /**
     * 根据行数据处理字段颜色
     *
     * @param row    数据行
     * @param colors 颜色映射表
     * @return 处理后的颜色哈希表
     */
    protected Hashtable<String, Object> FillFontColor(Map<String, Object> row, Map<String, String> colors) {
        if (colors == null || colors.isEmpty()) {
            return null;
        }
        Hashtable<String, Object> colorTab = new Hashtable<>();
        for (Map.Entry<String, String> entry : colors.entrySet()) {
            String key = entry.getKey();
            String colorValue = entry.getValue();
            // 调用CreateControl工具类的getDefaultValue方法处理颜色值
            String processedValue = createControl.GetDefaultValue(colorValue, row, null, SystemTypeEnums.PmType.sql);
            colorTab.put(key, processedValue);
        }
        return colorTab;
    }

    /**
     * 转换下拉框数据
     *
     * @param datas    数据列表
     * @param boxs     下拉框控件列表
     * @param moduleId 模块ID
     * @return 转换后的哈希表列表
     */
    protected List<Map<String, Object>> convertBoxVal(List<Map<String, Object>> datas,
                                                      List<ComboBox> boxs,
                                                      String moduleId) {
        // 边界条件判断
        if (boxs == null || boxs.isEmpty() || datas == null || datas.isEmpty()) {
            return datas;
        }

        // 存储所有下拉框的键值对映射（控件名称 -> (值 -> 显示文本)）
        Map<String, Map<String, String>> allDict = new HashMap<>();

        for (ComboBox box : boxs) {
            String boxName = box.getName();
            // 提取所有非空的字段值（处理多选情况）
            List<Object> vals = datas.stream()
                    .filter(data -> data.containsKey(boxName))
                    .map(data -> {
                        Object value = data.get(boxName);
                        String strVal = value != null ? value.toString() : "";
                        // 多选情况下替换逗号为%2C
                        return box.multiSelect ? strVal : strVal.replace(",", "%2C");
                    })
                    .filter(str -> !str.isEmpty())
                    .distinct() // 去重，替代RemoveSameObj()
                    .collect(Collectors.toList());

            if (vals.isEmpty()) {
                continue;
            }

            // 获取下拉框数据并构建键值对映射
            Map<String, String> kvs = new HashMap<>();
            kvs = getBoxDatas(kvs, datas, box, vals, moduleId);
            allDict.put(boxName, kvs);
        }

        // 处理每条数据，添加显示文本字段
        for (Map<String, Object> data : datas) {
            for (Map.Entry<String, Map<String, String>> entry : allDict.entrySet()) {
                String boxName = entry.getKey();
                Map<String, String> valueMap = entry.getValue();

                if (!data.containsKey(boxName)) {
                    continue;
                }

                // 获取原始值并处理显示字段名称
                Object valueObj = data.get(boxName);
                String idVal = valueObj != null ? valueObj.toString() : "";
                String displayName = String.format("@%s_%s", boxName, idVal.replace(",", "_"));

                // 处理多值情况（含逗号分隔）
                if (idVal.contains(",")) {
                    StringBuilder displayVals = new StringBuilder();
                    for (String val : idVal.split(",")) {
                        if (valueMap.containsKey(val)) {
                            displayVals.append(valueMap.get(val)).append(",");
                        }
                    }
                    // 移除末尾逗号
                    if (displayVals.length() > 0) {
                        displayVals.setLength(displayVals.length() - 1);
                    }
                    data.put(displayName, displayVals.toString());
                }
                // 单值情况
                else if (valueMap.containsKey(idVal)) {
                    data.put(displayName, valueMap.get(idVal));
                } else {
                    data.put(displayName, "");
                }
            }
        }

        return datas;
    }


    /**
     * 获取下拉框数据并填充键值对映射（处理大数据量分批查询）
     *
     * @param kvs      用于存储键值对的映射（引用传递）
     * @param datas    原始数据列表
     * @param box      下拉框控件
     * @param vals     需要查询的值列表
     * @param moduleId 模块ID
     */
    private Map<String, String> getBoxDatas(Map<String, String> kvs,
                                            List<Map<String, Object>> datas,
                                            ComboBox box,
                                            List<Object> vals,
                                            String moduleId) {
        String sql = box.getDataSource();
        int maxSize = 1000;

        // 处理大数据量，分批查询
        if (vals.size() > maxSize) {
            while (vals.size() > maxSize) {
                List<Object> firstList = vals.subList(0, maxSize);
                vals = vals.subList(maxSize, vals.size());
                kvs = getBoxDatas(kvs, datas, box, firstList, moduleId);
            }
        }

        // 处理当前批次的值
        String strVals = String.join(",", vals.stream()
                .map(val -> val != null ? val.toString() : "")
                .collect(Collectors.toList()));

        boolean prise = sql.indexOf("P_BaseMixInfoTab") < 0;
        sql = dealQuerySql(sql, datas.get(0), null, null, box.getValueField(), strVals, prise, prise, false);
        sql = sql.replace("{loginid}", DbOperator.escapeSqlParam(getUser().UserId));
        try {
            // 执行查询获取下拉框数据源
            List<Map<String, Object>> dtVal = jdbcTemplate.queryForList(sql);
            for (Map<String, Object> row : dtVal) {
                String key = DataTableUtil.getStringValue(row, box.getValueField(), "");
                String val = DataTableUtil.getStringValue(row, box.getDisplayField(), "");
                if (!kvs.containsKey(key) && !key.isEmpty()) {
                    kvs.put(key, val);
                }
            }
        } catch (Exception e) {
            String errMsg = e.getMessage() != null ? e.getMessage() : "";
            // 处理数字转换错误的情况
            if (errMsg.contains("转换成数据类型 int 时失败")) {
                // 清洗非数字字符
                strVals = strVals.replaceAll("[^\\d\\.\\-\\'\\,]", "0");
                sql = dealQuerySql(sql, datas.get(0), null, null, box.getValueField(), strVals, true, true, false);

                try {
                    // 记录错误日志并重新尝试查询
                    log.debug(String.valueOf(String.format("加载数据时解析字典数据出错%s=>%s:%s,已尝试处理并重新解析！",
                            moduleId, box.getName(), sql)));

                    List<Map<String, Object>> dtVal = jdbcTemplate.queryForList(sql);
                    for (Map<String, Object> row : dtVal) {
                        String key = DataTableUtil.getStringValue(row, box.getValueField(), "");
                        String val = DataTableUtil.getStringValue(row, box.getDisplayField(), "");
                        if (!key.isEmpty() && !kvs.containsKey(key)) {
                            kvs.put(key, val);
                        }
                    }
                } catch (Exception ex) {
                    log.debug(String.valueOf(String.format("再次尝试加载数据时解析字典数据出错%s=>%s:%s,请检查",
                            moduleId, box.getName(), sql)));
                }
            } else {
                log.debug(String.valueOf(String.format("加载数据时解析字典数据出错%s=>%s:%s,请检查",
                        moduleId, box.getName(), sql)));
            }
        }
        return kvs;
    }

    /**
     * 获取模块详情数据
     * 校验参数：ModuleId, detailId
     */
    public BaseResponse GetModuleDetailData() {
        // 获取请求参数
        String detailIdStr = Request("detailId", ""), pms = Request("qrpms", Request("pms"));
        int detailId = Integer.parseInt(detailIdStr);
        String leftRecord = Request("leftRecord", pms);

        // 处理pms参数，拼接查询条件
        if (pms != null && !pms.trim().replaceAll("[{}]", "").trim().isEmpty()) {
            leftRecord = leftRecord != null ? leftRecord.trim() : "";
            String trimmedLeftRecord = leftRecord.replaceAll("[{}]", "").trim();

            if (trimmedLeftRecord.isEmpty()) {
                leftRecord = "{\"$isPms\":1}";
            } else {
                // 确保JSON格式正确
                if (leftRecord.endsWith("}")) {
                    leftRecord = leftRecord.substring(0, leftRecord.length() - 1) + ",\"$isPms\":1}";
                } else {
                    leftRecord += ",\"$isPms\":1}";
                }
            }
        }


        // 解析布尔参数
        boolean isAuditAttc = Boolean.parseBoolean(Request("isAttc", ""));
        boolean multi = Boolean.parseBoolean(Request("multi", ""));
        boolean grid = Boolean.parseBoolean(Request("grid", ""));
        String moduleId = Request("moduleId", "");
        // 调用模块实现类获取详情数据
        BaseResponse response = GetModuleDetailData(moduleId, detailId, leftRecord, multi, grid, isAuditAttc, pms);

        return response;
    }

    /**
     * 获取模块详情数据
     *
     * @param moduleId    模块标识符
     * @param detailId    明细标识符
     * @param idOrPRow    基础模块标识符或父行数据（格式如{a:b}）
     * @param multi       是否多数据集
     * @param grid        是否表格模式
     * @param isAuditAttc 是否为审核附件模式
     * @return BaseResponse 响应结果
     */
    public BaseResponse GetModuleDetailData(String moduleId, int detailId, String idOrPRow,
                                            boolean multi, boolean grid, boolean isAuditAttc, String pms) {
        // 获取基础模块配置
        BaseModule module = GetBaseModule(moduleId, getMenuId());
        // 获取明细模块配置
        BaseDetailModule dModule = GetBaseDetailModuel(detailId, isAuditAttc);
        // 调用重载方法获取详情数据
        BaseResponse theRes = GetModuleDetailData(module, dModule, idOrPRow, multi, grid, isAuditAttc, pms);

        // 处理未找到明细SQL的提示信息
        if ("noDetailSql".equals(theRes.getMsg())) {
            theRes.setMsg("未找到模块编号【" + moduleId + "】下明细编号【" + detailId + "】的明细数据SQL，请检查相应模块的配置是否正确！");
        }

        return theRes;
    }

    /**
     * 获取基础明细明细模块配置
     *
     * @param id          明细模块ID
     * @param isAuditAttc 是否为审核附件模式
     * @return 明细模块实体（BaseDetailModuleEntity）或null
     */
    public BaseDetailModule GetBaseDetailModuel(int id, boolean isAuditAttc) {
        // 根据审核模式获取明细数据
        List<Map<String, Object>> detailData;
        if (isAuditAttc) {
            // 审核附件模式：调用获取审核信息明细的方法
            detailData = DataImpl.GetAuditInfoDetails("", "", id);
        } else {
            // 普通模式：调用获取基础明细模块的方法
            detailData = DataImpl.GetBaseDetailModuel(null, String.valueOf(id));
        }

        // 处理查询结果
        if (detailData != null && !detailData.isEmpty()) {
            // 取第一条数据创建明细模块实体
            BaseDetailModule module = new BaseDetailModule(detailData.get(0));
            // 非审核模式下设置ID字段
            if (!isAuditAttc) {
                // 调用DataImpl获取模块ID字段信息（通过DTO接收多返回值）
                ModuleIdFieldDTO mo = DataImpl.GetModuleIdField(module.getUnionKey(), module.getModuleId(), module.getMasterTable());
                module.setIdField(mo.getParmaryKey());
            }

            return module;
        }

        // 无数据时返回null
        return null;
    }

    public BaseDetailModule GetBaseDetailModuel(int id) {
        return GetBaseDetailModuel(id, false);
    }

    /**
     * 获取模块明细数据
     *
     * @param module      基础模块实体
     * @param dModule     明细模块实体
     * @param idOrPRow    ID或行数据JSON字符串
     * @param multi       是否多数据集
     * @param grid        是否表格模式
     * @param isAuditAttc 是否为审核附件模式
     * @return 基础响应对象
     */
    public BaseResponse GetModuleDetailData(
            BaseModule module,
            BaseDetailModule dModule,
            String idOrPRow,
            boolean multi,
            boolean grid,
            boolean isAuditAttc,
            String _pams) {

        BaseResponse response = new BaseResponse();
        if (module == null || dModule == null) {
            return response;
        }

        BaseModule oldModule = module;
        // 如果模块ID匹配联合菜单代码，重新获取基础模块
        if (module.getModuleId().equals(dModule.getUnionMenuCode())) {
            module = GetBaseModule(dModule.getUnionKey(), getMenuId());
        }

        // 确定明细SQL和相关参数
        String detailSql = isAuditAttc ? module.getMasterSql() : dModule.getUnionSql();
        String dformkey = dModule.getFromkey();
        String dmoduleId = dModule.getModuleId();
        boolean isMaster = false;
        String masterCondKey = "";
        Map<String, Object> leftRecord = null, pms = null;

        if (idOrPRow.startsWith("{")) {
            leftRecord = (Map<String, Object>) JSON.Decode(idOrPRow);
//            leftRecord = toLowerColumnName(leftRecord);
        }
        if (!isNullOrEmpty(_pams)) {
            pms = (Map<String, Object>) JSON.Decode(_pams);
            if (leftRecord == null) leftRecord = pms;
            else {
                for (Map.Entry<String, Object> entry : pms.entrySet()) {
                    leftRecord.put(entry.getKey(), entry.getValue());
                }
            }
        }
        // 处理联合模块逻辑
        if (dModule.getUnionMenuCode() != null && !dModule.getUnionMenuCode().isEmpty()) {
//            断点获取数据库ssql
            BaseModule unionModule = GetBaseModule(dModule.getUnionMenuCode(), getMenuId());
            if (unionModule == null || unionModule.getMasterSql() == null || unionModule.getMasterSql().isEmpty()) {
                return response;
            }
            //        断点查询数据库sql
            detailSql = unionModule.getMasterSql();
            dformkey = unionModule.getModuleId();
            dmoduleId = unionModule.getModuleId();
            isMaster = true;
            masterCondKey = unionModule.getCondKey();
        }

        // 校验明细SQL是否存在
        if (detailSql == null || detailSql.isEmpty()) {
            response.setMsg("noDetailSql");
            return response;
        }

        // 非报表模块且左侧记录为空时，从主表查询
        if (leftRecord == null && !module.isIsReport()) {
            if (module.getMasterTable() != null && !module.getMasterTable().isEmpty()) {
                String mainSql = dealQuerySql(module.getMasterSql(), "", null, null, module.getIdField(), idOrPRow, false, false, false);
                try {
                    List<Map<String, Object>> result = toHashTable(dbOperator.executeDataTable(mainSql, 0, 1, tot));
                    if (!result.isEmpty()) {
                        leftRecord = result.get(0);
                    }
                } catch (Exception e) {
                    // 异常处理：可根据需要添加日志记录
                }
            }
        }

        // 确保leftRecord不为null
        leftRecord = leftRecord != null ? leftRecord : new HashMap<>();

        // 定义空字符串变量，对应C#的 string idWhereKey = "";
        String idWhereKey = "";

// 这里是反起得，应该是 unionparentfield=unionfield||unionvalue;  cs错了，就将错就错
// 核心逻辑转换：拼接空字符串 + 移除首尾的{}
        idWhereKey = (Objects.toString(dModule.getUnionField(), "")).replaceAll("^[{]|[}]$", "");

        // 处理搜索条件和SQL
        if (isMaster && (leftRecord != null && leftRecord.containsKey("$isPms"))) {
            detailSql = reqSearchCondition(masterCondKey, detailSql, leftRecord, leftRecord, false);
        }
        detailSql = dealQuerySql(detailSql, leftRecord, leftRecord, null, null, null, false, false, false);
//        out.println(detailSql + " // 处理搜索条件和SQL的detailsql");
        // 处理关联条件
        if (!isNullOrEmpty(idWhereKey)) {
            String idWhereValKey = (isNullOrEmpty(dModule.getUnionParentField()))
                    ? module.getIdField()
                    : dModule.getUnionParentField().trim().replaceAll("^[{]|[}]$", "");

            // 检查字段是否存在，处理配置错误
            if (!leftRecord.containsKey(idWhereValKey.toLowerCase())) {
                if (leftRecord.containsKey(idWhereKey)) {
                    // 交换键值
                    String temp = idWhereKey;
                    idWhereKey = idWhereValKey;
                    idWhereValKey = temp;
                } else {
                    response.setSuccess(true);
                    response.setMsg(String.format("配置错误：未在主表中找到关联字段%s,请检查配置是否正确！", idWhereValKey));
                    return response;
                }
            }

            // 构建WHERE条件并插入SQL
            Object value1 = leftRecord.get(idWhereValKey.toLowerCase());
            // 修复方案1：添加格式校验，非法格式时返回默认值或处理逻辑
            Object value;
            try {
                value = (int) Double.parseDouble(value1.toString());
            } catch (NumberFormatException e) {
                // 处理非数字格式的情况，例如：
                value = value1.toString(); // 或其他默认值
                // 或记录日志
//                err.printf("字段值 {%s} 无法转换为数字，使用默认值-字符串\n", value1);
            }
//            value = (int) Double.parseDouble(value1.toString());
            String where = String.format("and %s='%s'", idWhereKey, value);
            SqlAnalyzer sqlAnalyzer = new SqlAnalyzer(detailSql);
            Map<String, String> whereMap = new HashMap<>();
            whereMap.put(idWhereKey, where);
            detailSql = sqlAnalyzer.InsertWhere(whereMap, true, false, false);
//            out.println(detailSql + " // 构建WHERE条件并插入SQL的detailsql" + value);
        }

        String detailIdStr = Request("detailId", ""), moduleIdStr = Request("moduleId", "");
        // 处理联合条件
        if (dModule.getUnionCond() != null && !dModule.getUnionCond().isEmpty()) {
            String where = PublicUtil.ReqSqlPms(leftRecord, leftRecord, dModule.getUnionCond(), SystemTypeEnums.PmType.sql, getUser());
            SqlAnalyzer sqlAnalyzer = new SqlAnalyzer(detailSql);
            Map<String, String> whereMap = new HashMap<>();
            whereMap.put(idWhereKey, where);
            detailSql = sqlAnalyzer.InsertWhere(whereMap, true, false, false);
//            out.println(detailSql + "// 处理联合条件的detailsql");
        }

        // 处理格式化、颜色和下拉框
        Map<String, Object> formats = null;
        Map<String, String> fontColors = null;
        List<Map<String, Object>> styles = null;
        String[] picFields;
        List<ComboBox> boxs = new ArrayList<>();

        if (dmoduleId != null && !dmoduleId.isEmpty()) {
            List<Map<String, Object>> colorAndboxCols = DataImpl.GetColorAndBoxColumns(dmoduleId);

            // 处理下拉框
            BaseModule finalModule = module;
            boxs = colorAndboxCols.stream()
                    .filter(row -> row.get("fieldsql") != null && !row.get("fieldsql").toString().isEmpty())
                    .map(row -> createControl.createControl(row, finalModule, false)) // 先获取原始控件
                    .filter(Objects::nonNull) // 过滤空值
                    .filter(control -> control instanceof ComboBox) // 只保留ComboBox类型
                    .map(control -> (ComboBox) control)
                    .filter(box -> box != null && !box.getValueField().equals(box.getDisplayField()))
                    .collect(Collectors.toList());

            // 处理表格颜色
            if (grid) {
                // 转换colorAndboxCols为字体颜色字典（fieldname为键，fontcolor为值）
                fontColors = colorAndboxCols.stream()
                        .filter(row -> {
                            Object fontColor = row.get("fontcolor");
                            return fontColor != null && !fontColor.toString().isEmpty();
                        })
                        .collect(Collectors.toMap(
                                row -> {
                                    Object fieldName = row.get("fieldname");
                                    return fieldName != null ? fieldName.toString().toLowerCase() : "";
                                },
                                row -> row.get("fontcolor").toString()
                        ));

                // 调用获取字体颜色列的方法（假设dmoduleId是当前类的属性或方法参数）
                //fontColors = GetFontColorColumns(dmoduleId);
                picFields = colorAndboxCols.stream()
                        .filter(row -> {
                            int fieldType = Integer.parseInt(row.get("fieldtype").toString());
                            return fieldType == SystemEnums.ControlType.LabPic.getValue()
                                    || fieldType == SystemEnums.ControlType.LabPicEx.getValue();
                        })
                        .map(row -> {
                            String fieldName = (String) row.getOrDefault("fieldname", "");
                            return fieldName.toLowerCase();
                        })
                        .toArray(String[]::new);
            } else {
                picFields = new String[0];
            }
        } else {
            picFields = new String[0];
        }
        // 获取样式
        styles = GetStyles(dformkey);
        Function<Map<String, Object>, Map<String, Object>> fillStyles = null;
        Function<Map<String, Object>, Map<String, Object>> fillColor = null;
        if (styles != null) {
            List<Map<String, Object>> finalStyles1 = styles;
            fillStyles = row -> FillStyles(row, finalStyles1);
        }
        if (fontColors != null) {
            Map<String, String> finalfontColors = fontColors;
            fillColor = row -> FillFontColor(row, finalfontColors);
        }
        // 处理多数据集
        if (multi) {
            List<List<Map<String, Object>>> dataSets = executeDataSet(detailSql);
            List<Object> tbs = new ArrayList<>();

            for (List<Map<String, Object>> dataSet : dataSets) {
                //resultList.add(toHashTable(dataSet, formats, fillColor, fillStyles, true));
                tbs.add(toHashTable(dataSet,
                        formats,  // 格式参数（对应C#的formats）
                        // 三元表达式1：判断fontColors为空则传null，否则传fillColor
                        (fontColors == null || fontColors.isEmpty()) ? null : fillColor,
                        // 三元表达式2：判断styles为空/无行则传null，否则传fillStyles
                        (styles == null || styles == null || styles.isEmpty()) ? null : fillStyles,
                        true,  // 布尔参数1
                        true,  // 布尔参数2
                        false, // 布尔参数3
                        // lambda回调：对应C#的 (r, name, v) => FillFilePath(picFields, name, v)
                        (r, name, v) -> {
                            try {
                                return fillFilePath(picFields, name, v);
                            } catch (UnsupportedEncodingException e) {
                                throw new RuntimeException(e);
                            }
                        }
                ));
            }

            response.setData(tbs);
            response.setTot(tbs.size());
        } else {
            // 处理图表模式和分页
            boolean isChart = dModule.getUnionType() == 1;
            int displayRows = dModule.getDisPlayRows() != null ? dModule.getDisPlayRows() : 0;
            int queryStart = isChart ? 0 : getStartsize();
            int querySize = isChart ? -1 : (displayRows == 0 ? getPageSize() : displayRows);

            // 执行查询
            List<Map<String, Object>> dtVal = dbOperator.executeDataTable(detailSql, queryStart, querySize, tot);
//            out.println(dtVal + "dataVal");
            if (dtVal != null && !dtVal.isEmpty()) {
                Map<String, Class<?>> columnTypes = getColumnTypes(dtVal, detailSql);
                module.MainDataColumns = (columnTypes.entrySet().stream()
                        .map(entry -> {
                            String columnName = entry.getKey();
                            Class<?> dataType = entry.getValue();

                            // 转换为前端组件类型（即使字段值为null，仍按数据库定义的类型处理）
                            String xtype = PublicUtil.TypeToColumnType(dataType);

                            // 构建列信息对象（可替换为实际的DTO类）
                            Map<String, String> columnInfo = new HashMap<>();
                            columnInfo.put("dataIndex", columnName);
                            columnInfo.put("xtype", xtype);
                            return columnInfo;
                        })
                        .collect(Collectors.toList()));
                // 设置主数据列信息
//                module.MainDataColumns = dtVal.get(0).keySet().stream()
//                        .map(columnName -> {
//                            Map<String, Object> columnInfo = new HashMap<>();
//                            columnInfo.put("dataIndex", columnName);
//                            columnInfo.put("xtype", PublicUtil.TypeToColumnType((Class<?>) getColumnType(dtVal, columnName)));
//                            return columnInfo;
//                        })
//                        .collect(Collectors.toList());

//                out.println(module.MainDataColumns + "MainDataColumns");
                // 转换数据并处理格式、颜色和样式
                List<Map<String, Object>> tbs = toHashTable(dtVal, formats, (fontColors == null || fontColors.isEmpty()) ? null : fillColor, styles == null || styles.isEmpty() ? null : fillStyles, true, true, false,
                        (r, name, v) ->
                        {
                            try {
                                return fillFilePath(picFields, name, v);
                            } catch (UnsupportedEncodingException e) {
                                throw new RuntimeException(e);
                            }
                        });
                response.setData(convertBoxVal(tbs, boxs, module.getModuleId() + (isNullOrEmpty(dmoduleId) ? "" : "=>" + dmoduleId)));
                response.setTot(tot[0] != 0 ? tot[0] : dtVal.size());
            }
        }
        // 设置响应结果
        response.setSuccess(true);
        response.setOther(oldModule);
        response.setSql(detailSql);
        return response;
    }

    /**
     * 获取配置了字体颜色的列，主要用在添加修改界面
     *
     * @param moduleId 模块ID
     * @return 字段名与字体颜色的映射关系
     * 2026.2.24注销
     */
//    public Map<String, String> GetFontColorColumns(String moduleId) {
//        // 调用DataImpl获取字体颜色配置列（假设存在对应的方法）
//        List<Map<String, Object>> colorColumns = DataImpl.GetFontColorColumns(moduleId);
//
//        // 转换为字段名（小写）到字体颜色的映射
//        return colorColumns.stream()
//                .collect(Collectors.toMap(
//                        // 键：fieldname转换为小写，空值处理为""
//                        row -> {
//                            Object fieldNameObj = row.get("fieldname");
//                            String fieldName = fieldNameObj != null ? fieldNameObj.toString() : "";
//                            return fieldName.toLowerCase();
//                        },
//                        // 值：fontcolor，空值处理为""
//                        row -> {
//                            Object fontColorObj = row.get("fontcolor");
//                            return fontColorObj != null ? fontColorObj.toString() : "";
//                        }
//                ));
//    }

    /**
     * /// <summary>
     * /// Gets the condition fields.
     * /// </summary>
     * /// <param name="moduleId">The module identifier</param>
     * /// <param name="detailId">The detail identifier</param>
     * /// <returns>BaseResponse</returns>
     * /// <remarks>
     */
    public BaseResponse GetCondition(String moduleId, String detailId) {
        BaseResponse response = new BaseResponse();
        String formKey = "";
        if (!isNullOrEmpty(detailId)) {
            BaseDetailModule module = GetBaseDetailModuel(parseInt(detailId));
            if (module != null) {
                formKey = module.getFromkey() + "_Cond";
            }
        } else {
            BaseModule module = GetBaseModule(moduleId, getMenuId());
            formKey = module.getCondKey();
        }
        if (isNullOrEmpty(formKey)) return response;
        response.setData(GetCondition(formKey));
        response.setSuccess(true);
        return response;

    }

    /**
     * 获取字段列组件列表
     *
     * @param moduleCode 模块编码
     * @param fieldId    字段ID
     * @return 组件列表
     */
    public List<Component> GetFieldColumns(String moduleCode, String fieldId) {
        List<Map<String, Object>> dtVal = GetFieldColumnRows(moduleCode, fieldId);
        return ToColumns(dtVal, false);
    }

    /**
     * 获取字段列配置数据（重载方法）
     *
     * @param moduleCode 模块编码
     * @param fieldId    字段ID
     * @param id         额外ID（默认0）
     * @return 字段列配置列表（Map集合表示的行数据）
     */
    public List<Map<String, Object>> GetFieldColumnRows(String moduleCode, String fieldId, int id) {
        // 调用数据实现层方法，传入当前用户信息
        return DataImpl.GetFieldColumnRows(moduleCode, fieldId, getUser().UserId, getUser().UserName, id);
    }

    // 提供默认id参数的重载
    public List<Map<String, Object>> GetFieldColumnRows(String moduleCode, String fieldId) {
        return GetFieldColumnRows(moduleCode, fieldId, 0);
    }

    /**
     * 获取基础模块的所有明细
     *
     * @param module 基础模块实体
     * @return 组件列表（List<Component>）
     * 版本：1.0.0.0
     */
    public Object GetModelDetails(BaseModule module) {
        List<Component> tabs = new ArrayList<>();

        // 获取基础模块明细配置数据
        List<Map<String, Object>> dtVal = DataImpl.GetBaesModuleDetails(module.getFromkey(), getUser().UserId, getUser().UserName);
        for (Map<String, Object> row : dtVal) {
            Component oneCmp = GetOneModelDetail(row, module, null, false);
            if (oneCmp != null) {
                tabs.add(oneCmp);
            }
        }
        return tabs;
    }

    /**
     * 获取一个模板的具体数据
     */
    public Component GetOneModelDetail(Map<String, Object> row, ModuleBaseEntity
            module, Hashtable<String, Object> elseValueHS, boolean isAttc) {
        // 初始化elseValueHS（处理默认参数）
        if (elseValueHS == null) {
            elseValueHS = new Hashtable<>();
        }

        // 从数据行提取基础属性
        String detailId = DataTableUtil.getStringValue(row, "id", "");
        String detailsql = DataTableUtil.getStringValue(row, "detailsql", "").trim();
        int detailType = ToInt32(get(row, "detailtype", 0));
        String formkey = DataTableUtil.getStringValue(row, "fromkey", "");
        String tabTitle = DataTableUtil.getStringValue(row, "detailname", "");
        boolean autoRefresh = DataTableUtil.getBooleanValue(row, "refresh", false);
        String unionmodule = DataTableUtil.getStringValue(row, "unionmodule", "").trim();
        String unionfield = DataTableUtil.getStringValue(row, "unionfield", "");
        String library = DataTableUtil.getStringValue(row, "library", "");
        int displayMode = DataTableUtil.getIntValue(row, "displaymode", 0);
        int addShowMode = DataTableUtil.getIntValue(row, "addshowmode", 0);
        int addVisible = DataTableUtil.getIntValue(row, "addvisible", 0);
        boolean noGridLine = DataTableUtil.getBooleanValue(row, "nogridline", false);
        boolean noRownumber = DataTableUtil.getBooleanValue(row, "norownumber", false);
        boolean noColumnHeader = DataTableUtil.getBooleanValue(row, "hidecolumnheader", false);
        boolean isDrag = DataTableUtil.getBooleanValue(row, "isdrag", false);
        String unionparentfield = DataTableUtil.getStringValue(row, "unionparentfield", "").isEmpty()
                ? module.getIdField()
                : DataTableUtil.getStringValue(row, "unionparentfield", "");
        int displayRows = DataTableUtil.getIntValue(row, "displayrows", 0);
        int orderid = DataTableUtil.getIntValue(row, "orderid", 0);

//        2026.2.24
        boolean defaultActive = DataTableUtil.getBooleanValue(row, "defaultItem", false);

        // 处理条件参数（转换C#的PublicUtil方法）
        String visibleCond = PublicUtil.ReqSqlPms(null, null, DataTableUtil.getStringValue(row, "visiblecond", ""), SystemTypeEnums.PmType.ignorenull, null);
        String fieldCond = PublicUtil.ReqSqlPms(null, null, DataTableUtil.getStringValue(row, "fieldcond", ""), SystemTypeEnums.PmType.ignorenull, null);
        String disableField = DataTableUtil.getStringValue(row, "disablefield", "");
        String fieldCond1 = PublicUtil.ReqSqlPms(null, null, DataTableUtil.getStringValue(row, "fieldcond1", ""), SystemTypeEnums.PmType.ignorenull, null);
        String disableField1 = DataTableUtil.getStringValue(row, "disablefield1", "");
        String fieldCond2 = PublicUtil.ReqSqlPms(null, null, DataTableUtil.getStringValue(row, "fieldcond2", ""), SystemTypeEnums.PmType.ignorenull, null);
        String disableField2 = DataTableUtil.getStringValue(row, "disablefield2", "");
        String bandHeight = DataTableUtil.getStringValue(row, "bandheight", "");
        String bandWidth = DataTableUtil.getStringValue(row, "bandwidth", "");

//        out.println("GetOneModelDetail" + "拿去数据没有问题");
        Object mobileCards = null;
        boolean[] isUrls = new boolean[1];
        boolean isUrl = false;
        // 处理library参数（URL转换逻辑）
        if (!library.isEmpty()) {
            // 转换模块名称并判断是否为URL
            String convertedLibrary = SystemMenu.convertToModuleName(library, 0, isUrls);
            isUrl = isUrls[0];
            if (isUrl) {
                Map<String, String> pmInfos = PublicUtil.UrlToDllInfo(convertedLibrary);
                if (pmInfos != null) {
                    library = pmInfos.get("xtype");
                    unionmodule = pmInfos.get("moduleid");
                    // 填充elseValueHS
                    for (Map.Entry<String, String> entry : pmInfos.entrySet()) {
                        elseValueHS.put(entry.getKey(), entry.getValue());
                    }
                }
            } else {
                library = convertedLibrary;
            }
        }
//        out.println("GetOneModelDetail" + "IsCard  0" + ((BaseModule) module).IsCard);
        // 处理卡片组信息（如果是BaseModule且为卡片模式）
        if (module instanceof BaseModule && ((BaseModule) module).IsCard) {
            mobileCards = GetModuleCardGroup(module.getModuleId(), false, true, Integer.parseInt(detailId), false);
        }
//        out.println("GetOneModelDetail" + "IsCard");
        // 填充elseValueHS通用参数
        if (!elseValueHS.containsKey("fieldCond")) {
            // 构建条件对象（使用Map模拟匿名类）
            Map<String, Object> fieldCondObj = new HashMap<>();
            fieldCondObj.put("displayCond", visibleCond);

            List<Map<String, Object>> fieldConds = new ArrayList<>();
            fieldConds.add(Map.of("cond", fieldCond, "fields", disableField));
            fieldConds.add(Map.of("cond", fieldCond1, "fields", disableField1));
            fieldConds.add(Map.of("cond", fieldCond2, "fields", disableField2));
            fieldCondObj.put("fieldConds", fieldConds);

            elseValueHS.put("fieldCond", fieldCondObj);
        }

//        2026.2.24
        if (!elseValueHS.containsKey("defaultActive")) elseValueHS.put("defaultActive", defaultActive);

        // 添加基础参数到elseValueHS
        if (!elseValueHS.containsKey("detailId")) elseValueHS.put("detailId", detailId);
        if (!elseValueHS.containsKey("orderId")) elseValueHS.put("orderId", orderid);
        if (!elseValueHS.containsKey("displayMode")) elseValueHS.put("displayMode", displayMode);
        if (!elseValueHS.containsKey("addShowMode")) elseValueHS.put("addShowMode", addShowMode);
        if (!elseValueHS.containsKey("addVisible")) elseValueHS.put("addVisible", addVisible);
        if (!elseValueHS.containsKey("bandHeight") && !bandHeight.isEmpty())
            elseValueHS.put("bandHeight", bandHeight);
        if (!elseValueHS.containsKey("bandWidth") && !bandWidth.isEmpty())
            elseValueHS.put("bandWidth", bandWidth);
        if (!elseValueHS.containsKey("dType")) elseValueHS.put("dType", detailType);
        if (!elseValueHS.containsKey("isDrag")) elseValueHS.put("isDrag", isDrag);

//        out.println(elseValueHS.toString() + "elseValueHS");
        // 获取列信息
        List<Component> columns = isAttc
                ? ToColumns(DataImpl.GetAuditInfoDetailColumns(detailId), false)
                : GetBaseDetailColumns(formkey);

        // 根据detailType处理不同组件类型
        if (detailType == 0 || detailType == 4) {
            if (unionmodule.isEmpty()) {
                // 网格面板（无关联模块）
                elseValueHS.put("tabTitle", tabTitle);
                elseValueHS.put("autoRefresh", autoRefresh);
                elseValueHS.put("unionField", unionfield.replace("{", "").replace("}", ""));
                elseValueHS.put("unionParentField", unionparentfield.replace("{", "").replace("}", ""));

                List<Object> bbItems = null;
                GridPanel gridPanel = new GridPanel();
                gridPanel.setXtype(library.isEmpty() ? "PubModule.DetailGrid" : library);
                gridPanel.title = (DataTableUtil.getStringValue(row, "detailname", ""));
                gridPanel.RightMenu = (GetRightMenu(formkey, 0, (Ref) bbItems)); // bbItems使用引用传递
                gridPanel.setColumns(columns);
                gridPanel.IdField = (columns.isEmpty() ? "" : ((Field) columns.get(0)).getName());
                gridPanel.NoGridLine = (noGridLine);
                gridPanel.setRowNumberer(!noRownumber);
                gridPanel.setHideColumnHeader(noColumnHeader);

                // 设置数据源
                DataStore store = new DataStore();
                store.setXtype("PubModule.Store.DetailGrid");
                Map<String, Object> extraParams = new HashMap<>();
                extraParams.put("DetailId", detailId);
                extraParams.put("ModuleId", module.getModuleId());
                store.extraParams = (extraParams);
                gridPanel.setStore(store);

                gridPanel.elseValue = (elseValueHS);
                gridPanel.PageAble = (true);
                gridPanel.MobileCards = (mobileCards);
                gridPanel.displayRows = (displayRows);

                return gridPanel;
            } else {
                // 容器组件（有关联模块）
                elseValueHS.put("autoRefresh", autoRefresh);
                elseValueHS.put("isAttc", isAttc);
                elseValueHS.put("unionField", unionfield.replace("{", "").replace("}", ""));
                elseValueHS.put("unionParentField", unionparentfield.replace("{", "").replace("}", ""));

                MContainer container = new MContainer();
                container.mXtype = (library);
                container.title = (tabTitle);
                container.ModuleId = (unionmodule);
                container.DetailId = (detailId);
                container.ParentModuleId = (module.getModuleId());
                container.ElseValue = (elseValueHS);
                container.MobileCards = (mobileCards);
                container.QueryPms = (isUrl ? elseValueHS : null);

                return container;
            }
        } else if (detailType == 1) {
            // 图表组件
            List<Map<String, Object>> chartRows = null;
            if (unionmodule.isEmpty()) {
                chartRows = DataImpl.GetChartCfg(formkey);
            } else {
                BaseModule unionModule = GetBaseModule(unionmodule, "");
                chartRows = (unionModule != null) ? DataImpl.GetChartCfg(unionModule.getFromkey()) : null;
            }

            // 转换图表配置
            List<Component> chartCfg = new ArrayList<>();
            if (chartRows != null) {
                for (Map<String, Object> chartRow : chartRows) {
                    chartCfg.add(new Chart(chartRow));
                }
            }

            // 填充参数
            elseValueHS.put("columns", columns);
            elseValueHS.put("tabTitle", tabTitle);
            elseValueHS.put("autoRefresh", autoRefresh);

            List<Object> bbItems = null;
            ChartContainer chart = new ChartContainer();
            chart.title = (tabTitle);
            chart.DetailId = (detailId);
            chart.ModuleId = (module.getModuleId());
            chart.RightMenu = (GetRightMenu(formkey, 0, (Ref) bbItems));
            chart.elseValue = (elseValueHS);
            chart.displayRows = (displayRows);
            chart.ChartCfg = (chartCfg);

            if (!library.isEmpty()) {
                chart.setXtype(library);
            }

            return chart;
        } else if (detailType == 2) {
            // 地图组件
            elseValueHS.put("tabTitle", tabTitle);
            elseValueHS.put("autoRefresh", autoRefresh);

            if (detailsql.contains(".html")) {
                // IFrame面板（HTML链接）
                String src = PublicUtil.ReqSqlPmsByRow(module.Updrow, null, detailsql, SystemTypeEnums.PmType.ignorenull, getUser());
                elseValueHS.put("src", src);

                Panel panel = new Panel();
                panel.setXtype("PubModule.IFramePanel");
                panel.title = (DataTableUtil.getStringValue(row, "detailname", ""));
                panel.elseValue = (elseValueHS);

                return panel;
            } else {
                // 地图网格面板
                GridPanel gridPanel = new GridPanel();
                gridPanel.setXtype("map");
                gridPanel.title = (DataTableUtil.getStringValue(row, "detailname", ""));

                DataStore store = new DataStore();
                store.setXtype("PubModule.DetailGrid");
                Map<String, Object> extraParams = new HashMap<>();
                extraParams.put("DetailId", detailId);
                extraParams.put("ModuleId", module.getModuleId());
                store.extraParams = (extraParams);
                gridPanel.setStore(store);

                gridPanel.elseValue = (elseValueHS);
                gridPanel.MobileCards = (mobileCards);

                return gridPanel;
            }
        } else if (detailType == 3) {
            // 自定义组件
            elseValueHS.put("columns", columns);

            MContainer container = new MContainer();
            container.setXtype(library.isEmpty() ? "MContainer" : library);
            container.title = (DataTableUtil.getStringValue(row, "detailname", ""));
            container.DetailId = (detailId);
            container.ModuleId = (module.getModuleId());
            container.ElseValue = (elseValueHS);

            return container;
        }

        return null;
    }

    /**
     * 获取基础详情列组件列表
     *
     * @param formkey 表单键
     * @return 组件列表
     */
    public List<Component> GetBaseDetailColumns(String formkey) {
        List<Map<String, Object>> dtval = GetBaseDetailColumnRows(formkey);
        return ToColumns(dtval, false);
    }

    /**
     * 获取基础详情列数据行
     *
     * @param fromkey 表单键
     * @return 列数据列表
     */
    public List<Map<String, Object>> GetBaseDetailColumnRows(String fromkey) {
        return DataImpl.GetBaseDetailColumnRows(fromkey, getUser().UserId, getUser().UserName, 0);
    }


    /**
     * Api的getBillIniParams方法
     */
    @Override
    public BaseResponse getBillIniParams() {
        BaseResponse response = new BaseResponse();
        String moduleId = Request("ModuleId", ""),
                menuId = Request("MenuId", "");
        BillModule module = GetBillModule(moduleId, menuId);
        List<BillSourceModule> theData = GetBillSourceModule(moduleId, module.getBillSourceIds(), "2");
        response.setData(theData);
        response.setOther(module);
        if (theData.isEmpty()) {
            response.setSuccess(false);
            String notFindModuleMsg = "未找到模块号【" + moduleId + "】的配置数据，请检查相应模块的配置是否正确！可能是多余的空格、回车，数字字母写错，模块暂未配置等情况！";
            response.setMsg(notFindModuleMsg);
        } else {
            response.setSuccess(true);
            log.debug(String.format(("进入模块,%s进入%s模块"), getUser().UserName, module.getModuleName()));
        }
        return response;
    }

    /**
     * 从数据库元数据中查询字段是否为整数类型（结果集为空时使用）
     */
    private boolean isIntegerColumnFromMetaData(String sql, String columnName) {
        try {
            // 解析SQL获取表名（简化逻辑，实际可能需要更复杂的SQL解析）
            String tableName = extractTableNameFromSql(sql);
            if (tableName == null || tableName.isEmpty()) {
                return false;
            }

            // 获取数据库连接和元数据
            Connection conn = jdbcTemplate.getDataSource().getConnection();
            DatabaseMetaData metaData = conn.getMetaData();

            // 查询字段元数据（catalog和schema根据实际数据库配置调整，这里留空）
            ResultSet rs = metaData.getColumns(
                    null,       // catalog（数据库名，可留空）
                    null,       // schema（模式名，如public，可留空）
                    tableName,  // 表名
                    columnName  // 字段名
            );

            if (rs.next()) {
                int dataType = rs.getInt("DATA_TYPE"); // 获取JDBC类型码
                // 匹配整数类型的JDBC类型码：INTEGER(4)、BIGINT(-5)、SMALLINT(5)、TINYINT(-6)
                return dataType == 4 || dataType == -5 || dataType == 5 || dataType == -6;
            }
        } catch (SQLException e) {
            log.error("Exception caught", e);
        }
        return false;
    }

    /**
     * 从SQL中提取表名（简化版，需根据实际SQL格式调整）
     * 适用于简单查询如：select * from table_name where ...
     */
    private String extractTableNameFromSql(String sql) {
        // 正则匹配表名（简化逻辑，复杂SQL需用SQL解析器）
        Pattern pattern = Pattern.compile("from\\s+([\\w]+)", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(sql);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }

    /**
     * 结果集为空时，从元数据中查找目标字段名（忽略大小写）
     */
    private String getKeyColNameFromMetaData(String sql, String keyField) {
        try {
            String tableName = extractTableNameFromSql(sql);
            if (tableName == null) {
                return null;
            }

            Connection conn = jdbcTemplate.getDataSource().getConnection();
            DatabaseMetaData metaData = conn.getMetaData();
            ResultSet rs = metaData.getColumns(null, null, tableName, null); // 查询所有字段

            while (rs.next()) {
                String colName = rs.getString("COLUMN_NAME");
                if (colName.toLowerCase().equals(keyField.toLowerCase())) {
                    return colName;
                }
            }
        } catch (SQLException e) {
            log.error("Exception caught", e);
        }
        return null;
    }

    /**
     * 检查数据库里的字段类型
     */
    // 核心逻辑实现
    public boolean checkIfIntColumn(String sql, String keyField) {
        // 执行查询并获取结果集元数据（即使结果为空也能获取字段信息）
        if (databaseType.equals("dm")) sql = RegexUtil.processDmServerSql(sql);
        return jdbcTemplate.query(sql, rs -> {
            ResultSetMetaData metaData = rs.getMetaData();
            int columnCount = metaData.getColumnCount();

            // 遍历所有列，匹配目标字段（忽略大小写）
            for (int i = 1; i <= columnCount; i++) {
                String columnName = metaData.getColumnName(i);
                if (columnName.toLowerCase().equals(keyField.toLowerCase())) {
                    // 获取字段的JDBC类型，判断是否为整数类型
                    int columnType = metaData.getColumnType(i);
                    // 匹配整数类型的JDBC类型码：INTEGER(4)、BIGINT(-5)、SMALLINT(5)、TINYINT(-6)
                    return columnType == 4 || columnType == -5 || columnType == 5 || columnType == -6;
                }
            }
            return false; // 未找到匹配字段
        });
    }

    /**
     * Api的getBaseAuditStepData方法
     */

    @Override
    public BaseResponse getBaseAuditStepData() {
        BaseResponse response = new BaseResponse();
        String record = Request("record", "") + "",
                leftRecord = Request("leftRecord", ""),
                pms = Request("pms", ""),
                moduleId = Request("moduleId", "");
        int stepId = ToInt32(Request("stepId", ""));

        response = GetAuditStepData(moduleId, stepId, record, leftRecord, pms, true);
        return response;
    }

    /**
     * 获取审批步骤、历史主表、历史明细表数据
     *
     * @param moduleId   模块ID
     * @param stepId     步骤标识：>0：审批步骤 0：主表 -1：明细
     * @param record     记录数据JSON字符串
     * @param leftRecord 左侧记录数据JSON字符串
     * @param pams       参数JSON字符串
     * @param isBase     是否为基础模块
     * @return BaseResponse 响应结果
     */
    public BaseResponse GetAuditStepData(String moduleId, int stepId, String record,
                                         String leftRecord, String pams, boolean isBase) {

        BaseResponse response = new BaseResponse();
        ModuleBaseEntity module = null;
        Map<String, Object> recordMap = new HashMap<>();
        Map<String, Object> leftRecordMap = new HashMap<>();
        Map<String, Object> pmsMap = new HashMap<>();
        String menuId = Request("menuId", "");
        // 获取模块信息
        if (isBase) {
            module = GetBaseModule(moduleId, menuId); // 假设menuId为类中已定义的变量
        }
        if (module == null) {
            module = GetBillModule(moduleId, menuId); // 假设存在获取单据模块的方法
            isBase = false;
        }
        if (module == null) {
            response.setMsg(LanguageUtil.GetString("InvalidCode"));
            // 可添加详细错误信息
            return response;
        }

        // 解析JSON参数
        if (StringUtils.hasText(record)) {
            recordMap = (Map<String, Object>) JSON.Decode(record); // 假设使用Jackson或FastJSON
        }
        if (StringUtils.hasText(leftRecord)) {
            leftRecordMap = (Map<String, Object>) JSON.Decode(leftRecord);
        }
        if (StringUtils.hasText(pams)) {
            pmsMap = (Map<String, Object>) JSON.Decode(pams);
            if (recordMap == null) {
                recordMap = pmsMap;
            } else if (pmsMap != null) {
                recordMap.putAll(pmsMap);
            }
        }

        String querySql = "";

        // 构建查询SQL
        if (stepId > 0 && stepId != -999) { // 普通自定义审批步骤数据
            AuditStep detail = GetAuditStep(stepId, isBase, "", ""); // 假设存在获取审批步骤的方法
            if (detail != null) {
                querySql = detail.getStepSql();
            }
        } else if (stepId == -1) { // 单据来源明细数据
            String tableName = isBase ? "p_baseflowstep" : "wms_billflowstep";
            querySql = String.format("select stepCode, stepName, happenTime, operatorName, operTime, " +
                            "case when operDirection='F' then '审核' else '返退' end as operDirectionName, " +
                            "applyTime, operAdvice, " +
                            "case when autoStep=1 then '自动' else '' end as autoStep " +
                            "from %s a " +
                            "where a.flowSourceKey= {{parent.%s}} or a.flowSourceKey= {{parent.billdocument_id}} " +
                            "order by operTime",
                    tableName, module.getIdField());
        } else if (StringUtils.hasText(module.getOverBackSql())) { // 历史主表数据
            querySql = module.getOverBackSql();
        } else { // 历史主表默认查询
            String overBackCond = module.getOverBackCond();
            if (StringUtils.hasText(overBackCond)) {
                overBackCond = "and " + PublicUtil.ReqSqlPms(recordMap, leftRecordMap, overBackCond, SystemTypeEnums.PmType.sql, getUser());
            } else {
                overBackCond = "";
            }
            stepId = -2;

            StringBuilder whereBuilder = new StringBuilder();
            if (pmsMap != null && !pmsMap.isEmpty()) {
                for (Map.Entry<String, Object> entry : pmsMap.entrySet()) {
                    String key = entry.getKey().toLowerCase();
                    Object value = entry.getValue();
                    if (value == null || isNullOrEmpty(value.toString())) {
                        continue;
                    }

                    switch (key) {
                        case "stepovername":
                            whereBuilder.append(String.format(" AND a.%s%s like '%%%s%%'",
                                    module.getMenuPrefix(), key, value));
                            break;
                        case "bdate":
                            whereBuilder.append(String.format(" AND a.%sStepOverTime >= '%s'",
                                    module.getMenuPrefix(), value));
                            break;
                        case "edate":
                            // 处理结束日期加一天
                            try {
                                LocalDate endDate = LocalDate.parse(value.toString());
                                endDate = endDate.plusDays(1);
                                whereBuilder.append(String.format(" AND a.%sStepOverTime <= '%s'",
                                        module.getMenuPrefix(), endDate.toString()));
                            } catch (DateTimeParseException e) {
                                // 日期解析失败处理
                                whereBuilder.append(String.format(" AND a.%sStepOverTime <= '%s'",
                                        module.getMenuPrefix(), value));
                            }
                            break;
                        case "billcode":
                            whereBuilder.append(String.format(" AND a.%s like '%%%s%%'",
                                    module.getIdField(), value));
                            break;
                    }
                }
            }

            // 构建主查询SQL
            String flowOperTable = isBase ? "p_baseflowoper" : "wms_billflowoper";
            String systemTable = isBase ? "p_systemdlltab" : "p_systembilltype";
            String systemTableKey = isBase ? "DllCoid" : "typecode";

            querySql = String.format("select a.%s, " +
                            "convert(varchar(25), a.%sOperatedate, 120) as %sOperateDate, " +
                            "dbo.P_GetOperator(a.%soperatorid) as %sOperatorName, " +
                            "a.%sstepCode, " +
                            "convert(varchar(25), a.%sAffirmDate, 120) as %sAffirmDate, " +
                            "dbo.P_GetOperator(a.%saffirmer) as %sAffirmerName, " +
                            "a.%sStepOverName, " +
                            "convert(varchar(25), a.%sStepOverTime, 120) as %sStepOverTime " +
                            "from %s a " +
                            "inner join %s w on w.keyvalue = a.%s " +
                            "and ((charindex(',%s,', ',' + browsers + ',') > 0 " +
                            "or charindex(',%s,', ',' + operators + ',') > 0) " +
                            "or (select count(1) from %s c where c.%s = '%s' " +
                            "and charindex(',%s,', ',' + c.OverBackOper + ',') > 0) > 0) " +
                            "where isnull(a.%sstepover, 0) = 1 %s %s",
                    module.getIdField(),
                    module.getMenuPrefix(), module.getMenuPrefix(),
                    module.getMenuPrefix(), module.getMenuPrefix(),
                    module.getMenuPrefix(),
                    module.getMenuPrefix(), module.getMenuPrefix(),
                    module.getMenuPrefix(), module.getMenuPrefix(),
                    module.getMenuPrefix(),
                    module.getMenuPrefix(), module.getMenuPrefix(),
                    module.getMasterTable(),
                    flowOperTable, module.getIdField(),
                    getUser().UserName, getUser().UserName,
                    systemTable, systemTableKey, moduleId,
                    getUser().UserName,
                    module.getMenuPrefix(),
                    whereBuilder, overBackCond);
        }
        // 处理查询条件
        if (StringUtils.hasText(querySql) && stepId != -2) {
            querySql = PublicUtil.ReqSqlPms(recordMap, leftRecordMap, querySql, SystemTypeEnums.PmType.sql, getUser());
            if (StringUtils.hasText(module.getOverBackSql()) && stepId == -999) {
                querySql = reqSearchCondition(module.getOverBackKey(), querySql, pmsMap, leftRecordMap, false);
            }
        }
        // 执行查询
        if (StringUtils.hasText(querySql)) {
//            int[] total = new int[1]; // 用于接收总数
            List<Map<String, Object>> result = dbOperator.executeDataTable(querySql, getStartsize(), getPageSize(), tot);
//            List<Map<String, Object>> result = jdbcTemplate.queryForList(querySql);
            response.setData(toHashTable(result));
            response.setTot(tot[0] != 0 ? tot[0] : result.size());
        }
        response.setSuccess(true);
        return response;
    }

    /**
     * 获取审批步骤明细
     *
     * @param stepId   步骤ID
     * @param isBase   是否为基础步骤
     * @param moduleId 模块ID（可选）
     * @param stepCode 步骤编码（可选）
     * @return 审批步骤对象，无数据时返回null
     */
    protected AuditStep GetAuditStep(int stepId, boolean isBase, String moduleId, String stepCode) {
        List<Map<String, Object>> auditStepInfos = DataImpl.GetAuditStepInfos(moduleId, stepId, isBase, stepCode);
        if (auditStepInfos != null && !auditStepInfos.isEmpty()) {
            return new AuditStep(auditStepInfos.get(0));
        }
        return null;
    }

    @Override
    public BaseResponse GetAuditIniParams() {
        String moduleid = Request("ModuleId", "0");
        String menuid = Request("MenuId", "0");
        Boolean windowsDirver = toBoolean(Request("windowsDirver", ""));
        Boolean isBase = toBoolean(Request("isBase", "1"));
        BaseResponse response = new BaseResponse();
        ModuleBaseEntity module = null;
        if (isBase) {
            module = GetBaseModule(moduleid, menuid);
//            out.println(module.getIdField());
        }
        if (module == null) {
            module = GetBillModule(moduleid, menuid);
            isBase = false;
        }
        if (module == null) {
            String baseMsg = LanguageUtil.GetString("InvalidCode");
            String fullMsg = baseMsg + " 未找到模块号【" + moduleid + "】以及MenuId【" + menuid + "】对应的(单据or基础模块)数据，请检查相应模块的配置是否正确！可能是多余的空格、回车，数字字母写错，模块暂未配置等情况！";
            response.setMsg(fullMsg);
            response.setSuccess(false); // 建议补充设置成功状态为false
            return response;
        }
        List<Map<String, Object>> dataTable = DataImpl.GetAuditStepInfos(moduleid, 0, isBase, "");
        String idField = module.getIdField();
        if (dataTable != null && !dataTable.isEmpty()) {
            Map<String, List<Map<String, Object>>> grouped = dataTable.stream()
                    .collect(Collectors.groupingBy(row -> {
                        Object stepGroup = row.get("stepgroup");
                        return stepGroup != null ? stepGroup.toString() : ""; // 确保键为字符串
                    }));
            ModuleBaseEntity finalModule = module;
            Boolean finalIsBase = isBase;

            List<Object> tabs = new ArrayList<>();

// 处理每个分组
            for (Map.Entry<String, List<Map<String, Object>>> entry : grouped.entrySet()) {
                String groupKey = entry.getKey(); // 对应dict.Key
                List<Map<String, Object>> groupRows = entry.getValue(); // 对应dict.ToList()

                // 1. 构建当前分组的结果Map（模拟匿名对象）
                Map<String, Object> tab = new HashMap<>();

                // 2. 设置text
                tab.put("text", groupKey);

                // 3. 设置count
                int count = GetStepDataCount(groupRows, module, groupKey);
                tab.put("count", count);

                // 4. 设置items（转换为GridPanel列表）
                List<Component> items = new ArrayList<>();
                for (Map<String, Object> row : groupRows) { // 对应from row in dict.ToList()
                    GridPanel gridPanel = new GridPanel();

                    // 设置title：row["stepname"] + ""
                    Object stepname = row.get("stepname");
                    gridPanel.title = stepname != null ? stepname.toString() : "";

                    // 设置IdField
                    gridPanel.IdField = idField;

                    // 设置columns
                    Object rowId = row.get("id");
                    Object stepcode = row.get("stepcode");
                    gridPanel.setColumns(
                            GetAuditStepColumns(
                                    moduleid,
                                    rowId != null ? rowId.toString() : "",
                                    stepcode != null ? stepcode.toString() : "",
                                    isBase
                            )
                    );

                    // 设置MobileCards：WindowsDirver?null: ...
                    if (windowsDirver) {
                        gridPanel.MobileCards = null;
                    } else {
                        gridPanel.MobileCards = GetAuditStepColumnCards(moduleid, stepcode != null ? stepcode.toString() : "", isBase
                        );
                    }

                    // 设置store及extraParams
                    DataStore store = new DataStore();
                    Map<String, Object> extraParams = new HashMap<>(); // 模拟匿名对象
                    extraParams.put("moduleId", moduleid);
                    extraParams.put("stepId", row.get("id"));
                    extraParams.put("stepCode", stepcode);
                    store.extraParams = extraParams;
                    gridPanel.setStore(store);

                    items.add(gridPanel);
                }
                tab.put("items", items);

                // 5. 将当前分组的Map添加到tabs
                tabs.add(tab);
            }
            // 创建Map模拟匿名对象
            Map<String, Object> completedTab = new HashMap<>();
            completedTab.put("text", "已完成单据");
            completedTab.put("count", GetStepDataCount(null, module, "已完成单据"));
            completedTab.put("items", GetAuditHistoryInfo(module));
            tabs.add(completedTab);


            response.setData(tabs);
            response.setOther(module);
            response.setSuccess(true);
        } else {
            response.setMsg(" 未找到模块号【" + moduleid + "】对应的审批步骤数据，请检查相应模块的配置是否正确！可能模块编号重复！");
        }
        return response;
    }

    /**
     * Api的GetStepDataCounts方法
     */
    @Override
    public BaseResponse GetStepDataCounts() {
        String moduleId = Request("ModuleId", "");

        BaseResponse response = new BaseResponse();
        response.setSuccess(true);
        response.setData(GetStepDataCounts(moduleId));
        response.setMsg("");
        response.setSharToken(true);
        return response;
    }

    public Map<String, Integer> GetStepDataCounts(String moduleId) {
        // 获取基础模块信息，优先使用GetBaseModule，不存在则使用GetBillModule
        String menuId = Request("MenuId", "");
        ModuleBaseEntity module = GetBaseModule(moduleId, menuId);
        if (module == null) {
            module = (ModuleBaseEntity) GetBillModule(moduleId, menuId);
        }

        // 获取审核步骤信息数据列表（假设返回List<Map<String, Object>>模拟DataTable）
        List<Map<String, Object>> dataList = DataImpl.GetAuditStepInfos(moduleId, 0, module instanceof BaseModule, "");
        Map<String, Integer> retMap = null;

        if (dataList != null && !dataList.isEmpty()) {
            // 按stepgroup分组，并转换为Map<String, Integer>
            ModuleBaseEntity finalModule = module;
            retMap = dataList.stream()
                    // 分组：以stepgroup为键
                    .collect(Collectors.groupingBy(
                            row -> String.valueOf(row.get("stepgroup")),
                            // 对每个分组计算数量
                            Collectors.collectingAndThen(
                                    Collectors.toList(),
                                    groupList -> GetStepDataCount(groupList, finalModule, groupList.get(0).get("stepgroup").toString())
                            )
                    ));
        }

        // 添加"已完成单据"的统计
        if (retMap != null) {
            retMap.put("已完成单据", GetStepDataCount(null, module, "已完成单据"));
        }

        return retMap;
    }

    private int GetStepDataCount(List<Map<String, Object>> rows, ModuleBaseEntity module, String groupName) {
        int tot = 0;
        if ("已完成单据".equals(groupName)) {
            String sql = module.getOverBackSql();
            if (sql == null || sql.trim().isEmpty()) {
                // 构建默认SQL查询
                String masterTable = module.getMasterTable();
                String menuPrefix = module.getMenuPrefix();
                String flowOperTable = module instanceof BaseModule ? "p_baseflowoper" : "wms_billflowoper";
                String idField = module.getIdField();
                String userName = getUser().UserName; // 假设从上下文获取用户名
                String moduleId = module.getModuleId();
                String systemTable = module instanceof BaseModule ? "p_systemdlltab" : "p_systembilltype";
                String systemTableField = module instanceof BaseModule ? "DllCoid" : "typecode";

                sql = String.format(
                        "select count(1) t from %s a " +
                                "inner join %s w on w.keyvalue = a.%s " +
                                "and ((charindex(',%s,', ',' + browsers + ',') > 0 or charindex(',%s,', ',' + operators + ',') > 0) " +
                                "or (select count(1) from %s c where c.%s = '%s' and charindex(',%s,', ',' + c.OverBackOper + ',') > 0) > 0) " +
                                "where isnull(a.%sstepover, 0) = 1 ",
                        masterTable, flowOperTable, idField,
                        userName, userName,
                        systemTable, systemTableField, moduleId, userName,
                        menuPrefix
                );

                try {
                    // 执行SQL并获取结果
                    Integer result = jdbcTemplate.queryForObject(sql, Integer.class);
                    tot += result != null ? result : 0; // 假设工具类转换为int
                } catch (Exception e) {
                    log.warn(String.valueOf(e.getMessage())); // 假设存在error日志方法
                }
                return tot;
            }

            try {
                // 处理自定义SQL：替换参数并构建计数查询
                String processedSql = PublicUtil.ReqSqlPms(null, null, sql, SystemTypeEnums.PmType.sql, getUser());
                SqlAnalyzer sqlAnalyzer = new SqlAnalyzer(processedSql);
                String countSql = sqlAnalyzer.BuildCoutCmdText("count(1) tot", true);
                Integer result = jdbcTemplate.queryForObject(countSql, Integer.class);
                tot += result != null ? result : 0;
            } catch (Exception e) {
                // 忽略异常
            }
        } else if (rows != null) {
            for (Map<String, Object> row : rows) {
                String sql = DataTableUtil.getStringValue(row, "stepsql", "");
                if (!sql.trim().isEmpty()) {
                    try {
                        // 处理步骤SQL：替换参数并构建计数查询
                        String processedSql = PublicUtil.ReqSqlPms(null, null, sql, SystemTypeEnums.PmType.sql, getUser());
                        SqlAnalyzer sqlAnalyzer = new SqlAnalyzer(processedSql);
                        String countSql = sqlAnalyzer.BuildCoutCmdText("count(1) tot", true);
                        String paramSql = countSql.replace("{loginname}", getUser().UserName);
                        // 使用参数化查询，第二个参数是替换的值
                        Integer result = jdbcTemplate.queryForObject(paramSql, Integer.class);
                        tot += result != null ? result : 0;
                    } catch (Exception e) {
                        // 打印详细异常信息（关键：排查错误原因）
                        log.debug(String.valueOf("执行sql异常！SQL: " + ", 错误: " + e.getMessage()));
                    }
                }
            }
        }

        return tot;
    }

    /**
     * api中 BaseResponse GetAddOrUpdFields()方法;
     */
    @Override
    public BaseResponse GetAddOrUpdFields() {
        BaseResponse response = new BaseResponse();
        String idValue = Request("idValue", ""),
                detailId = Request("detailId", ""),
                contextMenuId = Objects.toString(Request("contextMenuId", ""), ""),
                leftRecord = Request("leftRecord", "");
        boolean details = toBoolean(Request("details", "")),
                tpl = toBoolean(Request("tpl", "")),
                atts = toBoolean(Request("atts"));
        response = GetAddOrUpdFields(Request("ModuleId", ""), detailId, idValue, leftRecord, ToInt32(contextMenuId), details, tpl, toBoolean(Request("isattc", "")), atts);

        return response;
    }

    public BaseResponse GetAddOrUpdFields(String moduleId, String idValue, String _leftRecord) {
        Hashtable leftRecord = null;
        if (!isNullOrEmpty(_leftRecord)) {
            leftRecord = (Hashtable) JSON.Decode(_leftRecord);
        }
        BaseModule module = GetBaseModule(moduleId, getMenuId());
        if (module == null) {
            return new BaseResponse() {
                {
                    setSuccess(false);
                    setMsg("未找到模块");
                }
            };
        }
        module.setLeftRecord(leftRecord);
        return GetAddOrUpdFields(module, idValue, 0, "", false, false);
    }

    /**
     * /// <param name="moduleId"></param>
     * /// <param name="detailId"></param>
     * /// <param name="idValue"></param>
     * /// <param name="pIdOrPRow"></param>
     * /// <param name="contextMenuId"></param>
     * /// <param name="details"></param>
     * /// <param name="tpl"></param>
     * /// <param name="isAttc">是否为审批附加模块</param>
     * /// <returns></returns>
     */
    public BaseResponse GetAddOrUpdFields(String moduleId, String detailId, String idValue, String pIdOrPRow,
                                          int contextMenuId, boolean details, boolean tpl, boolean isAttc, boolean atts) {
        String menuId = Request("menuId", "");
        BaseModule module = GetBaseModule(moduleId, menuId);// 假设menuId为类中已定义的成员变量
        if (module == null) {
            BaseResponse response = new BaseResponse();
            response.setMsg("未找到对应模块！");
            return response;
        }
        if (atts) {
            module.AttCfgs = GetAttachModuleCfgs(moduleId);
        }
        if (detailId != null && !detailId.isEmpty()) {
            int detailIdInt = Integer.parseInt(detailId);
            module.DetailModule = (GetBaseDetailModuel(detailIdInt, isAttc));
            if (module.DetailModule == null && !isAttc) {
                module.DetailModule = (GetBaseDetailModuel(detailIdInt, true));
            }
        }

        if (pIdOrPRow != null && pIdOrPRow.startsWith("{")) {
            try {
                // 假设使用Jackson进行JSON解析
                Map<String, Object> leftRecord = (Map<String, Object>) JSON.Decode(pIdOrPRow, Map.class);
                module.setLeftRecord(leftRecord);
            } catch (Exception e) {
                // 处理JSON解析异常
                log.error("Exception caught", e);
            }
        }

        if (module.getLeftRecord() == null && (idValue == null || idValue.isEmpty())) {
            BaseDetailModule detailModule = module.DetailModule;
            if (detailModule != null) {
                BaseModule parentModule = GetBaseModule(detailModule.getUnionKey(), menuId);
                if (parentModule != null) {
                    // 获取添加或更新数据，假设返回的data是Map类型
                    BaseResponse addOrUpdResponse = GetAddOrUpdData(parentModule.getModuleId(), "", "", "");
                    module.setLeftRecord((Map<String, Object>) addOrUpdResponse.getData());

                    module.ParentModule = (parentModule);

                    // 设置关联父字段，若为空则使用父模块的IdField
                    String unionParentField = detailModule.getUnionParentField();
                    if (unionParentField == null || unionParentField.isEmpty()) {
                        detailModule.setUnionParentField(parentModule.getIdField());
                    }
                }
            }
        }
        return GetAddOrUpdFields(module, idValue, contextMenuId, "", details, tpl);
    }

    /**
     * 基础档案增删改逻辑
     */
    public BaseResponse GetAddOrUpdData(String moduleId, String detailId, String idValue, String pIdOrPRow,
                                        int contextMenuId, boolean isAttc) {
        // 调用获取字段信息的方法，对应C#中的GetAddOrUpdFields
        BaseResponse response = GetAddOrUpdFields(moduleId, detailId, idValue, pIdOrPRow, contextMenuId, false, false, isAttc, false);

        if (response.isSuccess()) {
            // 将返回数据转换为字段列表
            List<Field> fields = (List<Field>) response.getData();
            if (fields != null) {
                // 过滤出值不为空的字段，转换为Map
                Map<String, Object> dataMap = fields.stream()
                        .filter(field -> field.getValue() != null && !field.getValue().toString().isEmpty())
                        .collect(Collectors.toMap(Field::getName, Field::getValue));

                response.setData(dataMap);
                // 可选：清空其他数据
                // response.setOther(null);
            }
        }

        return response;
    }

    // 可选的方法重载（处理默认参数contextMenuId=0）
    public BaseResponse GetAddOrUpdData(String moduleId, String detailId, String idValue, String pIdOrPRow) {
        return GetAddOrUpdData(moduleId, detailId, idValue, pIdOrPRow, 0, false);
    }


    public BaseResponse GetAddOrUpdFields(ModuleBaseEntity module, String idValue, int contextMenuId, String sql,
                                          boolean details, boolean tpl) {
        BaseResponse response = new BaseResponse();
        if (module == null) {
            return response;
        }

        // 处理模板逻辑
        if (tpl) {
            boolean isBaseModule = module instanceof BaseModule;
            module.CusAddTpl = (DataImpl.GetModuleAddTpl(module.getModuleId(), isBaseModule));
        }

        // 处理更新数据逻辑
        if (idValue != null && !idValue.isEmpty()) {
            module.IdValue = idValue;
            response = SetUpdateRow(module, sql, idValue);
            if (!response.isSuccess()) {
                return response;
            }
        }

        // 处理右键菜单按钮逻辑
        SysPoPupMenuBtn btn = null;
        if (contextMenuId > 0) {
            btn = GetContextMenuBtn(contextMenuId, module.getLeftRecord());
            // 尝试从旧数据库操作器获取按钮配置(！！！！！以后注意更改)
//            if (btn == null && getOldDbOperator() != null) {
//                //右键可能是读取原账套的配置
//                DataImpl _oldDataImpl = DataImpl;
//                DataImpl = new DataImpl() {{
//                    dbOperator = getOldDbOperator();
//                }};
//                btn = GetContextMenuBtn(contextMenuId, module.getLeftRecord());
//                DataImpl = _oldDataImpl;
//            }
            if (btn == null) {
                DataImpl oldDataImpl = DataImpl;
                DataImpl = new DataImpl(dbOperator);
                btn = GetContextMenuBtn(contextMenuId, module.getLeftRecord());
                DataImpl = oldDataImpl; // 恢复原始dataImpl
            }

            if (btn != null) {
                // 处理弹出参数
                Map<String, Object> popPms = btn.getPopPms();
                module.PopPms = (popPms);

                // 合并左侧记录
                Map<String, Object> leftRecord = module.getLeftRecord();
                if (leftRecord == null) {
                    popPms = toLowerColumnName(popPms);
                    module.setLeftRecord(popPms);
                } else {
                    for (Map.Entry<String, Object> entry : popPms.entrySet()) {
                        leftRecord.put(entry.getKey().toLowerCase(), entry.getValue());
                    }
                    leftRecord = toLowerColumnName(leftRecord);
                    module.setLeftRecord(leftRecord);
                }
            }
        }

        // 获取列配置并创建控件
        List<Map<String, Object>> dtVal = GetColumnRows(module.getModuleId());
//        out.println(dtVal.size() + " GetColumnRows：dtVal的数量");
        List<Field> cols = createControl.createControl(dtVal, module);
        List<Object> bbItems = null;
        module.rightMenus = GetRightMenu(module.getModuleId(), 0, new Ref(bbItems), "toolClick");

        Field mapField = cols.stream()
                .filter(field -> "maplocationfield".equals(field.getXtype()))
                .findFirst()
                .orElse(null);
        if (mapField != null) {
            Field mapOtherField = cols.stream()
                    .filter(field -> (mapField.getName() + "_longitude").equals(field.getName()))
                    .findFirst()
                    .orElse(null);
            if (mapOtherField != null) {
                mapOtherField.disabled = (true); // 设置disabled为true（JavaBean setter）
            }

// 2. 查找名称为 mapField.name + "_latitude" 的字段并禁用
            mapOtherField = cols.stream()
                    .filter(field -> (mapField.getName() + "_latitude").equals(field.getName()))
                    .findFirst()
                    .orElse(null);
            if (mapOtherField != null) {
                mapOtherField.disabled = (true);
            }

// 3. 查找名称为 mapField.name + "_itude" 的字段并禁用
            mapOtherField = cols.stream()
                    .filter(field -> (mapField.getName() + "_itude").equals(field.getName()))
                    .findFirst()
                    .orElse(null);
            if (mapOtherField != null) {
                mapOtherField.disabled = (true);
            }

        }
// 核心逻辑：对应C#的if (cols != null && module.LeftRecord != null)
        if (cols != null && module.getLeftRecord() != null) {
            String unionFiled, unionParentFiled = "";

            // 分支1：按钮有UnionField/UnionValue时的处理
            if (btn != null &&
                    (!isNullOrEmpty(btn.getUnionField()) || !isNullOrEmpty(btn.getUnionValue()))) {
                unionFiled = "";
                String targetFieldName = (btn.getUnionField() + "").trim().replace("{", "").replace("}", "").toLowerCase();
                // 查找匹配的字段（等价于FirstOrDefault）
                Field field = cols.stream()
                        .filter(col -> targetFieldName.equals(col.getName().toLowerCase())) // 统一转小写避免大小写问题
                        .findFirst()
                        .orElse(null);
                // 设置默认值
                if (field != null) {
                    field.setDefaultval(btn.getUnionValue());
                }
            }
            // 分支2：模块有DetailModule且UnionField非空时的处理
            else if (module.DetailModule != null && !isNullOrEmpty(module.DetailModule.getUnionField())) {
                // 格式化关联字段名（去大括号、转小写）
                unionFiled = module.DetailModule.getUnionField().trim().replace("{", "").replace("}", "").toLowerCase();

                // 处理关联父字段
                if (module.DetailModule.getUnionParentField() != null) {
                    unionParentFiled = module.DetailModule.getUnionParentField().trim().replace("{", "").replace("}", "").toLowerCase();
                }

                // 如果UnionParentField为空，补充父模块信息
                if (isNullOrEmpty(module.DetailModule.getUnionParentField())) {
                    // 父模块为空则获取基础模块（等价于C#的?? 空合并）
                    if (module.ParentModule == null) {
                        module.ParentModule = (GetBaseModule(module.DetailModule.getUnionKey(), getMenuId()));
                    }
                    // 赋值父模块ID字段
                    unionParentFiled = (module.ParentModule == null) ? "" : module.ParentModule.getIdField();
                }

                // 查找匹配的字段并设置默认值
                Field field = cols.stream()
                        .filter(col -> unionFiled.equals(col.getName().toLowerCase()))
                        .findFirst()
                        .orElse(null);
                // 设置默认值（module.LeftRecord[unionParentFiled] 假设是Map/对象取值）
                if (field != null) {
                    // 注意：这里假设 module.getLeftRecord() 是 Map<String, Object> 类型
                    // 如果是自定义对象，需替换为 module.getLeftRecord().getXXX(unionParentFiled)
                    field.setDefaultval(module.getLeftRecord().get(unionParentFiled));
                }
            }
        }
        // 处理详情逻辑
        if (details && module != null && module instanceof BaseModule) {
            ((BaseModule) module).Details = (GetModelDetails((BaseModule) module));
        }

        // 设置响应数据
        response.setData(cols);
        response.setOther(module);
        response.setSuccess(true);
//        out.println("GetAddOrFields.response");
        if (!isNullOrEmpty(idValue)) {
//            getEventHandler().callOnModuleDetail(module, response);
        }
        return response;
    }


    // 辅助方法：禁用指定名称的字段
    private void disableField(List<Field> cols, String fieldName) {
        cols.stream()
                .filter(field -> fieldName.equals(field.getName()))
                .findFirst()
                .ifPresent(field -> field.disabled = (true));
    }

    private BaseResponse SetUpdateRow(ModuleBaseEntity module, String sql, String idValue) {
        BaseResponse response = new BaseResponse();
        Map<String, Object> updrow = null; // Java中用Map替代DataRow
        if (module == null) {
            return response;
        }

        String dataSql = "", noValSql = "";
        Map<String, Object> record = null;

        // 处理BaseModule类型
        if (module instanceof BaseModule) {
            BaseModule baseModule = (BaseModule) module;
            if (!isNullOrEmpty(baseModule.getMasterSql()) && !isNullOrEmpty(idValue)) {
                record = new HashMap<>();
                record.put(module.getIdField(), idValue);
                dataSql = dealQuerySql(
                        isNullOrEmpty(sql) ? baseModule.getMasterSql() : sql,
                        record,
                        module.getLeftRecord(),
                        null,
                        module.getIdField(),
                        idValue,
                        true, false, false
                );
            }
        }
        // 处理BillModule类型
        else if (module instanceof BillModule) {
            BillModule billModule = (BillModule) module;
            if (isNullOrEmpty(idValue) && module.PopBtn != null) {
                dataSql = module.PopBtn.dllpar8;
            } else if (!isNullOrEmpty(billModule.getMasterSql()) && !isNullOrEmpty(idValue)) {
                record = new HashMap<>();
                record.put(module.getIdField(), idValue);
                dataSql = dealQuerySql(
                        isNullOrEmpty(sql) ? billModule.getMasterSql() : sql,
                        record,
                        module.getLeftRecord(),
                        null,
                        module.getIdField(),
                        idValue,
                        true, false, false
                );
            }
        }

        // 构建noValSql
        if (!isNullOrEmpty(module.getMasterTable()) && !isNullOrEmpty(idValue)) {
            // 使用参数化SQL避免注入风险，这里先拼接（实际应使用占位符）
            noValSql = String.format("select top 1 * from %s where %s = ?",
                    module.getMasterTable(), module.getIdField());
        }

        // 校验SQL是否为空
        if (isNullOrEmpty(dataSql) && isNullOrEmpty(noValSql)) {
            response.setMsg(String.format(LanguageUtil.WrongMainCfg, "MasterTable Or MasterSql"));
            return response;
        }

        // 确定最终执行的SQL
        dataSql = isNullOrEmpty(dataSql) ? noValSql : dataSql;

        // 执行查询获取数据行
        try {
            // 执行SQL查询，获取结果集（这里假设dbOperator有对应的查询方法）
            if (databaseType.equals("dm")) dataSql = RegexUtil.processDmServerSql(dataSql);
            List<Map<String, Object>> resultList = jdbcTemplate.queryForList(dataSql); // 传递参数避免SQL注入
            if (resultList != null && !resultList.isEmpty()) {
                updrow = resultList.get(0); // 获取第一行数据
                // 转换列名为小写（模拟ToLowerColumnName）
                updrow = toLowerColumnName(updrow);
            }
        } catch (Exception e) {
            // 异常处理（根据实际需求添加日志）
//            this.Error($"根据主表sql获取模块[{module.ModuleId}][{idValue}]行数据出错!", e);
            log.error("Exception caught", e);
        }

        // 当主查询失败时尝试备用SQL
        if ((updrow == null || (updrow != null && updrow.size() > 1))
                && !dataSql.equals(noValSql)
                && !isNullOrEmpty(noValSql)) {
            try {
                List<Map<String, Object>> backupList = jdbcTemplate.queryForList(noValSql, idValue); // 传递参数
                if (backupList != null && !backupList.isEmpty()) {
                    updrow = backupList.get(0);
                    updrow = toLowerColumnName(updrow);
                    dataSql = noValSql;
                }
            } catch (Exception e) {
                log.error("Exception caught", e);
            }
        }

        // 设置模块的更新行数据
        module.Updrow = (updrow);

        // 处理BillModule的特殊逻辑
        if (module instanceof BillModule && module.PopBtn != null && updrow != null) {
            BillModule billModule = (BillModule) module;
            Object rtagidObj = updrow.get(module.getMenuPrefix() + "rtagid");
            billModule.setBillFlag(rtagidObj != null ? ToInt32(rtagidObj.toString()) : 0);
        }


        response.setSuccess(true);
        return response;
    }

    //专属附件的方法
    private BaseResponse SetUpdateRowforf(ModuleBaseEntity module, String sql, String idValue) {
        BaseResponse response = new BaseResponse();
        Map<String, Object> updrow = null; // Java中用Map替代DataRow
        if (module == null) {
            return response;
        }

        String dataSql = "", noValSql = "";
        Map<String, Object> record = null;

        // 处理BaseModule类型
        if (module instanceof BaseModule) {
            BaseModule baseModule = (BaseModule) module;
            if (!isNullOrEmpty(baseModule.getMasterSql()) && !isNullOrEmpty(idValue)) {
                record = new HashMap<>();
                record.put(module.getIdField(), idValue);
                dataSql = DealQuerySqlfor(
                        isNullOrEmpty(sql) ? baseModule.getMasterSql() : sql,
                        record,
                        module.getLeftRecord(),
                        null,
                        module.getIdField(),
                        idValue,
                        true, false, false
                );
            }
        }
        // 处理BillModule类型
        else if (module instanceof BillModule) {
            BillModule billModule = (BillModule) module;
            if (isNullOrEmpty(idValue) && module.PopBtn != null) {
                dataSql = module.PopBtn.dllpar8;
            } else if (!isNullOrEmpty(billModule.getMasterSql()) && !isNullOrEmpty(idValue)) {
                record = new HashMap<>();
                record.put(module.getIdField(), idValue);
                dataSql = DealQuerySqlfor(
                        isNullOrEmpty(sql) ? billModule.getMasterSql() : sql,
                        record,
                        module.getLeftRecord(),
                        null,
                        module.getIdField(),
                        idValue,
                        true, false, false
                );
            }
        }

        // 构建noValSql
        if (!isNullOrEmpty(module.getMasterTable()) && !isNullOrEmpty(idValue)) {
            // 使用参数化SQL避免注入风险，这里先拼接（实际应使用占位符）
            noValSql = String.format("select top 1 * from %s where %s = ?",
                    module.getMasterTable(), module.getIdField());
        }

        // 校验SQL是否为空
        if (isNullOrEmpty(dataSql) && isNullOrEmpty(noValSql)) {
            response.setMsg(String.format(LanguageUtil.WrongMainCfg, "MasterTable Or MasterSql"));
            return response;
        }

        // 确定最终执行的SQL
        dataSql = isNullOrEmpty(dataSql) ? noValSql : dataSql;

        // 执行查询获取数据行
        try {
            // 执行SQL查询，获取结果集（这里假设dbOperator有对应的查询方法）
            if (databaseType.equals("dm")) dataSql = RegexUtil.processDmServerSql(dataSql);
            List<Map<String, Object>> resultList = jdbcTemplate.queryForList(dataSql); // 传递参数避免SQL注入
            if (resultList != null && !resultList.isEmpty()) {
                updrow = resultList.get(0); // 获取第一行数据
                // 转换列名为小写（模拟ToLowerColumnName）
                updrow = toLowerColumnName(updrow);
            }
        } catch (Exception e) {
            // 异常处理（根据实际需求添加日志）
//            this.Error($"根据主表sql获取模块[{module.ModuleId}][{idValue}]行数据出错!", e);
            log.error("Exception caught", e);
        }

        // 当主查询失败时尝试备用SQL
        if ((updrow == null || (updrow != null && updrow.size() > 1))
                && !dataSql.equals(noValSql)
                && !isNullOrEmpty(noValSql)) {
            try {
                List<Map<String, Object>> backupList = jdbcTemplate.queryForList(noValSql, idValue); // 传递参数
                if (backupList != null && !backupList.isEmpty()) {
                    updrow = backupList.get(0);
                    updrow = toLowerColumnName(updrow);
                    dataSql = noValSql;
                }
            } catch (Exception e) {
                log.error("Exception caught", e);
            }
        }

        // 设置模块的更新行数据
        module.Updrow = (updrow);

        // 处理BillModule的特殊逻辑
        if (module instanceof BillModule && module.PopBtn != null && updrow != null) {
            BillModule billModule = (BillModule) module;
            Object rtagidObj = updrow.get(module.getMenuPrefix() + "rtagid");
            billModule.setBillFlag(rtagidObj != null ? ToInt32(rtagidObj.toString()) : 0);
        }


        response.setSuccess(true);
        return response;
    }


    // 辅助方法：转换Map的key为小写
    private Map<String, Object> toLowerColumnName(Map<String, Object> row) {
        if (row == null) {
            return null;
        }
        Map<String, Object> lowerRow = new HashMap<>();
        for (Map.Entry<String, Object> entry : row.entrySet()) {
            lowerRow.put(entry.getKey().toLowerCase(), entry.getValue());
        }
        return lowerRow;
    }

    @Override
    public BaseResponse GetAttcData() throws UnsupportedEncodingException {
        String moduleId = Request("moduleId", ""),
                idValue = Request("idValue", ""),
                specNo = Request("specNo", "01"),
                stepCode = Request("stepCode", "");
        return GetAttcList(moduleId, idValue, specNo, stepCode);
    }

    /**
     * 获取附件列表
     *
     * @param moduleId 模块ID
     * @param idValue  标识符值
     * @param specNo   规格编号
     * @param stepCode 步骤代码
     * @return 包含附件列表的BaseResponse
     */
    public BaseResponse GetAttcList(String moduleId, String idValue, String specNo, String stepCode) throws
            UnsupportedEncodingException {
        // 处理idValue，移除可能的前后花括号
        idValue = (idValue == null) ? "" : idValue;
        if (idValue.startsWith("{") && idValue.endsWith("}")) {
            idValue = "";
        }

        BaseResponse response = new BaseResponse();
        if (specNo == "01") {
            List<Map<String, Object>> specDt = DataImpl.GetAttTreeData(moduleId, specNo);
            Map<String, Object> spectb = null;
            if (specDt != null && !specDt.isEmpty()) {
                spectb = toHashTable(specDt.get(0));
            }
            if (spectb != null) {
                specNo = get(spectb, "speciesno", "").toString();
            }
        }
        String dirTabId = DataImpl.GetAttcParentId(moduleId, idValue);

        // 获取附件文件列表（假设返回List<Map>，对应C#的DataTable）
        List<Map<String, Object>> attcFiles = DataImpl.GetAttcFiles(moduleId, idValue, dirTabId, specNo, stepCode);

        // 处理每个附件的路径和名称
        for (Map<String, Object> row : attcFiles) {
            // 获取文件名（优先vname，其次sname）
            String vname = row.get("vname") != null ? row.get("vname").toString() : "";
            String sname = row.get("sname") != null ? row.get("sname").toString() : "";
            String fileName = vname.isEmpty() ? sname : vname;
            String fileId = row.get("fileid") != null ? row.get("fileid").toString() : "";
            String webPath = row.get("webpath") != null ? row.get("webpath").toString() : "";

            // 生成webPath（如果为空）
            if (webPath.isEmpty()) {
                String absFilePath = DataImpl.GetAbsFilePath(fileId);
                String encodedPath = FileUtil.urlEncode(absFilePath, false);
                webPath = String.format("/%s/%s", WebConfigUtil_web.getFileVPath(), encodedPath);
            }

            // 处理文件名中的空格
            if (fileName.contains(" ")) {
                String encodedFileName = FileUtil.urlEncode(fileName, false);
                webPath = webPath.replace(encodedFileName, encodedFileName.replace("+", " "));
            }

            // 拼接域名（如果需要）
            String appDomain = getAppDomain(); // 从配置或上下文获取实际的appDomain
            if ((Boolean.parseBoolean(WebConfigUtil_web.get("useDbAttc", "false"))
                    && !DataImpl.isDefaultServer()
                    && !webPath.isEmpty())
                    || !appDomain.isEmpty()) {
                String dbVerPath = WebConfigUtil.getFileVPath() + "_" + getUser().getServerId();
                if (SiteUtil.containsVirtualDirectory(dbVerPath)) {
                    webPath = webPath.replace(WebConfigUtil.getFileVPath(), dbVerPath);
                } else {
                    webPath = trimEnd(getAppDomain(), '/') + webPath;
                }
            }
            // 更新行数据
            row.put("webpath", webPath);
            row.put("sname", fileName); // 移除特殊字符（如需要）
        }

//       sn(" response.setData(attcFiles);");
        // 设置响应数据
        response.setData(attcFiles);
        response.setOther(CheckAttcAuthory(moduleId, idValue));
        response.setSuccess(true);
        return response;
    }


    public ArrayList GetAuditHistoryInfo(ModuleBaseEntity module) {
        ArrayList itemList = new ArrayList<>();
        Boolean isBase = module instanceof BaseModule;
        Boolean WindowsDirver = toBoolean(Request("WindowsDirver", ""));
        List<Field> serchFields = null;
        List<Component> columns = null;
//        IPublicUtil util = new IPublicUtil();
        if (!isNullOrEmpty(module.getOverBackSql())) {
//            out.println("2222");
            List<Map<String, Object>> dtVal = DataImpl.GetCondition(module.getOverBackKey(), 0);
            serchFields = createControl.createControl(dtVal, module, true, module.getOverBackKey());
            columns = GetAuditStepColumns(module.getModuleId(), "999", "999", isBase);
        } else {
//            out.println("1111");
            String prefix = (module.getMenuPrefix() + "").toLowerCase();

            columns = new ArrayList<>();
//   serchFields 操作
            Column column1 = new Column();
            column1.text = isBase ? "数据标识号" : "单据号";
            column1.dataIndex = String.format("%s", module.getIdField());
            column1.setWidth(1);
            columns.add(column1);

            Column column2 = new Column();
            column2.text = "制单日期";
            column2.dataIndex = String.format("%soperatedate", prefix);
            column2.setXtype("datecolumn");
            column2.setFormat("yyyy-MM-dd");
            column2.setWidth(1);
            columns.add(column2);

            Column column3 = new Column();
            column3.text = "制单人";
            column3.dataIndex = String.format("%soperatorname", prefix);
            column3.setWidth(1);
            columns.add(column3);

            Column column4 = new Column();
            column4.text = "提交时间";
            column4.dataIndex = String.format("%soperatedate", prefix);
            column4.setXtype("datecolumn");
            column4.setFormat("yyyy-MM-dd");
            column4.setWidth(1);
            columns.add(column4);

            Column column5 = new Column();
            column5.text = "提交人";
            column5.dataIndex = String.format("%saffirmername", prefix);
            column5.setWidth(1);
            columns.add(column5);

            Column column6 = new Column();
            column6.text = "终审时间";
            column6.dataIndex = String.format("%sstepovertime", prefix);
            column6.setXtype("datecolumn");
            column6.setFormat("yyyy-MM-dd");
            column6.setWidth(1);
            columns.add(column6);

            Column column7 = new Column();
            column7.text = "终审人";
            column7.dataIndex = String.format("%sstepovername", prefix);
            column7.setWidth(1);
            columns.add(column7);

//   serchFields 操作
            serchFields = new ArrayList<Field>();

            DateField bdateField = new DateField(null);
            bdateField.setName("bdate");
            bdateField.setFieldLabel("终审时间起");
            bdateField.setFormat("yyyy-MM-dd");
            bdateField.setWidth(143);
            bdateField.setLabelWidth(72);
            serchFields.add(new LabelCheckBox(null, bdateField));

            DateField edateField = new DateField(null);
            edateField.setName("edate");
            edateField.setFieldLabel("终审时间止");
            edateField.setFormat("yyyy-MM-dd");
            edateField.setWidth(143);
            edateField.setLabelWidth(72);
            serchFields.add(new LabelCheckBox(null, edateField));

            DataStore store = new DataStore();
            Map<String, Object> comboExtraParams = new HashMap<>();
            comboExtraParams.put("moduleId", module.getModuleId());
            comboExtraParams.put("id", 0);
            comboExtraParams.put("fdtype", 99);
            comboExtraParams.put("textField", "UserName");
            store.extraParams = comboExtraParams;

            ComboBox comboBox = new ComboBox(null);
            comboBox.setName("stepovername");
            comboBox.setFieldLabel("终审人");
            comboBox.setWidth(143);
            comboBox.setLabelWidth(72);
            comboBox.setValueField("UserCode");
            comboBox.setDisplayField("UserName");
            comboBox.queryParam = "username";
            comboBox.store = store;

            serchFields.add(new LabelCheckBox(null, comboBox));

            TextField billcodeField = new TextField(null);
            billcodeField.setName("billcode");
            billcodeField.setFieldLabel(isBase ? "数据标识号" : "单据号"); // 根据isBase动态设置标签
            billcodeField.setWidth(143);
            billcodeField.setLabelWidth(72);
            serchFields.add(billcodeField);
        }

        List<Component> sFields = null;
        if (serchFields != null) {
            sFields = new ArrayList<>(serchFields);

            Button searchButton = new Button();
            searchButton.setText("查询");
            searchButton.setHandler("search");
            sFields.add(searchButton);

            Button backButton = new Button();
            backButton.setText("返审");
            backButton.setHandler("OnBack");
            sFields.add(backButton);
        }

//       GridPanel1
        GridPanel gridPanel1 = new GridPanel();
        gridPanel1.setColumns(columns);
        gridPanel1.IdField = module.getIdField();
        gridPanel1.TbarItems = sFields;
        gridPanel1.SHeight = DataImpl.GetConditionPanleHeight(module.getOverBackKey());
        if (WindowsDirver) {
            gridPanel1.MobileCards = null;
        } else {
            gridPanel1.MobileCards = GetAuditStepColumnCards(module.getModuleId(), "999", isBase);
        }

        // 创建并设置数据存储
        DataStore store1 = new DataStore();
        Map<String, Object> extraParams1 = new HashMap<>();
        extraParams1.put("stepId", -999);
        extraParams1.put("moduleId", module.getModuleId());
        store1.extraParams = extraParams1;
        gridPanel1.setStore(store1);

// 设置ElseValue属性（使用Map存储匿名对象属性）
        Map<String, Object> ElseValue = new HashMap<>();
        ElseValue.put("IsComplete", true);
        gridPanel1.elseValue = ElseValue;

// 将GridPanel1添加到itemList -- GridPanel1结束
        itemList.add(gridPanel1);

//       GridPanel2
        GridPanel gridPanel2 = new GridPanel();
        gridPanel2.IdField = module.getIdField();
        gridPanel2.title = "已完成单据明细";

        ArrayList columns2 = new ArrayList<>();
        // 1. 步骤代码列
        Column stepCodeColumn = new Column();
        stepCodeColumn.text = "步骤代码";
        stepCodeColumn.dataIndex = "stepcode";
        stepCodeColumn.setWidth(1);
        columns2.add(stepCodeColumn);

// 2. 步骤名称列
        Column stepNameColumn = new Column();
        stepNameColumn.text = "步骤名称";
        stepNameColumn.dataIndex = "stepname";
        stepNameColumn.setWidth(1);
        columns2.add(stepNameColumn);

// 3. 发生时间列（日期类型）
        Column happenTimeColumn = new Column();
        happenTimeColumn.text = "发生时间";
        happenTimeColumn.dataIndex = "happentime";
        happenTimeColumn.setXtype("datecolumn");
        happenTimeColumn.setFormat("yyyy-MM-dd");
        happenTimeColumn.setWidth(1);
        columns2.add(happenTimeColumn);

// 4. 操作时间列（日期类型）
        Column operTimeColumn = new Column();
        operTimeColumn.text = "操作时间";
        operTimeColumn.dataIndex = "opertime";
        operTimeColumn.setXtype("datecolumn");
        operTimeColumn.setFormat("yyyy-MM-dd");
        operTimeColumn.setWidth(1);
        columns2.add(operTimeColumn);

// 5. 操作人员列
        Column operatorNameColumn = new Column();
        operatorNameColumn.text = "操作人员";
        operatorNameColumn.dataIndex = "operatorname";
        operatorNameColumn.setWidth(1);
        columns2.add(operatorNameColumn);

// 6. 操作方向列
        Column operDirectionColumn = new Column();
        operDirectionColumn.text = "操作方向";
        operDirectionColumn.dataIndex = "operdirectionname";
        columns2.add(operDirectionColumn); // 未指定width，使用默认值

// 7. 操作意见列
        Column operAdviceColumn = new Column();
        operAdviceColumn.text = "操作意见";
        operAdviceColumn.dataIndex = "operadvice";
        operAdviceColumn.setWidth(1);
        columns2.add(operAdviceColumn);

// 8. 自动审核列
        Column autoAuditColumn = new Column();
        autoAuditColumn.text = "自动审核";
        autoAuditColumn.dataIndex = "autoaudit";
        autoAuditColumn.setWidth(1);
        columns2.add(autoAuditColumn);

        // 设置列配置到GridPanel
        gridPanel2.setColumns(columns2);

// 创建并配置数据存储
        DataStore store2 = new DataStore();
        Map<String, Object> extraParams2 = new HashMap<>();
        extraParams2.put("moduleId", module.getModuleId());
        extraParams2.put("stepId", -1);
        store2.extraParams = extraParams2;
        gridPanel2.setStore(store2);

// 添加到itemList
        itemList.add(gridPanel2);

        return itemList;
    }

    private Object GetAuditStepColumnCards(String moduleId, String s, Boolean isBase) {
        return null;
    }

    private List<Component> GetAuditStepColumns(String moduleId, String stepId, String stepCode, Boolean isBase) {
        List<Map<String, Object>> dtval = DataImpl.GetAuditStepColumns(moduleId, stepId, stepCode, getUser().UserId, isBase);
//        IPublicUtil util = new IPublicUtil();
        List<Component> list = dtval.stream()
                // 对应C#的select new RowColumn(row, false, util)
                .map(row -> new RowColumn(row, false, createControl))
                // 对应ToList<Component>()
                .collect(Collectors.toList());
        return list;
    }

    /**
     * API中的AddOrUpd方法
     */
    @Override
    public BaseResponse AddOrUpd() throws UnsupportedEncodingException, CusException {
        Boolean isadd = null;
        String add = Request("isadd", "") + "";
        int comfirmFlag = ToInt32(Request("comfirm", "") + "");
        if (!isNullOrEmpty(add)) {
            isadd = toBoolean(add);
        }
        return AddOrUpd(Request("ModuleId", ""), Request("datas", "") + "",
                Request("details", "") + "",
                Request("leftRecord", "") + "",
                toBoolean(Request("apply", "")),
                isadd != null ? isadd : toBoolean(add), comfirmFlag,
                toBoolean(Request("batch", ""))
        );
    }

    public BaseResponse AddOrUpd(String moduleId, String data, String details, String leftRecord,
                                 boolean apply, Boolean isAdd, int comfirmFlag, boolean batch) throws UnsupportedEncodingException, CusException {
        // 获取基础模块信息
        BaseModule module = GetBaseModule(moduleId, Request("menuId", ""));
        if (isNullOrEmpty(data) || "[]".equals(data)) {
            return new BaseResponse();
        }

        // 确保数据以数组格式存在
        data = data.startsWith("[") ? data : String.format("[%s]", data);
        ArrayList datas = (ArrayList) JSON.Decode(data);
        // 解析明细数据
        Map<String, Object> detailDatas = null;
        if (!isNullOrEmpty(details) && details.startsWith("{") && details.endsWith("}")) {
            detailDatas = (Map<String, Object>) JSON.Decode(details);
        }

        // 解析左侧记录数据
        Map<String, Object> leftRecordMap = null;
        if (!isNullOrEmpty(leftRecord) && leftRecord.startsWith("{")) {
            leftRecordMap = (Map<String, Object>) JSON.Decode(leftRecord);
        }
        // 调用核心处理方法
        return AddOrUpd(module, datas, detailDatas, leftRecordMap, apply, isAdd, comfirmFlag, batch);
    }

    /**
     * 新增或更新数据处理
     */
    @Transactional
    protected BaseResponse AddOrUpd(BaseModule module, List<Map<String, Object>> datas,
                                    Map<String, Object> details, Map<String, Object> leftRecord,
                                    boolean apply, Boolean isAdd, int comfirmFlag, boolean batch) throws UnsupportedEncodingException, CusException {
        final BaseResponse[] response = {new BaseResponse()};

        // 数据校验
        if (datas == null || datas.isEmpty()) {
            return response[0];
        }
        if (module == null) {
            return response[0];
        }

        String tableName = module.getMasterTable();
        if (tableName == null || tableName.isEmpty()) {
            response[0].setMsg(String.format("模块【%s】未配置主表名，请检查配置!", module.getModuleId()));
            return response[0];
        }

        // 获取表结构信息和自增字段
        List<Map<String, Object>> tableInfos = DataImpl.GetTableInfo(tableName);
        String identityField = GetIdentityField(tableInfos);

//        out.println("获取表结构信息和自增字段 ");
        // 获取列信息
        List<Map<String, Object>> dtColumns = GetColumnRows(module.getModuleId());

//        out.println("获取列信息 ");
        List<Object> retIds = new ArrayList<>();
        List<Object> applyDatas = new ArrayList<>();
        StringBuilder errMsg = new StringBuilder();

        if (module.getNewVer()) {
            // 遍历tableInfos查找匹配的行
            Map<String, Object> idTabRow = null;
            for (Map<String, Object> row : tableInfos) {
                String rowName = get(row, "name", null) + "".toLowerCase();
                if (rowName.equals(module.getIdField().toLowerCase())) {
                    idTabRow = row;
                    break;
                }
            }
            if (idTabRow != null) {
                idTabRow.put("isnullable", 0);
            }
        }
//        out.println("module.getNewVer() " + datas.size() + " " + datas);

        // 批量处理逻辑
        if ((datas.size() > 1 && batch) || (datas.size() >= 10 && toBoolean(WebConfigUtil.get("batchDetailDatas")))) {
//            out.println("批量处理逻辑 : 第一个判断");
            response[0] = BuildAndSaveAoUDatas(module, datas, dtColumns, leftRecord, tableInfos,
                    identityField, apply, isAdd, 0, comfirmFlag);
        } else {
//            out.println("批量处理逻辑 : 第二个判断");
            for (Map<String, Object> fRow : datas) {
                Map<String, Object> row = fRow;
                List<Map<String, Object>> copyChildRows = new ArrayList<>();
                boolean fromDrag = row.containsKey("$fdrag");
                boolean isCopy = row.containsKey("$copy") && toBoolean(row.get("$copy"));
                String updateChildrenVal = "";

                if (fromDrag && module.IsSpecModule) {
                    // 查找匹配的字段行
                    Map<String, Object> treeRow = dtColumns.stream()
                            .filter(r -> DataTableUtil.getRowVal(r, "fieldname", "").toString()
                                    .equalsIgnoreCase(module.LeftUnionField))
                            .findFirst()
                            .orElse(null);

                    Map<String, Object> tabTreeRow = tableInfos.stream()
                            .filter(r -> DataTableUtil.getRowVal(r, "name", "").toString()
                                    .equalsIgnoreCase(module.LeftUnionField))
                            .findFirst()
                            .orElse(null);

                    if (treeRow != null && tabTreeRow != null) {
                        Field field = new Field(treeRow);
                        int xtype;
                        if (databaseType.equals("dm")) {
                            xtype = ToInt32(PublicUtil.DMSqltypeToProType(tabTreeRow.get("xtype").toString()));
                        } else {
                            xtype = ToInt32(tabTreeRow.get("xtype"));
                        }
                        // 获取树节点的特定编号
                        String fieldValue = createControl.GetTreeSpecNo(
                                module.getMasterTable(),
                                module.LeftUnionField,
                                field.getValueField(),
                                field.getDataSource(),
                                row.get(module.LeftUnionField) + "",
                                PublicUtil.SqlxtypeToProType(xtype),
                                0
                        );

                        if (isCopy) {
                            String oldIdFieldValue = row.get(module.getIdField()) + "",
                                    oldSpecValue;
                            // 查询原始数据行
                            row = jdbcTemplate.queryForList(
                                    String.format("select * from %s where %s='%s'",
                                            module.getMasterTable(),
                                            module.getIdField(),
                                            oldIdFieldValue)
                            ).get(0);
                            oldSpecValue = row.get(module.LeftUnionField) + "";
                            row.put("$is_copy", true);
                            row.put("$old_" + module.getIdField(), module.getIdField());
                            // 查询子记录并处理
                            List<Map<String, Object>> childRows = jdbcTemplate.queryForList(
                                    String.format("select * from %s where %s like '%s_%%'",
                                            module.getMasterTable(),
                                            module.LeftUnionField,
                                            oldSpecValue)
                            );
                            for (Map<String, Object> childRow : childRows) {
                                String oldUnionVal = childRow.get(module.LeftUnionField) + "";
                                String newUnionVal = fieldValue + oldUnionVal.substring(oldSpecValue.length());
                                childRow.put(module.LeftUnionField, newUnionVal);
                                childRow.put("$is_copy", true);
                                childRow.put("$old_" + module.getIdField(), childRow.get(module.getIdField()));
                                copyChildRows.add(childRow);
                            }

                        } else {
                            row = fRow;
                            String currentUnionVal = row.get(module.LeftUnionField) + "";
                            if (!currentUnionVal.equals(fieldValue)) {
                                // 获取需要更新的子记录值
                                Object result = jdbcTemplate.queryForObject(
                                        String.format("select %s from %s where %s='%s'",
                                                module.LeftUnionField,
                                                module.getMasterTable(),
                                                module.getIdField(),
                                                row.get(module.getIdField())), Object.class
                                );
                                updateChildrenVal = result != null ? result.toString() : "";
                            }
                        }
                        if (row == null) continue;
                        if (row != null) row.put(module.LeftUnionField, fieldValue);

                    } else {
                        row = fRow;
                    }
                } else {
                    row = fRow;
                }

//                out.println("Datas判断 " +
//                        (datas.size() == 1 && details != null && !details.isEmpty()) + " " +
//                        (isCopy && copyChildRows != null && copyChildRows.size() > 0)
//                );
                if (datas.size() == 1 && details != null && !details.isEmpty()) {
                    int succ = 0;
                    response[0] = BuildAndSaveAoUData(
                            module, row, dtColumns, leftRecord, tableInfos,
                            identityField, apply, isCopy, 0, comfirmFlag
                    );

                    if (!response[0].isSuccess() && !response[0].getMsg().equals(LanguageUtil.GetString("NoneField"))) {
                        if (!isNullOrEmpty(response[0].getMsg())) {
                            errMsg.append(response[0].getMsg()).append("\n");
                        }
                    } else {
                        response[0].setSuccess(true);
                        response[0].setMsg(LanguageUtil.Success);
                        succ = BatchAddOrUpdDetail(module, row, details, isAdd, response[0]);
                        if (succ <= 0) {
                            errMsg.append(response[0].getMsg()).append("\n");
                        } else {
                            retIds.add(response[0].getOther());
                            module.MasterData = (row);
                            getEventHandler().callAfterModuleDataChange(
                                    module,
                                    isAdd == true ? SystemEnums.ActionType.Add : SystemEnums.ActionType.Update,
                                    response[0]);
                        }
                    }
                } else if (isCopy && copyChildRows != null && copyChildRows.size() > 0) {
                    // 使用JdbcTemplate的事务回调，确保操作在同一事务中
                    Map<String, Object> finalRow = row;
                    jdbcTemplate.execute((ConnectionCallback<Integer>) con -> {
                        int succ = 0;
                        // 构建并保存主数据（isCopy为true时传入isNew参数为true）
                        response[0] = BuildAndSaveAoUData(
                                module,
                                finalRow,
                                dtColumns,
                                leftRecord,
                                tableInfos,
                                identityField,
                                apply,
                                true,  // isCopy ? true : isAdd 此处isCopy为true，直接传true
                                0,
                                comfirmFlag
                        );

                        if (response[0].isSuccess()) {
                            // 收集返回的ID
                            retIds.add(response[0].getOther());

                            // 确保在子表操作前更新模块主数据
                            module.MasterData = (finalRow);  // 假设存在setter方法

                            // 触发数据变更后事件
                            getEventHandler().callAfterModuleDataChange(
                                    module,
                                    isAdd == true ? SystemEnums.ActionType.Add : SystemEnums.ActionType.Update,  // 注意枚举值大写（Java规范）
                                    response[0]
                            );

                            // 执行子表复制操作
                            BaseResponse childResponse = null;
                            try {
                                childResponse = AddOrUpd(
                                        module,
                                        copyChildRows,
                                        null,
                                        leftRecord,
                                        false,
                                        true,
                                        comfirmFlag,
                                        false
                                );
                            } catch (UnsupportedEncodingException e) {
                                throw new RuntimeException(e);
                            } catch (CusException e) {
                                throw new RuntimeException(e);
                            }
                            succ = childResponse.isSuccess() ? 1 : 0;
                        } else if (!isNullOrEmpty(response[0].getMsg())) {
                            // 拼接错误信息
                            errMsg.append(response[0].getMsg()).append("\n");
                        }
                        return succ;
                    });
                } else {
//                    out.println("Datas判断 最后一个else：start");
                    response[0] = BuildAndSaveAoUData(
                            module, row, dtColumns, leftRecord, tableInfos,
                            identityField, apply, isCopy, 0, comfirmFlag
                    );

//                    out.println("BuildAndSaveAoUData end" + response[0].isSuccess());
                    if (response[0].isSuccess()) {
                        retIds.add(response[0].getOther());
                        module.MasterData = (row);
                        getEventHandler().callAfterModuleDataChange(
                                module,
                                Optional.ofNullable(isAdd).orElse(false) ? SystemEnums.ActionType.Add : SystemEnums.ActionType.Update,
                                response[0]
                        );
                    }
                }

                if (response[0].isSuccess() && "99".equals(response[0].getOther() + "")) {
                    if ("99".equals(response[0].getData() + "")) {
                        response[0] = Deal99Response(response[0], null, module.getModuleId());
                    }
                    return response[0];
                }

                if (response[0].isSuccess() && apply) {
                    String applyModuleId = Request("applyModuleId", module.getModuleId());
                    response[0] = BaseApply(applyModuleId, module.IdValue);

                    if (response[0].isSuccess()) {
                        if (ToInt32(response[0].getOther()) == -10) {
                            applyDatas.add(response[0].getData());
                        }
                    } else if (ToInt32(response[0].getOther()) == 9) {
                        response[0].setSuccess(true);
                        Map<String, Object> applyData = new HashMap<>();
                        applyData.put("moduleId", module.getModuleId());
                        applyData.put("idValue", module.IdValue);
                        applyData.put("msg", response[0].getMsg());
                        applyDatas.add(applyData);
                    } else {
                        response[0].setSuccess(true);
                        errMsg.append(String.format("数据已添加，但提交失败！%s", response[0].getMsg())).append("\n");
                    }
                }

                if (response[0].isSuccess() && fromDrag && !isNullOrEmpty(updateChildrenVal)) {
                    jdbcTemplate.update(
                            String.format("update %s set %s='%s'||substring(%s, length('%s')+1) where position('%s' in %s)=1",
                                    module.getMasterTable(),
                                    module.LeftUnionField,
                                    row.get(module.LeftUnionField),
                                    module.LeftUnionField,
                                    updateChildrenVal,
                                    updateChildrenVal,
                                    module.LeftUnionField)
                    );
                }
            }
        }
        // 处理返回结果
        if (!applyDatas.isEmpty()) {
            Map<String, Object> data = new HashMap<>();
            data.put("code", response[0].getOther() != null ? response[0].getOther() : 0);
            data.put("datas", applyDatas);
            response[0].setData(data);
        }

        if (!retIds.isEmpty()) {
            response[0].setOther(retIds.size() == 1 ? retIds.get(0) : retIds);
        }

        if (errMsg.length() > 0) {
            response[0].setMsg(errMsg.toString());
        }

        return response[0];
    }

    /**
     * 提交或撤销操作
     *
     * @param moduleId  模块ID
     * @param idValue   标识值
     * @param stateEn   单据状态实体（可为null）
     * @param applyType 操作类型：1-提交，2-撤销
     * @return 基础响应对象
     */
    public BaseResponse BaseApply(String moduleId, String idValue, BillStateEn stateEn, int applyType) throws
            CusException {
        // 获取基础模块信息
        BaseModule module = GetBaseModule(moduleId, getMenuId());
        module.IdValue = (idValue);

        // 获取单据状态
        BillStateEn state = GetBillState(module, "");

        // 记录系统日志
        sysLog(String.format("%s->%s%s%s",
                        module.getMenuName(),
                        getUser().UserName,
                        applyType == 1 ? "提交" : "撤销",
                        module.IdValue),
                "提交数据");
        // 处理状态信息
        if (state != null) {
            state.StepCode = ("0");
            if (stateEn != null) {
                state.setSelectConfirmFlag(stateEn.getSelectConfirmFlag());
                state.nextSelectStepCode = (stateEn.nextSelectStepCode);
                state.nextSelectStepOper = (stateEn.nextSelectStepOper);
                state.comfirmFlag = (stateEn.comfirmFlag);
            }
        }

        // 调用状态变更前事件
        getEventHandler().callBeforeModuleStateChange(
                module,
                applyType == 1 ? SystemEnums.ActionType.Submit : SystemEnums.ActionType.EscSubmit,
                null
        );

        // 处理提交/撤销逻辑并获取响应
        BaseResponse response = Deal99Response(
                DataImpl.BaseApply(module, state, applyType),
                null,
                moduleId
        );

        // 调用状态变更后事件
        getEventHandler().callAfterModuleStateChange(
                module,
                applyType == 1 ? SystemEnums.ActionType.Submit : SystemEnums.ActionType.EscSubmit,
                response
        );

        return response;
    }

    /**
     * 重载方法，提供默认参数（applyType默认为1-提交）
     */
    public BaseResponse BaseApply(String moduleId, String idValue, BillStateEn stateEn) throws CusException {
        return BaseApply(moduleId, idValue, stateEn, 1);
    }

    /**
     * 重载方法，提供默认参数（stateEn默认为null，applyType默认为1-提交）
     */
    public BaseResponse BaseApply(String moduleId, String idValue) throws CusException {
        return BaseApply(moduleId, idValue, null, 1);
    }

    /// <summary>
    /// 生成批量保存sql并执行
    /// </summary>
    /// <param name="module"></param>
    /// <param name="rows"></param>
    /// <param name="dtColumns"></param>
    /// <param name="leftRecord"></param>
    /// <param name="dtTableColumns"></param>
    /// <param name="identityField"></param>
    /// <param name="apply"></param>
    /// <param name="isAdd"></param>
    /// <param name="count"></param>
    /// <param name="comfirmFlag"></param>
    /// <returns></returns>
    public BaseResponse BuildAndSaveAoUDatas(
            BaseModule module,
            List<Map<String, Object>> rows,
            List<Map<String, Object>> dtColumns,
            Map<String, Object> leftRecord,
            List<Map<String, Object>> dtTableColumns,
            String identityField,
            boolean apply,
            Boolean isAdd,
            int count,
            int comfirmFlag) {

        BaseResponse response = new BaseResponse();
        StringBuilder sqls = new StringBuilder();
        StringBuilder errs = new StringBuilder();
        boolean isAddFlag = false;
        int i = 0;

        // 遍历所有行生成SQL
        for (Map<String, Object> row : rows) {
            BaseResponse rowResponse = BuildAoUSaveSql(
                    module, row, dtColumns, leftRecord, dtTableColumns,
                    identityField, apply, isAdd, i
            );
            if (rowResponse.isSuccess()) {
                // 拼接SQL语句
                sqls.append(rowResponse.getData() != null ? rowResponse.getData().toString() : "");
                isAddFlag = rowResponse.getOther() != null && Boolean.parseBoolean(rowResponse.getOther().toString());
            } else {
                // 收集错误信息
                if (rowResponse.getMsg() != null) {
                    errs.append(rowResponse.getMsg());
                }
            }
            i++;
        }

        // 执行批量SQL
        if (sqls.length() > 0) {
            // 调用数据层保存方法（1=新增，2=修改）
            response = DataImpl.BaseDataSave(
                    module,
                    sqls.toString(),
                    isAddFlag ? 1 : 2,
                    comfirmFlag
            );

            // 记录系统日志
            String logMsg = String.format(
                    "%s->%s%s@#@%s=>%s",
                    module.getTitle(),
                    getUser().UserName,
                    isAddFlag ? "批量添加" : "批量修改",
                    sqls.toString(),
                    JSON.Encode(rows)
            );
            log.debug(String.valueOf(logMsg + "操作模块"));
        } else {
            // 无有效SQL时返回失败
            response.setSuccess(false);
            response.setMsg(LanguageUtil.GetString("NoneField"));
        }

        // 处理特殊返回值
        if (response.isSuccess() && "99".equals(response.getOther() != null ? response.getOther().toString() : "")) {
            return response;
        }

        // 合并错误信息
        if (errs.length() > 0) {
            String finalMsg = (response.getMsg() != null ? response.getMsg() : "") + errs.toString();
            response.setMsg(finalMsg);
        }

        return response;
    }

    private BaseResponse BuildAoUSaveSql(
            BaseModule module,
            Map<String, Object> row,
            List<Map<String, Object>> dtColumns,
            Map<String, Object> leftRecord,
            List<Map<String, Object>> dtTableColumns,
            String identityField,
            boolean apply,
            Boolean isAdd,
            int batchIndex) {

        BaseResponse response = new BaseResponse();
        StringBuilder errbder = new StringBuilder();
        String idField = module.getIdField();
        String idValue = row.getOrDefault(idField, "").toString();
        module.IdValue = (idValue);

        // 判断是否为新增操作
        boolean isAddFlag = CheckIsAdd(module); // 对应原CheckIsAdd方法
        if ((isAdd != null && isAdd) || isAddFlag) {
            isAddFlag = true;
            if (idField.equals(identityField)) {
                idValue = "";
                module.IdValue = ("");
                row.put(idField, "");
            }
        }

        // 设置模块主数据并触发数据变更前事件
        module.MasterData = (row);
        getEventHandler().callBeforeModuleDataChange(
                module,
                isAddFlag ? SystemEnums.ActionType.Add : SystemEnums.ActionType.Update,
                null
        );

        // 获取待操作字段列表
        List<FieldModel> fieldList = GetAOUFields_TabInfo(
                module, row, dtColumns, leftRecord, dtTableColumns,
                identityField, isAddFlag, null, batchIndex
        );

        // 收集字段验证错误信息
        StringBuilder errMsg = new StringBuilder();
        for (FieldModel field : fieldList) {
            if (!isNullOrEmpty(field.ValidMsg)) {
                if (errMsg.length() > 0) {
                    errMsg.append("<br>");
                }
                errMsg.append(field.ValidMsg);
            }
        }

        if (!errMsg.isEmpty()) {
            response.setSuccess(false);
            response.setMsg(errMsg.toString());
            return response;
        }

        // 旧版主键非自增且为空的校验
        if (isNullOrEmpty(module.IdValue)
                && !idField.equals(identityField)
                && !module.getNewVer()) {
            response.setSuccess(false);
            response.setMsg(String.format(
                    "旧版保存，主键值不能为空!请检查主键【%s】是否配置默认值！",
                    idField
            ));
            return response;
        }

        // 构建保存SQL
        StringBuilder updStrKeyHolder = new StringBuilder(); // 用于接收out参数
        String sql = BuildSaveSql(
                module, module.getMasterTable(), identityField,
                fieldList, isAddFlag, updStrKeyHolder
        );
        // 设置响应结果
        response.setSuccess(true);
        response.setData(sql);
        response.setOther(isAddFlag);

        return response;
    }

    /**
     * 根据表信息，将数据组装成SQL字段模型列表
     *
     * @param module           模块信息
     * @param source           数据源
     * @param dtColumns        配置的列
     * @param leftRecord       左边树或上级的选中行（用于获取默认值）
     * @param dtTableColumns   表所有的列
     * @param identityField    自动增长字段
     * @param isAdd            是否为添加操作
     * @param eqDtTableColumns 等效表列信息（可选）
     * @param batchIndex       批量操作索引（可选）
     * @return 字段模型列表
     */
    protected List<FieldModel> GetAOUFields_TabInfo(ModuleEntity module,
                                                    Map<String, Object> source,
                                                    List<Map<String, Object>> dtColumns,
                                                    Map<String, Object> leftRecord,
                                                    List<Map<String, Object>> dtTableColumns,
                                                    String identityField,
                                                    boolean isAdd,
                                                    List<Map<String, Object>> eqDtTableColumns,
                                                    int batchIndex) {
        String idField = module.getIdField();
//        out.println("筛选符合条件的列并转换为FieldModel " + source);
        Set<String> lowerCaseKeys = new HashSet<>();
        for (String key : source.keySet()) {
            lowerCaseKeys.add(key.toLowerCase()); // 统一转为小写
        }
        // 筛选符合条件的列并转换为FieldModel
        return dtTableColumns.stream()
                .filter(row -> {
//                    out.println("row + idField" + row + idField);
                    String colname = DataTableUtil.getStringValue(row, "name", "").toLowerCase();
                    int colstat = DataTableUtil.getIntValue(row, "colstat", 0);
                    boolean isComputed = DataTableUtil.getBooleanValue(row, "isComputed", false);
                    boolean isIdentity = DataTableUtil.getBooleanValue(row, "isIdentity", false);
//                    boolean condition = (colstat != 1 && colstat != 4) && !(
//                            isIdentity || isComputed ||  // 排除主键（isIdentity）和自动计算列（isComputed）
//                                    (!isAdd && (!source.containsKey(colname) || colname.equalsIgnoreCase(idField)))  // 修改时：跳过无改变字段、主键字段
//                    );
                    // 1. 提取列状态条件（基础条件）
                    boolean isValidColstat = (colstat != 1 && colstat != 4);

                    // 2. 提取需要排除的特殊列类型
                    boolean isSpecialColumn = isIdentity || isComputed;

                    // 3. 提取"修改模式下需要跳过的字段"条件
                    boolean isSkipInUpdateMode = !isAdd && (!lowerCaseKeys.contains(colname) || colname.equalsIgnoreCase(idField));

                    // 4. 组合最终条件（原表达式的逻辑）
                    boolean condition = isValidColstat && !(isSpecialColumn || isSkipInUpdateMode);
//                    out.println("condition " + condition + !isAdd + !lowerCaseKeys.contains(colname) + colname.equalsIgnoreCase(idField));
                    return condition;
                    // 排除条件：状态为1或4、自增列、计算列
//                    if (colstat == 1 || colstat == 4 || isIdentity || isComputed) {
//                        return false;
//                    }
//                    // 修改时，跳过未改变的字段和主键字段
//                    if (!isAdd) {
//                        boolean sourceContainsKey = source.containsKey(DataTableUtil.getStringValue(row, "name", ""));
//                        boolean isIdField = colname.equalsIgnoreCase(idField);
//                        if (!sourceContainsKey || isIdField) {
//                            return false;
//                        }
//                    }
//                    return true;
                })
                .map(row -> {
                    String colname = DataTableUtil.getStringValue(row, "name", "").toLowerCase();

                    // 查找配置列中对应的行
                    Map<String, Object> colrow = dtColumns.stream()
                            .filter(r -> DataTableUtil.getStringValue(r, "fieldname", "").toLowerCase().equals(colname))
                            .findFirst()
                            .orElse(null);

                    boolean isnullable = DataTableUtil.getBooleanValue(row, "isnullable", false);
                    int rowType;
                    if (databaseType.equals("dm")) {
                        rowType = PublicUtil.DMSqltypeToProType(DataTableUtil.getStringValue(row, "xtype", ""));
                    } else {
                        rowType = DataTableUtil.getIntValue(row, "xtype", 0);
                    }
//                    int rowType = DataTableUtil.getIntValue(row, "xtype", 0);
                    String tabDefaultVal = DataTableUtil.getStringValue(row, "text", "");
                    int length = DataTableUtil.getIntValue(row, "length", 0);

                    // 处理等效表列信息
                    if (eqDtTableColumns != null) {
                        Map<String, Object> eqRow = eqDtTableColumns.stream()
                                .filter(r -> DataTableUtil.getStringValue(r, "name", "").toLowerCase().equals(colname))
                                .findFirst()
                                .orElse(null);
                        if (eqRow != null) {
                            if (databaseType.equals("dm")) {
                                rowType = PublicUtil.DMSqltypeToProType(DataTableUtil.getStringValue(eqRow, "xtype", DataTableUtil.getStringValue(row, "xtype", "")));
                            } else {
                                rowType = DataTableUtil.getIntValue(eqRow, "xtype", rowType);
                            }
//                            rowType = DataTableUtil.getIntValue(eqRow, "xtype", rowType);
                        }
                    }

                    boolean cfgNullAble = true;
//                    String colCnName = colname;
                    String colCnName = DataTableUtil.getStringValue(row, "name", "");
                    Object fieldValue = get(source, colCnName, "");
//                    out.println(colCnName + " Object fieldValue " + fieldValue);
                    Field field = null;

                    if (colrow != null) {
                        field = new Field(colrow);
                        if (!field.getControlHidden()) {
                            cfgNullAble = !DataTableUtil.getBooleanValue(colrow, "Nullable", false);
                            colCnName = DataTableUtil.getStringValue(colrow, "FieldCaption", colname);
                        }
                    }

                    // 处理可空性
                    if (!isnullable && !tabDefaultVal.isEmpty() && cfgNullAble) {
                        isnullable = true;
                    }

                    // 获取数据类型
                    Class<?> dataType = PublicUtil.SqlxtypeToProType(rowType);

//                    out.println("获取数据类型 " + dataType);
                    // 配置列不存在且非添加操作且可空且值为空时，返回null
                    if (colrow == null && !isAdd && isnullable &&
                            (fieldValue == null || fieldValue.toString().trim().isEmpty())) {
                        return null;
                    }

                    // 处理字段值
                    if (colrow != null && field != null) {

                        // 树节点类型字段处理
                        if (isAdd && field.getFieldType() == SystemEnums.ControlType.LabTreeType.ordinal() &&
                                !isNullOrEmpty(field.getDataSource()) && leftRecord != null) {

                            String fieldValStr = fieldValue != null ? fieldValue.toString() : "";
                            boolean needGenerate = fieldValStr.isEmpty() || batchIndex > 0;

                            if (!needGenerate) {
                                // 检查值是否已存在
                                String checkSql = String.format("select count(1) from %s where %s='%s'",
                                        module.getMasterTable(), field.getName(), fieldValStr);
                                Integer count = jdbcTemplate.queryForObject(checkSql, Integer.class);
                                needGenerate = count != null && count > 0;
                            }
//                            out.println("树节点类型字段处理 " + needGenerate);
                            if (needGenerate) {
                                // 生成树节点编号
                                Object parentValue = leftRecord.get(field.getValueField());
                                String parentValStr = parentValue != null ? parentValue.toString() : "";
                                fieldValue = createControl.GetTreeSpecNo(
                                        module.getMasterTable(),
                                        field.getName(),
                                        field.getValueField(),
                                        field.getDataSource(),
                                        parentValStr,
                                        dataType,
                                        batchIndex
                                );
//                                out.println("生成树节点编号 " + fieldValue);
                            }
                        }
                        // ID字段默认值处理
                        else if (fieldValue == null || fieldValue.toString().trim().isEmpty() &&
                                colname.equalsIgnoreCase(idField)) {

                            String defaultVal = Objects.toString(field.getDefaultsource(), "");

//                            out.println("ID字段默认值处理 " + defaultVal);
                            if (defaultVal.toLowerCase().contains("parent.key")) {
                                // 替换parent.key为实际父字段
                                Map<String, Object> pkRow = dtColumns.stream()
                                        .filter(r -> DataTableUtil.getStringValue(r, "FieldType", "")
                                                .equals(String.valueOf(SystemEnums.ControlType.LabTreeType.ordinal())))
                                        .findFirst()
                                        .orElse(null);

                                if (pkRow != null) {
                                    String valueMember = DataTableUtil.getStringValue(pkRow, "ValueMember", "");
                                    defaultVal = defaultVal.toLowerCase().replace("{parent.key}",
                                            String.format("{parent.%s}", valueMember));
                                }
                            }

                            // 获取默认值
                            ModuleEntity tempModule = new ModuleEntity();
                            tempModule.setLeftRecord(leftRecord);
//                            out.println(" // 获取默认值 1");
                            fieldValue = createControl.GetDefaultValue(defaultVal, tempModule, SystemTypeEnums.PmType.store);
//                            out.println(" // 获取默认值 2");
                        }
                    }

                    // 设置模块ID值
                    if (colname.equalsIgnoreCase(idField) && isAdd) {
                        module.IdValue = (fieldValue != null ? fieldValue.toString() : "");
                    }

//                    out.println("设置模块ID值 ");
                    // 密码字段加密处理
                    if ("password".equals(colname) &&
                            fieldValue != null &&
                            fieldValue.toString().length() < 32) {
                        fieldValue = SafetyUtil.encryptPassword(fieldValue.toString());
                    }

//                    out.println("密码字段加密处理 ");
                    // 添加操作时，可空且值为空的字段跳过（主键除外）
                    if (isAdd && isnullable &&
                            (fieldValue == null || fieldValue.toString().trim().isEmpty()) &&
                            !colname.equalsIgnoreCase(idField)) {
                        return null;
                    }
//                    out.println("添加操作时，可空且值为空的字段跳过（主键除外） ");
                    // 创建字段模型
                    FieldModel model = new FieldModel();
                    model.IsNullAble = (isnullable);
                    model.FiledName = (colname);
                    model.dbType = (rowType);
                    model.ctype = (dataType);
                    model.FieldLength = (length);
//                    out.println("setFiledValue " + fieldValue);
                    model.setFiledValue(fieldValue);
//                    out.println("setFiledValue END");
                    // 验证非空
                    if (!cfgNullAble && (fieldValue == null || fieldValue.toString().trim().isEmpty())) {
                        model.ValidMsg = (colCnName + "不能为空!");
                    }

//                    out.println("验证非空 ");
                    // 图片类型字段处理
                    if (model.ValidMsg == null && field != null &&
                            (field.getFieldType() == SystemEnums.ControlType.LabPic.ordinal() ||
                                    field.getFieldType() == SystemEnums.ControlType.LabPicEx.ordinal())) {

                        String fieldVal = model.hasSpecVal ?
                                (model.pmFiledValue != null ? model.pmFiledValue.toString() : "") :
                                (model.getFiledValue() != null ? model.getFiledValue().toString() : "");

                        if (!fieldVal.isEmpty() && fieldVal.indexOf("%") < 0) {
                            // 去除首尾的双引号和单引号
                            String trimmedVal = fieldVal.replaceAll("^[\"']+", "").replaceAll("[\"']+$", "");
                            String[] fVal = trimmedVal.split(",");
                            for (int i = 0; i < fVal.length; i++) {
                                // 对应C#: string fvali = fVal[i], host = "";
                                String fvali = fVal[i];
                                String host = "";
                                // 对应C#: if (fvali.StartsWith("http"))
                                if (fvali != null && fvali.startsWith("http")) {
                                    try {
                                        // 对应C#: Uri uri = new Uri(fvali);
                                        URI uri = new URI(fvali);
                                        // 对应C#: host = $"{uri.Scheme}://{uri.Authority}";
                                        host = uri.getScheme() + "://" + uri.getAuthority();
                                        // 对应C#: fvali = HttpUtility.UrlDecode(uri.PathAndQuery).Replace(" ", "+");
                                        String pathAndQuery = uri.getRawPath() + (uri.getRawQuery() != null ? "?" + uri.getRawQuery() : "");
                                        fvali = URLDecoder.decode(pathAndQuery, StandardCharsets.UTF_8).replace(" ", "+");
                                        // 对应C#: OAUrl.Equals(host, StringComparison.OrdinalIgnoreCase) || ...
                                        boolean isHostMatch =
                                                // OAUrl和host忽略大小写相等
                                                getOAUrl().equalsIgnoreCase(host)
                                                        // host以OAUrl开头（忽略大小写）
                                                        || host.toLowerCase().startsWith(getOAUrl().toLowerCase())
                                                        // OAUrl以host开头（忽略大小写）
                                                        || getOAUrl().toLowerCase().startsWith(host.toLowerCase());
                                        if (isHostMatch) {
                                            host = ""; // 匹配则清空host
                                        }
                                    } catch (URISyntaxException e) {
                                        // URI解析失败时，保留原fvali，host为空（容错处理）
                                        log.error("Exception caught", e);
                                    }
                                }
                                // 对应C#: string[] vs = fvali.Split('?');
                                String[] vs = fvali != null ? fvali.split("\\?", 2) : new String[0]; // 分割为最多2部分，避免多个?的问题

                                // 对应C#: if (vs.Length > 1)
                                if (vs.length > 1) {
                                    // 对应C#: fVal[i] = $"{host}{FileUtil.UrlEncode(vs[0], false)}?{vs[1]}";
                                    try {
                                        fVal[i] = host + FileUtil.urlEncode(vs[0], false) + "?" + vs[1];
                                    } catch (UnsupportedEncodingException e) {
                                        throw new RuntimeException(e);
                                    }
                                } else {
                                    // 对应C#: fVal[i] = $"{host}{FileUtil.UrlEncode(vs[0], false)}";
                                    String encodeStr = null;
                                    try {
                                        encodeStr = vs.length > 0 ? FileUtil.urlEncode(vs[0], false) : "";
                                    } catch (UnsupportedEncodingException e) {
                                        throw new RuntimeException(e);
                                    }
                                    fVal[i] = host + encodeStr;
                                }

                                // 原C#注释的编码逻辑（如需启用，取消注释）
                                // fVal[i] = URLEncoder.encode(fVal[i].replace(" ", "%20"), StandardCharsets.UTF_8)
                                //         .replace("%5c", "/").replace("%2f", "/").replace("%3f", "?").replace("%3d", "=").replace("%26", "&");
                            }
                            model.setFiledValue(fVal);
                        }
                    }
//                    out.println("model " + JSON.Encode(model));
                    return model;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }


    protected boolean CheckIsAdd(ModuleEntity module) {
        if (module != null && !isNullOrEmpty(module.getMasterTable()) && !isNullOrEmpty(module.IdValue)) {
            String idWhere = module.IdValue;
            if (databaseType.equals("dm")) idWhere = RegexUtil.escapeSingleQuote(idWhere);
            idWhere = "and " + module.getIdField() + "= '" + idWhere + "'";
            return CheckIsAdd(module.getMasterTable(), idWhere);
        }
        return true;
    }

    protected boolean CheckIsAdd(String tabName, String idWhere) {
        if (!isNullOrEmpty(tabName) && !isNullOrEmpty(idWhere)) {
            String sql = String.format("select count(1) from %s where 1=1 %s", tabName, idWhere);
//            out.println("CheckIsAdd.SQL " + sql);
            if (databaseType.equals("dm")) sql = RegexUtil.processDmServerSql(sql);
            Object result = jdbcTemplate.queryForObject(sql, Object.class);
            return ToInt32(result) <= 0;
        }
        return true;
    }

    public String GetIdentityField(List<Map<String, Object>> tabInfo) {
        // 遍历数据表中的每一行
        for (Map<String, Object> row : tabInfo) {
            // 判断当前行是否为标识列（isIdentity为true）
            boolean isIdentity = toBoolean(get(row, "isIdentity", false));
            if (isIdentity) {
                // 获取列名并转为小写返回
                return get(row, "name", "") + "".toLowerCase();
            }
        }
        // 未找到标识列时返回null
        return null;
    }

    private BaseResponse BuildAndSaveAoUData(
            BaseModule module,
            Map<String, Object> row,
            List<Map<String, Object>> dtColumns,
            Map<String, Object> leftRecord,
            List<Map<String, Object>> dtTableColumns,
            String identityField,
            boolean apply,
            Boolean isAdd,
            int count,
            int comfirmFlag
    ) {
        BaseResponse response = new BaseResponse();
        String idField = module.getIdField();
        String idValue = get(row, idField, "").toString();
        module.IdValue = (idValue);

//        out.println("row " + row + " " + idField + " isAddFlag start: " + idValue);
        boolean isAddFlag = CheckIsAdd(module); // 需实现checkIsAdd方法
//        out.println("isAddFlag end: " + isAddFlag + " " + isAdd);

        // 处理添加/修改状态
        if ((isAdd != null && isAdd && !isAddFlag) || isAddFlag) {
            isAddFlag = true;
            if (idField.equalsIgnoreCase(identityField)) {
                idValue = "";
                module.IdValue = ("");
                row.put(idField, "");
            }
        }

        // 日志记录（实际使用时补充用户信息）
        sysLog(String.format("%s->%s%s%s", module.getTitle(), getUser().UserName, isAddFlag ? "添加" : "修改", idValue), "操作模块");

        module.MasterData = (row);
        getEventHandler().callBeforeModuleDataChange(
                module,
                isAddFlag ? SystemEnums.ActionType.Add : SystemEnums.ActionType.Update,
                null
        );

//        out.println("GetAOUFields_TabInfo 1");
        // 获取字段列表
        List<FieldModel> fieldList = GetAOUFields_TabInfo(
                module, row, dtColumns, leftRecord, dtTableColumns, identityField, isAddFlag, null, 0
        );
//        out.println("GetAOUFields_TabInfo 2" + JSON.Encode(fieldList));
        // 验证字段
        String errMsg = fieldList.stream()
                .filter(field -> field.ValidMsg != null && !field.ValidMsg.isEmpty())
                .map(field -> field.ValidMsg)  // 改用lambda表达式获取属性值
                .collect(Collectors.joining("<br>"));

        if (!errMsg.isEmpty()) {
            response.setMsg(errMsg);
            response.setSuccess(false);
            return response;
        }

        // 旧版验证
        if (module.IdValue == null || module.IdValue.isEmpty()) {
            if (!idField.equalsIgnoreCase(identityField) && !module.getNewVer()) {
                response.setSuccess(false);
                response.setMsg(String.format(
                        "旧版保存，非自增主键不能为空!请检查主键%s的【defaultdata】字段是否有效，或存储过程p_BaseSave是否有效!",
                        idField
                ));
                return response;
            }
        }

        // 构建保存SQL
        StringBuilder updStrKey = new StringBuilder();
        String opSql = BuildSaveSql(
                module, module.getMasterTable(), identityField, fieldList, isAddFlag, updStrKey
        );
        log.debug(String.valueOf("response+opSql: " + opSql));

        if (opSql != null && !opSql.isEmpty()) {
            response = DataImpl.BaseDataSave(module, opSql, isAddFlag ? 1 : 2, comfirmFlag);
//            out.println("response+opSql: " + JSON.Encode(response));
            // 日志记录（实际使用时补充用户信息）
            sysLog(String.format(
                    "%s->%s%s%s%s@#@%s=>%s",
                    module.getTitle(), getUser().UserName, isAddFlag ? "添加" : "修改", idValue,
                    response.isSuccess() ? "成功" : ("失败：" + response.getMsg()),
                    opSql, JSON.Encode(row)
            ), "操作模块");
        } else {
            response.setSuccess(false);
            response.setMsg(LanguageUtil.GetString("NoneField"));
        }

        // 特殊响应处理
        if (response.isSuccess() && "99".equals(response.getOther() + "")) {
            return response;
        }

        // 失败重试逻辑
        if (!response.isSuccess()) {
            if (response.getMsg().contains("已经存在编号为") && !isNullOrEmpty(module.IdValue) && count <= 1)//最多执行两次
            {
                module.IdValue = "";
                row.put(idField, "");
                return BuildAndSaveAoUData(module, row, dtColumns, leftRecord, dtTableColumns, identityField, apply, isAdd, ++count, comfirmFlag);
            } else if (response.getMsg().contains("EXECUTE 后的事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配") && count <= 1)//try again
            {
                return BuildAndSaveAoUData(module, row, dtColumns, leftRecord, dtTableColumns, identityField, apply, isAdd, ++count, comfirmFlag);
            }
            return response;
        }

        // 打印信息处理
        boolean hasPrint = response.getMsg() != null &&
                response.getMsg().toLowerCase().startsWith("printfile=");

        if (updStrKey.length() == 0) {
            response.setOther(idValue);
            module.IdValue = (idValue);
            if (response.isSuccess() && hasPrint) {
                // 核心调用代码
                response.setPrintInfo(GetPrintInfo(
                        module.getModuleId(),          // 对应C#的module.ModuleId
                        PRINT_FILE_PATTERN.matcher(response.getMsg()).replaceAll(""),  // 不区分大小写替换printfile=为空
                        row                            // 行数据参数
                ));
                response.setMsg(LanguageUtil.Success);
            }
            return response;
        }

        // 处理保存结果
        Object other = response.getOther();
        if (!(other instanceof Map)) {
            return response;
        }
        Map<String, UpdStrModule> saveResult = (Map<String, UpdStrModule>) other;
        UpdStrModule updStrModule = saveResult.get(updStrKey.toString());

        // 自增主键处理
        boolean getNewIdValue = true;
        if (isAddFlag && module.IdValue != null && !module.IdValue.isEmpty() &&
                updStrModule != null && !module.getIdField().equals(identityField) &&
                identityField != null && !identityField.isEmpty() &&
                updStrModule.getIdentityId() != null && !updStrModule.getIdentityId().isEmpty()) {

            // 使用参数化查询防止SQL注入
            String sql = "select top 1 " + identityField + " from " + module.getMasterTable() +
                    " where " + module.getIdField() + " = ?";

            // 执行查询并获取结果
            Object result = jdbcTemplate.queryForObject(sql, new Object[]{module.IdValue}, Object.class);

            // 转换为字符串（处理null情况）
            String identityVal = (result != null) ? result.toString() : "";

            if (identityVal != null && !identityVal.isEmpty()) {
                updStrModule.setIdValue(identityVal);
                updStrModule.setIdentityId(identityVal);
                getNewIdValue = false;
            }
        }

        // 保存字段处理
        boolean saveSuccess = SaveStrFieldByPms(fieldList, updStrModule);
        if (!hasPrint) {
            String msg = saveSuccess ?
                    LanguageUtil.Success :
                    "数据已保存，但因自增主键原因导致部分字段以及附件未保存成功，请重新修改保存！";
            response.setMsg(msg);
        }

        // 最终处理
        if (updStrModule != null && saveSuccess) {
            String newIdValue = getNewIdValue ? updStrModule.getIdValue() : module.IdValue;

            if (getNewIdValue && (isAddFlag || (module.IdValue == null || module.IdValue.isEmpty())) &&
                    !module.getIdField().equals(identityField) &&
                    identityField != null && !identityField.isEmpty() &&
                    updStrModule.getIdentityId() != null && !updStrModule.getIdentityId().isEmpty()) {

                // 使用参数化查询防止SQL注入
                String sql = "select " + module.getIdField() + " from " + module.getMasterTable() +
                        " where " + identityField + " = ?";

                // 执行查询并获取结果
                Object result = jdbcTemplate.queryForObject(sql, new Object[]{updStrModule.getIdentityId()}, Object.class);

                // 转换为字符串（处理null情况，与原代码"+ ""效果一致）
                newIdValue = (result != null) ? result.toString() : "";

                newIdValue = (newIdValue == null || newIdValue.isEmpty()) ? module.IdValue : newIdValue;
                module.IdValue = (newIdValue);
            }

            if (isAddFlag && (module.IdValue == null || module.IdValue.isEmpty()) &&
                    updStrModule.getIdValue() != null && !updStrModule.getIdValue().isEmpty()) {
                module.IdValue = (updStrModule.getIdValue());
            }

            module.IdentityId = (updStrModule.getIdentityId());
            response.setOther(newIdValue);
            row.put(module.getIdField(), module.IdValue);

            // 更新临时附件信息
            if (row.containsKey("temp_attachment_id")) {
                String attcTempId = row.get("temp_attachment_id") != null ?
                        row.get("temp_attachment_id").toString() : "";
                if (!attcTempId.isEmpty()) {
                    UpdateTempAttcInfo(module.getModuleId(), attcTempId, module.IdValue);
                }
            }

            if (response.isSuccess() && hasPrint) {
                response.setPrintInfo(GetPrintInfo(
                        module.getModuleId(),          // 对应C#的module.ModuleId
                        PRINT_FILE_PATTERN.matcher(response.getMsg()).replaceAll(""),  // 不区分大小写替换printfile=为空
                        row                            // 行数据参数
                ));
                response.setMsg(LanguageUtil.Success);
            }
        }

        return response;
    }

    private static final Pattern PRINT_FILE_PATTERN = Pattern.compile("printfile=", Pattern.CASE_INSENSITIVE);

    /**
     * 构建保存SQL语句
     *
     * @param module        模块实体
     * @param tabName       表名
     * @param identityField 自增字段名
     * @param fieldList     字段列表
     * @param isAdd         是否为新增操作
     * @param updStrKey     输出参数：更新标识键
     * @return 构建的SQL语句
     */
    protected static String BuildSaveSql(ModuleEntity module, String tabName, String identityField,
                                         List<FieldModel> fieldList, boolean isAdd, StringBuilder updStrKey) {
        String dataBaseName = WebConfigUtil_web.get("custom.database.type");
        boolean isDM = dataBaseName.equals("dm");
        log.debug(String.valueOf("BuildSaveSql"));
        String opsql = "";
        String updInfoSql = "";
        updStrKey.setLength(0); // 清空输出参数

        // 检查是否有特殊值字段
        boolean hasSpec = fieldList.stream().anyMatch(m -> m.hasSpecVal);

        // 构建更新信息SQL模板

        updStrKey.append(UUID.randomUUID().toString());
        updInfoSql = String.format(
                "select '%%s' %s, '%%s' %s, '%%s' %s, %%s %s, %%s %s;",
                UpdStrModule.GuidKeyName,
                UpdStrModule.TableNameName,
                UpdStrModule.IdFieldName,
                UpdStrModule.IdValueName,
                UpdStrModule.IdentityIdName
        );
//out.println("isAdd : " + isAdd);

        if (isAdd) {
            // 构建INSERT语句
            String sqlKey = fieldList.stream()
                    .map(m -> "[" + m.FiledName + "]")
                    .collect(Collectors.joining(","));

            String sqlValue = fieldList.stream()
                    .map(m -> {
//                        out.println(JSON.Encode(m));
                        Object val = m.SqlFieldVal;
                        return val != null ? val.toString() : "";
                    })
                    .collect(Collectors.joining(","));

            opsql = String.format("insert into %s (%s) values (%s);", tabName, sqlKey, sqlValue);

            // 处理非BillModule的情况
            if (!(module instanceof BillModule)) {
                if (!isNullOrEmpty(identityField)) {
                    opsql += !isDM ?
                            String.format(updInfoSql,
                                    updStrKey.toString(),
                                    tabName,
                                    identityField,
                                    "@@identity",
                                    "@@identity") :
                            String.format(updInfoSql,
                                    updStrKey.toString(),
                                    tabName,
                                    identityField,
                                    "@@identity",
                                    "@@identity");
                } else {
                    opsql += isDM ?
                            String.format(updInfoSql,
                                    updStrKey.toString(),
                                    tabName,
                                    module.getIdField(),
                                    "'" + module.IdValue + "'",
                                    "@@identity") :
                            String.format(updInfoSql,
                                    updStrKey.toString(),
                                    tabName,
                                    module.getIdField(),
                                    "'" + module.IdValue + "'",
                                    "@@identity");
                }
            }
        } else {
            // 构建UPDATE语句
//            String setClause = fieldList.stream()
//                    .map(m -> String.format("[%s]=%s", m.FiledName, m.SqlFieldVal))
//                    .collect(Collectors.joining(","));
            String setClause = fieldList.stream()
                    .map(m -> String.format("[%s]=%s",
                            m.FiledName,
                            (isNullOrEmpty(m.SqlFieldVal))
                                    ? m.pmFiledValue
                                    : m.SqlFieldVal))
                    .collect(Collectors.joining(","));
            log.debug(String.valueOf("BuildSaveSql(isAdd为false)setClause : " + setClause));
            if (isNullOrEmpty(setClause)) {
                return opsql;
            }

            opsql = String.format("update %s set %s where %s='%s';",
                    tabName,
                    setClause,
                    module.getIdField(),
                    module.IdValue);
            log.debug(String.valueOf("opsql(else) : " + opsql));
            if (hasSpec) {
                opsql += String.format(updInfoSql,
                        updStrKey.toString(),
                        tabName,
                        module.getIdField(),
                        "'" + module.IdValue + "'",
                        "'" + module.IdValue + "'");
            } else {
                updStrKey.setLength(0);
            }
        }
        log.debug(String.valueOf("opsql : " + opsql));
        return opsql;
    }

    /**
     * 获取打印信息
     *
     * @param moduleId  模块ID
     * @param printName 打印名称
     * @param record    记录数据
     * @return 打印信息哈希表
     */
    private Map<String, Object> GetPrintInfo(String moduleId, String printName, Map<String, Object> record) {
        BaseResponse response = new BaseResponse();
        Map<String, Object> retData = new HashMap<>();
        Map<String, Object> saveRec = new HashMap<>();

        // 获取打印SQL配置（转换为Hashtable并取第一条）
        List<Map<String, Object>> printSqls = DataImpl.GetPrintSqls(moduleId, "", DataImpl.IsBaseModule(moduleId));
        Map<String, Object> dtVal = printSqls != null && !printSqls.isEmpty() ? printSqls.get(0) : null;

        // 添加数据库服务器和数据库名信息
        retData.put("dbServer", WebConfigUtil_web.get("dbServer", ""));
        retData.put("dbName", ConfigUtil.toConDict(getUser().ConnectionString).get("databasename"));

        // 构建保存记录
        saveRec.put("dllcoid", moduleId);
        saveRec.put("printname", printName);
        saveRec.put("operatorname", getUser().UserName);
        saveRec.put("operatorid", getUser().UserId);

        // 获取模块信息并设置单据ID
        ModuleBaseEntity module = GetModule(moduleId);
        String idValue = String.valueOf(record.get(module.getIdField()));
        saveRec.put("billdocumentId", idValue);

        // 处理SQL参数
        if (dtVal != null) {
            for (Map.Entry<String, Object> entry : dtVal.entrySet()) {
                String key = entry.getKey();
                String value = String.valueOf(entry.getValue());
                if (key.startsWith("sql") && !value.isEmpty()) {
                    // 处理查询SQL
                    value = dealQuerySql(value, record, null, null, null, null, false, false, false);
                    saveRec.put(key, value);
                }
            }
        }
// 将Entry集合转换为Hashtable列表
        List<Map<String, Object>> hashtableList = new ArrayList<>();
        for (Map.Entry<String, Object> entry : saveRec.entrySet()) {
            Map<String, Object> hashtable = new HashMap<>();
            // 假设需要将键值对分别存入Hashtable的特定字段（根据实际业务调整字段名）
            hashtable.put("keyField", entry.getKey());  // 例如存入"key"字段
            hashtable.put("valueField", entry.getValue());  // 例如存入"value"字段
            hashtableList.add(hashtable);
        }
        // 执行添加或更新操作
        response = AddOrUpdTable(hashtableList, "p_systemWebPrintTab", "id", true, null);
        if (!response.isSuccess()) {
            response.setMsg("操作失败，请重试！");
            return null;
        }

        // 添加打印ID到返回数据
        retData.put("printId", Objects.toString(response.getOther(), ""));

        return retData;
    }

    /**
     * 添加或更新表数据
     *
     * @param datas    数据列表（每个元素为一行数据的Hashtable）
     * @param tabName  表名
     * @param idFields ID字段（多个用逗号分隔）
     * @param isAdd    是否强制添加（null表示自动判断）
     * @param onFinish 操作完成后的回调函数
     * @return 操作结果响应
     */
    public BaseResponse AddOrUpdTable(List<Map<String, Object>> datas, String tabName,
                                      String idFields, Boolean isAdd,
                                      BiConsumer<Map<String, Object>, Boolean> onFinish) {
        BaseResponse response = new BaseResponse();
        if (tabName == null || tabName.isEmpty()) {
            return response;
        }

        // 获取表结构信息
        List<Map<String, Object>> dtTableColumns = DataImpl.GetTableInfo(tabName);
//        out.println("dtTableColumns不应该为空(sql没问题),这里是模拟数据库数据建立的 : " + dtTableColumns);
        String identityField = GetIdentityField(dtTableColumns);
//        out.println("identityField应该拿到dtTableColumns中的name给其赋值 : " + identityField);
        // 处理ID字段
        if (idFields == null || idFields.isEmpty()) {
            idFields = identityField;
        }
        String[] ids = idFields.toLowerCase().split(",");
        String[] identityFieldsArr = identityField.split(",");

        for (Map<String, Object> row : datas) {
            if (row == null) continue;

            // 判断是否为新增操作
            boolean isAddOperation = CheckIsAdd(tabName, BuildIdWhere(row, ids));
            if (Boolean.TRUE.equals(isAdd) && !isAddOperation) {
                isAddOperation = true;
                // 清空ID字段值以触发新增
                for (String id : ids) {
                    if (row.containsKey(id)) {
                        row.put(id, "");
                    }
                }
            }

            List<FieldModel> fieldList = BuildActionField(
                    row, dtTableColumns, tabName,
                    isAddOperation ? identityFieldsArr : ids,
                    isAddOperation
            );
//            out.println("构建字段模型列表 end" + fieldList);

            if (fieldList == null) continue;

            // 构建操作SQL并获取更新键
//            out.println("构建操作SQL并获取更新键 start");
            String[] updStrKey = new String[1];
            String actionSql = BuildActionSql(
                    fieldList,
                    row,
                    tabName,
                    identityField,
                    updStrKey,
                    isAddOperation ? identityField : idFields,
                    isAddOperation
            );
//            out.println("构建操作SQL并获取更新键 end " + actionSql);

            if (databaseType.equals("dm")) actionSql = RegexUtil.processDmServerSql(actionSql);

            // 执行SQL并处理结果
//            out.println("执行SQL并处理结果 1 : " + actionSql);
            List<List<Map<String, Object>>> dataSet = dbOperator.executeDataSet(actionSql);
            response.setSuccess(true);

//            out.println("执行SQL并处理结果 end " + dataSet);

            Map<String, UpdStrModule> saveResult = DataImpl.DecodeSaveResult(dataSet);
            if (saveResult == null) continue;

//            out.println(JSON.Encode(saveResult) + " " + updStrKey[0]);
            UpdStrModule updStrModule = saveResult.get(updStrKey[0]);
            boolean saveSuccess = SaveStrFieldByPms(fieldList, updStrModule);
            response.setSuccess(saveSuccess);

            if (updStrModule != null) {
                response.setOther(updStrModule.getIdValue());
                response.setMsg(saveSuccess ? LanguageUtil.Success : LanguageUtil.Fail);

                // 执行回调函数
                if (onFinish != null) {
                    onFinish.accept(row, saveSuccess);
                }
            }
        }

        return response;
    }

    public BaseResponse AddOrUpdTable(String data, String tabName, String idFields, Boolean
            isAdd, BiConsumer<Map<String, Object>, Boolean> onFinish) {
        BaseResponse response = new BaseResponse();
        data = data.startsWith("[") ? data : String.format("[%s]", data);
        List<Map<String, Object>> datas = (List<Map<String, Object>>) JSON.Decode(data);//这里必须传一个数组
        if (datas == null || datas.isEmpty()) return response;
        return AddOrUpdTable(datas, tabName, idFields, isAdd, onFinish);
    }

    /**
     * 构建ID条件语句
     *
     * @param record   数据记录哈希表
     * @param idFields ID字段数组
     * @return 拼接后的WHERE条件字符串（带前缀" and "，若为空则返回空字符串）
     */
    protected String BuildIdWhere(Map<String, Object> record, String[] idFields) {
        try {
            // 校验ID字段数组有效性
            if (idFields == null || idFields.length <= 0) {
                return "";
            }

            List<String> conditions = new ArrayList<>();
            for (String idf : idFields) {
                // 跳过空字段名
                if (idf == null || idf.trim().isEmpty()) {
                    continue;
                }

                // 获取字段值（不存在则为空字符串）
                Object valueObj = record.containsKey(idf) ? record.get(idf) : "";
                String value = valueObj != null ? valueObj.toString() : "";

                // 构建条件表达式：若值以"@@"开头则不加单引号，否则加单引号
                String condition;
                if (value.startsWith("@@")) {
                    condition = String.format("%s=%s", idf, value);
                } else {
                    condition = String.format("%s='%s'", idf, value);
                }
                conditions.add(condition);
            }

            // 拼接所有条件，若有条件则添加" and "前缀
            if (conditions.isEmpty()) {
                return "";
            } else {
                return " and " + String.join(" and ", conditions);
            }
        } catch (Exception e) {
            // 异常时返回空字符串
            return "";
        }
    }

    /**
     * 构建操作字段模型列表
     *
     * @param source         源数据哈希表
     * @param dtTableColumns 表结构信息（模拟DataTable，用List<Map>表示）
     * @param tabName        表名
     * @param ids            ID字段数组
     * @param isAdd          是否为新增操作
     * @return 字段模型列表
     */
    protected List<FieldModel> BuildActionField(Map<String, Object> source,
                                                List<Map<String, Object>> dtTableColumns,
                                                String tabName, String[] ids, boolean isAdd) {
        // 转换ID数组为小写Set，便于快速判断包含关系
        Set<String> idSet = Arrays.stream(ids)
                .map(String::toLowerCase)
                .collect(Collectors.toSet());

        return dtTableColumns.stream()
                .filter(row -> {
                    // 获取列名并转为小写
                    String colName = DataTableUtil.getStringValue(row, "name", "").toLowerCase();
                    // 判断是否为计算列
                    boolean isComputed = toBoolean(DataTableUtil.get(row, "isComputed", false));
                    // 判断是否为自增列
                    boolean isIdentity = toBoolean(DataTableUtil.get(row, "isIdentity", false));

                    // 过滤条件：排除ID字段、自增列、计算列；修改时还需排除源数据中不存在的字段
                    return !(idSet.contains(colName)
                            || isIdentity
                            || isComputed
                            || (!isAdd && !source.containsKey(colName)));
                })
                .map(row -> {
                    String name = DataTableUtil.getStringValue(row, "name", "").toLowerCase();
                    boolean isNullAble = toBoolean(DataTableUtil.get(row, "isnullable", false));
                    int length = Integer.parseInt(DataTableUtil.get(row, "length", 0).toString());
//                    int sqlType = DataTableUtil.getIntValue(row, "xtype", Types.VARCHAR);
                    int sqlType;
                    if (databaseType.equals("dm")) {
                        sqlType = PublicUtil.DMSqltypeToProType(Objects.toString(get(row, "xtype")));
                    } else {
                        sqlType = DataTableUtil.getIntValue(row, "xtype", Types.VARCHAR);
                    }
                    // 从源数据获取字段值，不存在则为空字符串
                    Object value = source.containsKey(name) ? source.get(name) : "";

                    // 处理字符串类型值（trim）
                    if (value instanceof String) {
                        value = ((String) value).trim();
                    }

                    // 处理空值默认值（操作人、操作时间等）
                    String valueStr = value != null ? value.toString() : "";
                    if (valueStr.isEmpty()) {
                        switch (name) {
                            case "operatorid":
                                value = getUser().UserId; // 假设存在user对象及getUserId()方法
                                break;
                            case "operatorname":
                                value = getUser().UserName; // 假设存在user对象及getUserName()方法
                                break;
                            case "operatedate":
//                                value = new Date(); // 使用当前时间
                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
                                value = sdf.format(new Date()); // 格式化当前时间
                                break;
                        }
                    }

                    // 转换SQL类型为Java类型
                    Class<?> rowType = PublicUtil.SqlxtypeToProType(sqlType);

                    // 创建字段模型
                    FieldModel model = new FieldModel();
                    model.ctype = (rowType);
                    model.FieldLength = (length);
                    model.IsNullAble = (isNullAble);
                    model.FiledName = (name);
                    model.setFiledValue(value);

                    // 验证字段长度
                    int[] valueLen = new int[1]; // 用数组存储引用类型的长度结果
                    if (!model.validLength(valueLen)) {
                        // 表字段长度不够的处理逻辑（原代码未实现，此处保持空实现）
                    }

                    return model;
                })
                .filter(Objects::nonNull) // 过滤空模型
                .collect(Collectors.toList());
    }

    /**
     * 构建操作SQL语句（新增或更新）
     *
     * @param fieldList     字段模型列表
     * @param source        源数据哈希表
     * @param tabName       表名
     * @param identityField 自增字段名
     * @param updStrKey     输出参数：更新操作的唯一标识键
     * @param idFields      ID字段（逗号分隔字符串）
     * @param isAdd         是否为新增操作
     * @return 构建的SQL语句
     */
    protected String BuildActionSql(List<FieldModel> fieldList,
                                    Map<String, Object> source,
                                    String tabName,
                                    String identityField,
                                    String[] updStrKey,  // 使用数组作为输出参数容器
                                    String idFields,
                                    boolean isAdd) {
        // 初始化输出参数（默认空字符串）
        updStrKey[0] = "";

        // 处理默认参数，Java不支持方法参数默认值，这里通过重载或直接赋值处理
        if (idFields == null) {
            idFields = "";
        }

        String updateSql = "";
        String[] ids = idFields.split(",");

        // 判断是否有特殊值字段
        boolean hasSpec = fieldList.stream().anyMatch(m -> m.hasSpecVal);

        updStrKey[0] = (UUID.randomUUID().toString());

        // 构建updInfoSql，使用%%s保留二次替换占位符
        String updInfoSql = String.format(
                "select '%%s' %s, '%%s' %s, '%%s' %s, '%%s' %s;",
                UpdStrModule.GuidKeyName,
                UpdStrModule.TableNameName,
                UpdStrModule.IdFieldName,
                UpdStrModule.IdValueName
        );
        if (isAdd) {
            // 处理插入逻辑
            String sqlKey = fieldList.stream()
                    .map(m -> "[" + m.FiledName + "]")
                    .collect(Collectors.joining(","));

            String sqlValue = fieldList.stream()
                    .map(m -> m.SqlFieldVal.toString())
                    .collect(Collectors.joining(","));

            updateSql = String.format("insert into %s (%s) values (%s);", tabName, sqlKey, sqlValue);

            // 构建updInfoSql2，注意第四个参数没有单引号
            String updInfoSql2 = String.format(
                    "select '%%s' %s, '%%s' %s, '%%s' %s, %%s %s;",
                    UpdStrModule.GuidKeyName,
                    UpdStrModule.TableNameName,
                    UpdStrModule.IdFieldName,
                    UpdStrModule.IdValueName
            );

            // 执行二次替换
            updateSql += String.format(updInfoSql2,
                    updStrKey[0],
                    tabName,
                    identityField,
                    "@@identity"
            );
        } else {
            // 处理更新逻辑
            updateSql = fieldList.stream()
                    .map(m -> String.format("[%s]=%s", m.FiledName, m.SqlFieldVal))
                    .collect(Collectors.joining(","));

            if (updateSql.isEmpty()) {
                return updateSql;
            }

            updateSql = String.format(
                    "update %s with(rowlock) set %s where 1=1 %s;",
                    tabName,
                    updateSql,
                    BuildIdWhere(source, ids)
            );

            if (hasSpec) {
                // 执行二次替换
                updateSql += String.format(updInfoSql,
                        updStrKey[0],
                        tabName,
                        idFields,
                        GetIdValues(source, ids)
                );
            } else {
                updStrKey[0] = ""; // 清空
            }
        }

        return updateSql;
    }

    /**
     * 获取ID字段对应的值，拼接成逗号分隔的字符串
     *
     * @param record   包含字段值的哈希表
     * @param idFields ID字段数组
     * @return 拼接后的ID值字符串，异常时返回空字符串
     */
    protected String GetIdValues(Map<String, Object> record, String[] idFields) {
        try {
            // 处理空数组情况
            if (idFields == null || idFields.length <= 0) {
                return "";
            }

            // 过滤非空字段，获取对应值并拼接
            return Arrays.stream(idFields)
                    .filter(idf -> idf != null && !idf.isEmpty()) // 排除空字段名
                    .map(idf -> {
                        // 从哈希表获取值，不存在则返回空字符串
                        Object value = record.containsKey(idf) ? record.get(idf) : "";
                        // 转换为字符串（处理null值）
                        return value != null ? value.toString() : "";
                    })
                    .collect(Collectors.joining(",")); // 用逗号拼接
        } catch (Exception e) {
            // 捕获所有异常，返回空字符串
            return "";
        }
    }

    /**
     * 将特殊字符串数据以pms形式保存
     *
     * @param fieldList 字段模型列表
     * @param module    更新字符串模块对象
     * @return 操作是否成功（true表示成功或无需操作，false表示执行失败）
     */
    protected boolean SaveStrFieldByPms(List<FieldModel> fieldList, UpdStrModule module) {
        String pmOpSql = "";
        if (module == null || !module.Valide()) {
            return true;
        }

        // 筛选出有特殊值的字段
        List<FieldModel> stringFields = fieldList.stream()
                .filter(m -> m.hasSpecVal)
                .collect(Collectors.toList());

        if (!stringFields.isEmpty()) {
            // 构建参数和SQL
            List<Object> params = new ArrayList<>();
            List<String> setClauses = new ArrayList<>();

            for (FieldModel field : stringFields) {
                setClauses.add(String.format("[%s] = ?", field.FiledName));
                params.add(field.getPmName()); // 添加参数值
            }

            pmOpSql = String.join(",", setClauses);

            if (!pmOpSql.isEmpty()) {
                pmOpSql = String.format(
                        "update %s set %s where 1=1 %s",
                        module.getTableName(),
                        pmOpSql,
                        BuildIdWhere(module.getIdField(), module.getIdValue())
                );
            }

            // 执行更新
            if (!pmOpSql.isEmpty()) {
                int affectedRows = jdbcTemplate.update(
                        pmOpSql,
                        params.toArray()
                );
                return affectedRows > 0;
            }
        }

        return true;
    }

    /**
     * 构建ID条件的WHERE子句片段
     *
     * @param idFields ID字段名，多个以逗号分隔
     * @param idVals   ID值，多个以逗号分隔，与字段一一对应
     * @return 构建好的WHERE条件片段（如" and id='1' and name='test'"），异常时返回空字符串
     */
    protected String BuildIdWhere(String idFields, String idVals) {
        try {
            // 分割字段名和值数组
            String[] ids = idFields.split(",");
            String[] vals = idVals.split(",");

            List<String> conditionList = new ArrayList<>();

            for (int i = 0; i < ids.length; i++) {
                // 获取当前索引对应的值，超出范围则用空字符串
                String currentVal = (vals.length > i) ? vals[i] : "";

                // 构建条件：如果值以@@开头则不加单引号，否则加单引号
                String condition;
                if (currentVal.startsWith("@@")) {
                    condition = String.format("%s=%s", ids[i], currentVal);
                } else {
                    condition = String.format("%s='%s'", ids[i], currentVal);
                }

                conditionList.add(condition);
            }

            // 拼接所有条件，用and连接
            String conditions = String.join(" and ", conditionList);

            // 如果有条件则添加前缀"and "，否则返回空字符串
            return conditionList.size() > 0 ? " and " + conditions : "";
        } catch (Exception e) {
            // 捕获所有异常，返回空字符串
            return "";
        }
    }

    private void UpdateTempAttcInfo(String moduleId, String tempId, String newId) {
        try {
            // 获取临时附件文件信息
            List<Map<String, Object>> infos = DataImpl.GetTempAttcFiles(tempId);
            if (infos.isEmpty()) {
                return;
            }

            // 提取物种编号
            String specNo = String.valueOf(infos.get(0).getOrDefault("speciesno", ""));

            // 获取旧文件路径及目录ID
            String[] oldDirTabIdHolder = new String[1]; // 用于接收输出参数
            String oldFilePath = DataImpl.GetAcFileFolder(moduleId, tempId, specNo, oldDirTabIdHolder);
            String oldDirTabId = oldDirTabIdHolder[0];

            // 更新目录表中的父ID和名称
            String updateDirSql = "update P_fm_DirectoryTab set sname = ?, pid = ? where pid = ?";
            jdbcTemplate.update(updateDirSql, newId, newId, tempId);

            // 获取新文件路径及目录ID
            String[] dirTabIdHolder = new String[1]; // 用于接收输出参数
            String newFilePath = DataImpl.GetAcFileFolder(moduleId, newId, specNo, dirTabIdHolder);
            String dirTabId = dirTabIdHolder[0];

            // 处理文件路径信息
            StringBuilder fileName = new StringBuilder("temp.temp");
            StringBuilder oldRelPath = new StringBuilder(), oldWebPath = new StringBuilder();// 0:oldSavepath, 1:oldRelPath, 2:oldWebPath
            String oldSavepath = FileUtil.getFileSavePath(
                    oldFilePath, null, getAppDomain(), getAttcPath(),
                    fileName, oldRelPath, oldWebPath// 输出参数：pathInfo[1] = oldRelPath, pathInfo[2] = oldWebPath
            );

            // 记录系统日志
            sysLog(String.format(
                    "%s在模块%s记录%s移动附件%s",
                    getUser().UserName, moduleId, newId, fileName
            ), "移动临时附件");

            // 遍历处理每个文件
            for (Map<String, Object> row : infos) {
                String fname = String.valueOf(row.getOrDefault("sname", "")), newFName = fname;
                String parentid = String.valueOf(row.getOrDefault("parentid", ""));
                String oldrelpath = URLDecoder.decode(String.valueOf(row.getOrDefault("webpath", "")), StandardCharsets.UTF_8);

                //String sourcePath = oldRelPath.toString().replace(fileName, fname);
                String _filename = GetAttcFileName(moduleId, newId, fname);
                if (!isNullOrEmpty(_filename)) {
                    newFName = _filename;
                }
                // 移动文件
                BaseResponse response = FileUtil.moveTo(
                        oldRelPath.toString().replace(fileName, fname),
                        newFilePath, newFName, getAttcPath(), true
                );

                // 更新文件表记录
                if (!isNullOrEmpty(response.getData() + "") && (response.getData() + "" != "9")) {
                    String updateFileSql = "update P_fm_FileTab set parentid = ?, webpath = ? where fileId = ?";
                    jdbcTemplate.update(
                            updateFileSql,
                            dirTabId,
                            response.getData().toString(),
                            String.valueOf(row.getOrDefault("fileId", ""))
                    );
                }
            }

            // 删除历史遗留临时文件（使用参数化查询处理日期）
            String deleteOldFilesSql = "delete f from P_fm_FileTab f " +
                    "inner join P_fm_DirectoryTab p on f.parentid = p.dirId " +
                    "where CHARINDEX('temp_', p.pid) = 1 " +
                    "and p.CreateTime < ?; " +
                    "delete P_fm_DirectoryTab " +
                    "where CHARINDEX('temp_', pid) = 1 " +
                    "and CreateTime < ?";

            // 计算一天前的日期
            LocalDateTime oneDayAgo = LocalDateTime.now().minusDays(1);
            Timestamp timestamp = Timestamp.valueOf(oneDayAgo);

            jdbcTemplate.update(deleteOldFilesSql, timestamp, timestamp);

            // 删除临时目录
            String filePath = WebConfigUtil_web.getFilePath();
            FileUtil.deleteFloder(filePath, dirInfo ->
                    dirInfo.getName().startsWith("temp_") &&
                            dirInfo.lastModified() < System.currentTimeMillis() - 24 * 60 * 60 * 1000L
            );

        } catch (Exception e) {
            log.warn(String.valueOf("附件上传时出错" + e.getMessage()));
        }
    }

    /**
     * 获取附件文件名（核心逻辑，对应原C# GetAttcFileName）
     *
     * @param moduleId 模块ID
     * @param idVal    ID值
     * @param fileName 文件名
     * @return 处理后的附件名（无结果返回空字符串）
     */
    public String GetAttcFileName(String moduleId, String idVal, String fileName) throws SQLException {
        BaseResponse response = new BaseResponse();
        String proName = "p_GetAttcName";

        // 判断存储过程是否存在
        if (DataImpl.IsExitPro(proName)) {
            // 构造存储过程参数
            Object[] vals = new Object[]{moduleId, idVal, fileName, getUser().getUserId()};
            // 执行存储过程
            response = DataImpl.excuteStore(proName, vals);

            // 解析返回结果
            if (response.getData() != null) {
                // C# DataSet → Java List<List<Map<String, Object>>>（DataSet=表列表，Table=行列表）
                List<List<Map<String, Object>>> ds = (List<List<Map<String, Object>>>) response.getData();

                // 判断是否有数据表且有数据行
                if (ds != null && !ds.isEmpty() && !ds.get(0).isEmpty()) {
                    // 获取第一张表的第一行数据
                    Map<String, Object> firstRow = ds.get(0).get(0);

                    // 优先取"n"字段值
                    String newName = Objects.toString(firstRow.get("n"), "");

                    // "n"字段为空时，取第一列的值
                    if (newName == null || newName.trim().isEmpty()) {
                        // 获取第一列的列名（模拟ds.Tables[0].Columns[0].ColumnName）
                        String firstColumnName = firstRow.keySet().iterator().next();
                        newName = Objects.toString(firstRow.get(firstColumnName), "");
                    }

                    return newName;
                }
            }

            // ------------------- 注释内的备选存储过程调用逻辑（可选实现） -------------------
            /*
            // 获取存储过程参数列表
            List<DbParameter> pmList = dbOperator.getStoreParams(proName);

            // 创建输出参数和返回值参数
            DbParameter msgpm = dbOperator.getParameter("@msg", "", DbType.STRING, 5000, ParameterDirection.OUTPUT);
            DbParameter returnval = dbOperator.getParameter("@return", -1, DbType.INTEGER, 4, ParameterDirection.RETURN_VALUE);

            int i = 0;
            for (int j = 0; j < pmList.size(); j++) {
                DbParameter pm = pmList.get(j);
                if (pm.getDirection() == ParameterDirection.RETURN_VALUE) {
                    pmList.set(j, returnval);
                } else if (pm.getParameterName().equals("@msg")) {
                    pmList.set(j, msgpm);
                } else {
                    pm.setValue(vals[i]);
                    i++;
                }
            }

            // 执行存储过程获取DataSet
            List<List<Map<String, Object>>> ds = dbOperator.executeDataSet(
                proName,
                CommandType.STORED_PROCEDURE,
                pmList,
                proName
            );

            // 解析结果（和核心逻辑一致）
            if (ds != null && !ds.isEmpty() && !ds.get(0).isEmpty()) {
                Map<String, Object> firstRow = ds.get(0).get(0);
                String newName = Objects.toString(firstRow.get("n"), "");
                if (newName == null || newName.trim().isEmpty()) {
                    String firstColumnName = firstRow.keySet().iterator().next();
                    newName = Objects.toString(firstRow.get(firstColumnName), "");
                }
                return newName;
            } else {
                return "";
            }
            */
        }

        // 存储过程不存在/无结果时返回空字符串
        return "";
    }

    /**
     * 批量添加或更新明细数据
     *
     * @param module    基础模块实体
     * @param parentRow 父表数据
     * @param details   明细数据集合
     * @param isAdd     是否为新增操作
     * @param response  响应对象（引用传递）
     * @return 处理结果（1：成功，0：失败）
     */
    public int BatchAddOrUpdDetail(BaseModule module, Map<String, Object> parentRow,
                                   Map<String, Object> details, Boolean isAdd, BaseResponse response) throws
            UnsupportedEncodingException, CusException {
        // 遍历所有明细项
        for (Map.Entry<String, Object> entry : details.entrySet()) {
            String detailModuleId = entry.getKey();
            // 跳过明细ID字段
            if (detailModuleId.endsWith("_detailId")) {
                continue;
            }

            // 获取明细ID
            String detailIdKey = detailModuleId + "_detailId";
            Object detailIdObj = details.get(detailIdKey);
            String detailId = detailIdObj != null ? detailIdObj.toString() : "";

            if (detailId.isEmpty() || detailModuleId.isEmpty()) {
                continue;
            }

            // 转换明细数据为列表
            List<Map<String, Object>> detailData = (List<Map<String, Object>>) entry.getValue();
            List<Map<String, Object>> newDetailData = new ArrayList<>();

            if (detailData == null || detailData.isEmpty()) {
                continue;
            }

            // 获取明细模块信息
            BaseModule detalModule = GetBaseModule(detailModuleId, module.getMenuId());
            if (detalModule == null) {
                response.setSuccess(false);
                response.setMsg("未找到明细模块" + detailModuleId + "，添加失败");
                return 0;
            }

            // 获取明细模块详情
            detalModule.DetailModule = GetBaseDetailModuel(Integer.parseInt(detailId));
            if (detalModule.DetailModule == null) {
                continue;
            }

            // 处理关联字段
            String unionField = detalModule.DetailModule.getUnionField()
                    .replace("{", "")
                    .replace("}", "")
                    .toLowerCase();
            String unionParentFiled = detalModule.DetailModule.getUnionParentField()
                    .replace("{", "")
                    .replace("}", "")
                    .toLowerCase();

            if (unionParentFiled.isEmpty()) {
                unionParentFiled = module.getIdField();
            }

            // 获取表结构信息
            List<Map<String, Object>> tableInfos = DataImpl.GetTableInfo(detalModule.getMasterTable());
            String identityField = GetIdentityField(tableInfos);
            List<Map<String, Object>> dtColumns = GetColumnRows(detalModule.getModuleId());

            // 获取主键字段
            String valueMember = "";
            Optional<Map<String, Object>> pkRow = dtColumns.stream()
                    .filter(row -> {
                        Object fieldType = row.get("FieldType");
                        return fieldType != null &&
                                fieldType.toString().equalsIgnoreCase(String.valueOf(SystemEnums.ControlType.LabTreeType.ordinal()));
                    })
                    .findFirst();

            if (pkRow.isPresent()) {
                valueMember = pkRow.get().getOrDefault("ValueMember", "").toString();
            }

            // 构建列信息
            List<ColInfos> colInfos = BuildBillDetailColInfos(identityField, dtColumns, tableInfos, null);

            // 处理每条明细数据
            for (Map<String, Object> dData : detailData) {
                if (dData == null) {
                    continue;
                }

                StringBuilder errBuilder = new StringBuilder();
                List<FieldModel> fieldList = GetAOUFields_ColInfo(
                        module, colInfos, dData, null, valueMember, identityField,
                        isAdd != null ? isAdd : false, errBuilder);

                if (fieldList == null || fieldList.isEmpty()) {
                    continue;
                }

                // 设置关联字段值
                Object unionFieldValue = dData.get(unionField);
                if (unionFieldValue == null || unionFieldValue.toString().isEmpty()) {
                    dData.put(unionField, parentRow.get(unionParentFiled));
                }
                newDetailData.add(dData);
            }

            // 执行添加或更新操作
            BaseResponse detailResponse = AddOrUpd(
                    detalModule, newDetailData, null, parentRow, false, isAdd, 0, false);

            if (!detailResponse.isSuccess()) {
                response.setSuccess(false);
                response.setMsg("明细模块" + detailModuleId + "，添加失败" + detailResponse.getMsg());
                return 0;
            }

            // 处理特殊响应码
            if (response.isSuccess() && "99".equals(response.getOther() + "")) {
                return 0;
            }
        }

        return 1;
    }

    /**
     * 获取AOU字段的列信息
     */
    protected List<FieldModel> GetAOUFields_ColInfo(ModuleEntity module,
                                                    List<ColInfos> colList,
                                                    Map<String, Object> source,
                                                    Map<String, Object> leftRecord,
                                                    String identityField,
                                                    String treeValueMember,
                                                    boolean isAdd,
                                                    StringBuilder errBuilder) throws UnsupportedEncodingException {
        String idField = module.getIdField();
        List<FieldModel> fieldList = new ArrayList<>();

        for (ColInfos colInfo : colList) {
            if (colInfo == null) {
                continue;
            }

            String colname = colInfo.colname;
            Map<String, Object> colrow = colInfo.colrow; // 假设ColInfos中colrow为Map类型
            boolean isNullable = colInfo.isnullable;
            int length = colInfo.length;
            boolean cfgNullAble = colInfo.cfgNullAble;
            String colCnName = colInfo.colCnName;
            Field field = colInfo.field;
            Class<?> dataType = (Class<?>) colInfo.dataType;
            // 排除主键和自增主键
            if (colname.equalsIgnoreCase(identityField) ||
                    (!isAdd && (!source.containsKey(colname) || colname.equalsIgnoreCase(idField)))) {
                continue;
            }

            Object fieldValue = source.containsKey(colname) ? source.get(colname) : "";

            if (colrow != null && field != null) {
                // 处理树节点编号，避免重复
                if (isAdd && field.getFieldType() == SystemEnums.ControlType.LabTreeType.ordinal() &&
                        !isNullOrEmpty(field.getDataSource()) && leftRecord != null) {

                    if (!isNullOrEmpty(field.getDataSource()) &&
                            (isNullOrEmpty(fieldValue + "") ||
                                    ToInt32(jdbcTemplate.update(String.format("select count(1) from %s where %s='%s'",
                                            module.getMasterTable(),
                                            field.getName(),
                                            fieldValue + ""), Object.class)) > 0)) {

                        fieldValue = createControl.GetTreeSpecNo(module.getMasterTable(),
                                field.getName(),
                                field.getValueField(),
                                field.getDataSource(),
                                String.valueOf(leftRecord.get(field.getValueField())),
                                dataType, 0);
                    }
                }
                // 处理默认值
                else if (isNullOrEmpty(String.valueOf(fieldValue)) && colname.equals(idField)) {
                    String defaultVal = String.valueOf(field.getDefaultsource());
                    if (defaultVal.contains("parent.key") && !isNullOrEmpty(treeValueMember)) {
                        defaultVal = defaultVal.toLowerCase().replace("{parent.key}",
                                String.format("{parent.%s}", treeValueMember));
                    }
                    ModuleEntity tempModule = new ModuleEntity();
                    tempModule.setLeftRecord(leftRecord);
                    fieldValue = createControl.GetDefaultValue(defaultVal, tempModule, SystemTypeEnums.PmType.store);
                }
            }

            // 设置模块ID值
            if (colname.equals(idField) && isAdd) {
                module.IdValue = (String.valueOf(fieldValue));
            }

            // 密码加密处理
            if ("password".equals(colname) && String.valueOf(fieldValue).length() < 32) {
                fieldValue = SafetyUtil.encryptPassword(String.valueOf(fieldValue));
            }

            // 添加时跳过可为空且值为空的字段（主键除外）
            if (isAdd && isNullable && isNullOrEmpty(String.valueOf(fieldValue)) && !colname.equals(idField)) {
                continue;
            }

            // 构建字段模型
            FieldModel model = new FieldModel();
            model.IsNullAble = (isNullable);
            model.FiledName = (colname);
            model.dbType = (colInfo.dbType);
            model.ctype = (dataType);
            model.FieldLength = (length);
            model.setFiledValue(fieldValue);

            // 验证非空
            if (!cfgNullAble && isNullOrEmpty(String.valueOf(fieldValue))) {
                model.ValidMsg = (colCnName + "不能为空!");
            }

            // 处理图片字段URL编码
            if (!isNullOrEmpty(model.ValidMsg) && field != null &&
                    (field.getFieldType() == SystemEnums.ControlType.LabPic.ordinal() ||
                            field.getFieldType() == SystemEnums.ControlType.LabPicEx.ordinal())) {

                String fieldVal = model.hasSpecVal ? String.valueOf(model.pmFiledValue) : String.valueOf(model.getFiledValue());
                if (!isNullOrEmpty(fieldVal) && fieldVal.indexOf("%") < 0) {
                    // 处理引号和分割（替换原有的trim方法）
                    fieldVal = fieldVal.replaceAll("^[\"']+|[\"']+$", "");
                    String[] fVal = fieldVal.split(",");

                    for (int i = 0; i < fVal.length; i++) {
                        String[] vs = fVal[i].split("\\?");
                        if (vs.length > 1) {
                            fVal[i] = FileUtil.urlEncode(vs[0], false) + "?" + vs[1];
                        } else {
                            fVal[i] = FileUtil.urlEncode(vs[0], false);
                        }
                    }
                    model.setFiledValue(fVal);
                }
            }

            // 收集错误信息
            if (!isNullOrEmpty(model.ValidMsg)) {
                errBuilder.append(model.ValidMsg).append("\n");
            }

            fieldList.add(model);
        }

        return fieldList;
    }

    /**
     * 获取单据明细组装SQL需要的各类信息
     *
     * @param identityField    自增主键（C#的string）
     * @param dtColumns        列配置（C#的DataTable → Java的List<Map<String, Object>>）
     * @param dtTableColumns   数据库列配置（单据明细临时表）
     * @param eqDtTableColumns 比较的数据库列配置（单据明细正式表，可为null）
     * @return 列信息列表（List<ColInfos>）
     */
    protected List<ColInfos> BuildBillDetailColInfos(
            String identityField,
            List<Map<String, Object>> dtColumns,
            List<Map<String, Object>> dtTableColumns,
            List<Map<String, Object>> eqDtTableColumns) {

        // 1. 过滤dtTableColumns：排除colstat=1/4、计算列、自增列、自增主键列
        List<ColInfos> colInfos = dtTableColumns.stream()
                .filter(row -> {
                    // 列名（转为小写，对应C#的ToLower()）
                    String colname = Objects.toString(row.get("name"), "").toLowerCase();
                    // colstat转换为int（对应C#的ToInt32()）
                    int colstat = ToInt32(row.get("colstat"));
                    // 是否为计算列（isComputed）
                    boolean isComputed = toBoolean(row.get("isComputed"));
                    // 是否为自增列（isIdentity）
                    boolean isIdentity = toBoolean(row.get("isIdentity"));
                    // 排除条件：colstat=1/4、计算列、自增列、等于自增主键
                    return colstat != 1
                            && colstat != 4
                            && !isComputed
                            && !isIdentity
                            && !colname.equalsIgnoreCase(identityField);
                })
                // 2. 转换过滤后的行→ColInfos对象（对应C#的Select）
                .map(row -> {
                    String colname = Objects.toString(row.get("name"), "").toLowerCase();
                    int length = ToInt32(row.get("length")); // 列长度
                    String tabDefaultVal = Objects.toString(row.get("text"), ""); // 表默认值
                    boolean isnullable = toBoolean(row.get("isnullable")); // 数据库可空性
                    int rowType; //= ToInt32(row.get("xtype")); // 数据库类型（xtype）
                    if (databaseType.equals("dm")) {
                        rowType = PublicUtil.DMSqltypeToProType(DataTableUtil.getStringValue(row, "xtype", ""));
                    } else {
                        rowType = DataTableUtil.getIntValue(row, "xtype", 0);
                    }
                    // 3. 处理eqDtTableColumns：若不为null，从正式表列配置覆盖rowType和isnullable
                    if (eqDtTableColumns != null && !eqDtTableColumns.isEmpty()) {
                        // 查找正式表中与当前列名匹配的行（忽略大小写）
                        Map<String, Object> eqRow = eqDtTableColumns.stream()
                                .filter(r -> Objects.toString(r.get("name"), "").toLowerCase().equals(colname))
                                .findFirst()
                                .orElse(null);

                        if (eqRow != null) {
                            // 覆盖数据库类型和可空性（250122改的逻辑）
                            if (databaseType.equals("dm")) {
                                rowType = PublicUtil.DMSqltypeToProType(DataTableUtil.getStringValue(eqRow, "xtype", DataTableUtil.getStringValue(row, "xtype", "")));
                            } else {
                                rowType = DataTableUtil.getIntValue(eqRow, "xtype", rowType);
                            }
                            //rowType = ToInt32(eqRow.get("xtype"));
                            isnullable = toBoolean(eqRow.get("isnullable"));
                        }
                    }

                    // 4. 处理dtColumns：获取列配置信息（colrow、field、cfgNullAble、colCnName）
                    boolean cfgNullAble = true; // 默认配置可空
                    String colCnName = colname; // 默认中文名为列名
                    Field field = null;
                    Map<String, Object> colrow = null;

                    if (dtColumns != null && !dtColumns.isEmpty()) {
                        // 查找dtColumns中与当前列名匹配的行（fieldname忽略大小写）
                        colrow = dtColumns.stream()
                                .filter(r -> Objects.toString(get(r, "fieldname", ""), "").toLowerCase().equals(colname))
                                .findFirst()
                                .orElse(null);

                        if (colrow != null) {
                            // 初始化Field对象（对应C#的new Field(colrow)）
                            field = new Field(colrow);
                            // 若控件未隐藏，读取配置的非空性和中文名称
                            if (!field.getControlHidden()) {
                                // cfgNullAble = !colrow.Get("Nullable", 0).ToBoolean()
                                cfgNullAble = !toBoolean(get(colrow, "Nullable", 0));
                                // 列中文名称（FieldCaption）
                                colCnName = Objects.toString(get(colrow, "FieldCaption", ""), colname);
                            }
                        }
                    }

                    // 5. 调整isnullable：若数据库列非空但有默认值，且配置可空→设为可空
                    if (!isnullable && !tabDefaultVal.isEmpty() && cfgNullAble) {
                        isnullable = true;
                    }

                    // 6. 数据库类型→Java数据类型（对应C#的SqlxtypeToProType）
                    Class<?> dataType = PublicUtil.SqlxtypeToProType(rowType);

                    // 7. 组装ColInfos对象并返回
                    ColInfos colInfo = new ColInfos();
                    colInfo.colname = (colname);
                    colInfo.colrow = (colrow);
                    colInfo.field = (field);
                    colInfo.length = (length);
                    colInfo.colCnName = (colCnName);
                    colInfo.cfgNullAble = (cfgNullAble);
                    colInfo.isnullable = (isnullable);
                    colInfo.dataType = (dataType);
                    colInfo.dbType = (rowType);

                    return colInfo;
                })
                // 8. 转换为List（对应C#的ToList()）
                .collect(Collectors.toList());

        return colInfos;
    }

    private BaseResponse Deal99Response(BaseResponse response, Map<String, Object> record, String moduleId) throws
            CusException {
        // message格式示例: 发现重要参数未进行填写，是否现在填写？;module=TEST001_01^Lskj.PubAdd3.dll^详细信息填写^1^0^TEST001_01^#select ' + QUOTENAME(@packageNo, '''') + ' as packageNo
        // message拆分说明: 提示信息;模块参数(^分隔)
        // 模块参数结构: 模块编号^调用模板dll^模块标题^调用标记^参数1^参数2^...
        if ("99".equals(String.valueOf(response.getOther()))) {
            String rMsg = response.getMsg();
            // 检查参数格式是否包含分隔符
            if (rMsg.indexOf(';') < 0) {
                response.setMsg("过程返回参数配置不正确！请检查！\n" + response.getMsg());
                return response;
            }

            // 拆分提示信息和模块参数
            String[] msgParts = rMsg.split(";", 2);
            response.setMsg(msgParts[0]);

            // 处理模块参数，不足部分用空字符串填充（最多14个参数）
            String[] params = (msgParts[1] + "^^^^^^^^^^^^").split("\\^"); // 12个^用于补充空参数

            // 构建右键菜单按钮对象
            SysPoPupMenuBtn btn = new SysPoPupMenuBtn();
            btn.MenuBtnId = 0;
            btn.dllname = getParamValue(params, 1);  // 调用模板dll
            btn.setText(getParamValue(params, 2));     // 模块标题
            btn.action = (getParamValue(params, 3));   // 调用标记
            btn.actiontype = 3;                      // 固定动作类型
            btn.dllpar1 = getParamValue(params, 4);  // 参数1
            btn.dllpar2 = getParamValue(params, 5);  // 参数2
            btn.dllpar3 = getParamValue(params, 6);  // 参数3
            btn.dllpar4 = getParamValue(params, 7);  // 参数4
            btn.dllpar5 = getParamValue(params, 8);  // 参数5
            btn.dllpar6 = getParamValue(params, 9);  // 参数6
            btn.dllpar7 = getParamValue(params, 10); // 参数7
            btn.dllpar8 = getParamValue(params, 11); // 参数8
            btn.dllpar9 = getParamValue(params, 12); // 参数9
            btn.dllpar10 = getParamValue(params, 13);// 参数10

            // 处理上下文菜单按钮
            DealContextMenuBtn(btn, record, null);

            // 执行菜单点击逻辑并设置返回数据
            response.setData(GoContextMenuClick(btn, record, null, moduleId, 0).getData());
        }
        return response;
    }

    /**
     * 安全获取参数数组中的值，避免数组越界
     */
    private String getParamValue(String[] params, int index) {
        return (index < params.length) ? params[index] : "";
    }

    private SysPoPupMenuBtn DealContextMenuBtn(SysPoPupMenuBtn
                                                       btn, Map<String, Object> record, Hashtable<String, Object> leftRecord) {
        // 处理leftRecord默认值（Java不支持方法参数默认值，通过重载或内部判断实现）
        if (leftRecord == null) {
            leftRecord = new Hashtable<>();
        }

        if (btn != null) {
            SystemTypeEnums.PmType pmtype = SystemTypeEnums.PmType.sql;

            // 根据actiontype确定参数处理类型
            switch (btn.actiontype) {
                case 0:
                    pmtype = SystemTypeEnums.PmType.sql;
                    break;
                case 1:
                    pmtype = SystemTypeEnums.PmType.store;
                    break;
                case 2:
                case 3:
                case 4:
                    pmtype = SystemTypeEnums.PmType.program;
                    break;
            }

            // 处理dllname
            btn.dllname = createControl.GetDefaultValue((btn.dllname == null ? "" : btn.dllname), record, leftRecord, SystemTypeEnums.PmType.store);

            // 处理action
            if (pmtype == SystemTypeEnums.PmType.store) {
                // 保持原值不变
            } else {
                btn.action = createControl.GetDefaultValue(btn.action, record, leftRecord,
                        pmtype == SystemTypeEnums.PmType.store ? SystemTypeEnums.PmType.ignorenull : pmtype);
            }

            // 处理所有参数
            btn.dllpar1 = createControl.GetDefaultValue(btn.dllpar1, record, leftRecord, pmtype);
            btn.dllpar2 = createControl.GetDefaultValue(btn.dllpar2, record, leftRecord, pmtype);
            btn.dllpar3 = createControl.GetDefaultValue(btn.dllpar3, record, leftRecord, pmtype);
            btn.dllpar4 = createControl.GetDefaultValue(btn.dllpar4, record, leftRecord, pmtype);
            btn.dllpar5 = createControl.GetDefaultValue(btn.dllpar5, record, leftRecord, pmtype);
            btn.dllpar6 = createControl.GetDefaultValue(btn.dllpar6, record, leftRecord, pmtype);
            btn.dllpar7 = createControl.GetDefaultValue(btn.dllpar7, record, leftRecord, pmtype);
            btn.dllpar8 = createControl.GetDefaultValue(btn.dllpar8, record, leftRecord, pmtype);
            btn.dllpar9 = createControl.GetDefaultValue(btn.dllpar9, record, leftRecord, pmtype);
            btn.dllpar10 = createControl.GetDefaultValue(btn.dllpar10, record, leftRecord, pmtype);

            // 处理其他属性
            btn.comfirm = createControl.GetDefaultValue(btn.comfirm, record, leftRecord, pmtype);
            btn.selectConfirmFlag = createControl.GetDefaultValue(btn.selectConfirmFlag, record, leftRecord, pmtype);
            btn.nextSelectStepCode = createControl.GetDefaultValue(btn.nextSelectStepCode, record, leftRecord, pmtype);
            btn.nextSelectStepOper = createControl.GetDefaultValue(btn.nextSelectStepOper, record, leftRecord, pmtype);
            btn.comfirmOpers = createControl.GetDefaultValue(btn.comfirmOpers, record, leftRecord, pmtype);
            btn.remark = createControl.GetDefaultValue(btn.remark, record, leftRecord, pmtype);

            // 处理PmStr
            if (btn.getPmStr() != null && !btn.getPmStr().trim().isEmpty()) {
                String pmStr = btn.getPmStr().trim().replaceAll("[\r\t]", "");
                if (pmStr.startsWith("#")) {
                    pmStr = pmStr.substring(1);
                }
                String pmSql = createControl.GetDefaultValue(pmStr, record, leftRecord, SystemTypeEnums.PmType.sql);

                try {
                    // 执行SQL并获取结果（假设dbOperator已注入且有对应方法）
                    Map<String, Object> resultMap = jdbcTemplate.queryForMap(pmSql);
                    if (resultMap != null) {
                        btn.Pms = (new Hashtable<>(resultMap));
                        btn.dllpar6 = "";
                    }
                } catch (Exception e) {
                    // 忽略异常
                }
            }
        }

        return btn;
    }

    /// <summary>
    /// 执行右键菜单按钮点击事件
    /// </summary>
    /// <param name="menuid">按钮id</param>
    /// <param name="moduleId">The module identifier.</param>
    /// <param name="rec">The record</param>
    /// <returns>BaseResponse</returns>
    /// </remarks>

    private BaseResponse GoContextMenuClick(SysPoPupMenuBtn btn, Map<String, Object> record,
                                            Map<String, Object> leftRecord, String moduleId, int menuid) throws CusException {
        BaseResponse response = new BaseResponse();
        if (btn != null) {
            btn.Record = record;
            btn.setModuleId(moduleId);

            switch (btn.actiontype) {
                case 0:
                    getEventHandler().callBeforeModuleContextMenu(btn, null);
                    response = ExcuteSql(btn);
                    getEventHandler().callAfterModuleContextMenu(btn, response);
                    break;

                case 1:
                    getEventHandler().callBeforeModuleContextMenu(btn, null);
                    response = Deal99Response(ExcuteStore(btn), record, moduleId);
                    if (response.isSuccess() && response.getMsg() != null && response.getMsg().toLowerCase().startsWith("printfile=")) {
                        String printFile = response.getMsg().replaceAll("(?i)printfile=", ""); // (?i)表示忽略大小写
                        response.setPrintInfo(GetPrintInfo(moduleId, printFile, record));
                        response.setMsg(LanguageUtil.Success);
                    }
                    getEventHandler().callAfterModuleContextMenu(btn, response);
                    break;

                case 2:
                case 3:
                case 4:
                    response.setSuccess(true);
                    boolean[] isUrl = new boolean[1]; // Java中用数组传递引用类型结果
                    Object width = btn.dllpar9;
                    Object height = btn.dllpar10;
                    String xtype = SystemMenu.convertToModuleName(btn.dllname, 1, isUrl);
                    String dbServer = null;
                    String dbName = null;
                    Object printCommand = ToPrintCommand(btn);
                    if (xtype.contains("pubprint") && isWindowsDirver()) {
                        dbServer = WebConfigUtil_web.get("dbServer", "");
                        Map<String, String> conDict = ConfigUtil.toConDict(getUser().ConnectionString);
                        dbName = conDict.get("databasename");

                        if (UpdateImpl.getVersion() >= 1035) {
                            Map<String, Object> saveRec = new HashMap<>();
                            saveRec.put("dllcoid", moduleId);
                            saveRec.put("printname", btn.dllpar2);
                            saveRec.put("operatorname", getUser().UserName);
                            saveRec.put("operatorid", getUser().UserId);
                            saveRec.put("contextmenuid", menuid);
                            saveRec.put("sql1", btn.dllpar3);
                            saveRec.put("sql2", btn.dllpar4);
                            saveRec.put("sql3", btn.dllpar6);
                            saveRec.put("sql4", btn.dllpar5);

                            List<Map<String, Object>> dataList = new ArrayList<>();
                            dataList.add(saveRec);
                            response = AddOrUpdTable(dataList, "p_systemWebPrintTab", "id", true, null);

                            if (!response.isSuccess()) {
                                response.setMsg("操作失败，请重试！");
                                return response;
                            }

                            Map<String, Object> responseData = new HashMap<>();
                            responseData.put("xtype", xtype);
                            responseData.put("title", btn.getText());
                            Map<String, Object> dllParms = new HashMap<>();
                            dllParms.put("printId", response.getOther());
                            dllParms.put("ModuleId", btn.getModuleId());
                            dllParms.put("IdValue", btn.getIdValue());
                            dllParms.put("Pms", btn.Pms);
                            dllParms.put("dbServer", dbServer);
                            dllParms.put("dbName", dbName);
                            responseData.put("DllParms", dllParms);
//                            responseData.put("DllParms", Map.of(
//                                    "printId", response.getOther(),
//                                    "moduleId", btn.getModuleId(),
//                                    "idValue", btn.getIdValue(),
//                                    "pms", btn.Pms,
//                                    "dbServer", dbServer,
//                                    "dbName", dbName
//                            ));
                            response.setData(responseData);
                            break;
                        }
                    }
                    // 处理权限相关类型
                    else if (xtype.contains("accraditation")) {
                        xtype = xtype.replace(".index", ".windowcard");
                    }
                    // 处理单据相关类型
                    else if (xtype.contains("pubbill.index")) {
                        xtype = xtype.replace(".index", ".billAdd");
                        btn.dllpar8 = "";
                        btn.dllpar9 = "";
                        width = "90%";
                        height = "80%";
                    } else if (xtype.contains("pubbill.sindex")) {
                        xtype = xtype.replace(".sindex", ".index");
                    }

                    // 设置窗口大小
                    if (!isNullOrEmpty(btn.getModuleId()) &&
                            (width == null || width.toString().isEmpty()) &&
                            (height == null || height.toString().isEmpty())) {

                        List<Map<String, Object>> winSizeTb = DataImpl.GetAuditWindowSize(btn.getModuleId());
                        if (winSizeTb != null && !winSizeTb.isEmpty()) {
                            Map<String, Object> row = winSizeTb.get(0);
                            width = (int) (ToInt32(row.get("width")) * 1.5);
                            height = (int) (ToInt32(row.get("height")) * 2);
                        }
                    }

                    // 构建返回卡片数据
                    Hashtable<String, Object> card = new Hashtable<>();
                    card.put("xtype", xtype);
                    card.put("href", isUrl[0] ? xtype : "");
                    card.put("isUrl", isUrl[0]);
                    card.put("title", btn.getText());
                    // 使用HashMap存储DllParms
                    Map<String, Object> dllParms = new HashMap<>();
                    dllParms.put("dllpar1", btn.dllpar1);
                    dllParms.put("dllpar2", btn.dllpar2);
                    dllParms.put("dllpar3", btn.dllpar3);
                    dllParms.put("dllpar4", btn.dllpar4);
                    dllParms.put("dllpar5", btn.dllpar5);
                    dllParms.put("dllpar6", btn.dllpar6);
                    dllParms.put("dllpar7", btn.dllpar7);
                    dllParms.put("dllpar8", btn.dllpar8);
                    dllParms.put("dllpar9", btn.dllpar9);
                    dllParms.put("dllpar10", btn.dllpar10);
                    dllParms.put("ModuleId", btn.getModuleId());
                    dllParms.put("IdValue", btn.getIdValue());
                    dllParms.put("ServerId", btn.getServerId());
                    dllParms.put("Pms", btn.Pms);
                    dllParms.put("width", width);
                    dllParms.put("height", height);
                    dllParms.put("dbServer", dbServer);
                    dllParms.put("dbName", dbName);
                    dllParms.put("maxWindow", btn.maxWindow);
                    dllParms.put("showMode", btn.showMode);
                    dllParms.put("PmOffset", btn.getPmOffset());
                    dllParms.put("printCommand", printCommand);
                    dllParms.put("printType", ToInt32(btn.dllpar2));
                    card.put("DllParms", dllParms);

                    // 处理浏览器相关逻辑
                    if (btn.dllname != null && btn.dllname.toLowerCase().contains("pubbrower")) {
                        btn.dllpar2 = isNullOrEmpty(btn.dllpar2) ? moduleId : btn.dllpar2;
                        String url = btn.dllpar3 + btn.dllpar2;
//                        String pmsstr = url.replace("\r\n", "").split("\\?").length > 1
//                                ? url.split("\\?")[1]
//                                : "";
                        String[] splitParts = url.replace("\r\n", "").split("\\?");
                        String pmsstr = splitParts.length > 0 ? splitParts[splitParts.length - 1] : "";
                        Pattern pattern = Pattern.compile("(?i)(?s)/(?:[^/]*?)(.*?)(?=\\.html)");
                        Matcher matcher = pattern.matcher(url);
                        String urlName = matcher.find() ? matcher.group(1) : "";

                        Hashtable<String, Object> pms = new Hashtable<>();
                        if (!isNullOrEmpty(pmsstr)) {
                            for (String str : pmsstr.split("&")) {
                                if (isNullOrEmpty(str) || str.indexOf('=') < 0) continue;
                                String[] kvs = str.split("=", 2); // 限制分割为2部分，避免值中包含=
                                String key = kvs[0];
                                String val = kvs.length > 1 ? kvs[1] : "";

                                if (!List.of("username", "password").contains(key)) {
                                    pms.put(key, val.replace("'", ""));
                                }
                            }
                        }
                        card.put("itemName", urlName);
                        card.put("QueryPms", pms);
                    }

                    response.setData(card);
                    break;
            }

            // 日志记录
            if (response.isSuccess() && (btn.actiontype == 1 || btn.actiontype == 2)) {
                sysLog(String.format("【%s】执行模块【%s】的右键【%s】参数：%s_%s_%s_%s_%s_%s_%s_%s_%s_%s",
                                getUser().UserName, moduleId, btn.getText(),
                                btn.action, btn.getPm1(), btn.getPm2(), btn.getPm3(), btn.getPm4(),
                                btn.getPm5(), btn.getPm6(), btn.getPm7(), btn.getPm8(), btn.getPm9(), btn.getPm10()),
                        "执行右键");
            }
        }
        return response;
    }

    /**
     * 执行SQL语句（适用于右键菜单为SQL的场景）
     *
     * @param btn 右键菜单按钮对象
     * @return 基础响应对象
     */
    private BaseResponse ExcuteSql(SysPoPupMenuBtn btn) {
        BaseResponse response = new BaseResponse();
        int returnValue = 0;
        String action = btn.action != null ? btn.action.trim() : "";

        if (action.isEmpty()) {
            response.setSuccess(false);
            response.setMsg(LanguageUtil.GetString("EmptySql"));
            return response;
        }

        // 判断SQL类型（查询或执行）
        String lowerAction = action.toLowerCase();
        if (lowerAction.startsWith("select") || lowerAction.startsWith("exec")) {
            // 处理查询语句
            List<Map<String, Object>> resultList = new ArrayList<>();
            try {
                resultList = jdbcTemplate.queryForList(action);
                returnValue = resultList.size();
                response.setData(resultList);
            } catch (Exception e) {
                response.setSuccess(false);
                response.setMsg(LanguageUtil.GetString("SqlExecuteError") + ": " + e.getMessage());
                return response;
            }

        } else {
            // 处理更新语句（insert/update/delete等）
            try {
                returnValue = jdbcTemplate.update(action);
            } catch (Exception e) {
                response.setSuccess(false);
                response.setMsg(LanguageUtil.GetString("SqlExecuteError") + ": " + e.getMessage());
                return response;
            }
        }

        // 设置响应结果
        response.setSuccess(true);//returnValue > 0);
        response.setMsg(response.isSuccess()
                ? LanguageUtil.GetString("Success")
                : LanguageUtil.GetString("Fail"));
        return response;
    }

    /**
     * 执行存储过程（适用于右键菜单为存储过程的场景）
     *
     * @param btn 右键菜单按钮对象
     * @return 基础响应对象
     */
    private BaseResponse ExcuteStore(SysPoPupMenuBtn btn) {
        BaseResponse response = new BaseResponse();

        try {
            // 解析参数名称
            List<String> paramName = PublicUtil.getParamValue(btn.action);
            String[] paramStr = paramName.get(0).toLowerCase()
                    .replace("{", "").replace("}", "").split(",");

            String storeName = btn.action.replace(paramName.get(0), "").trim();
            boolean pm1IsStore = false;

            if (storeName.isEmpty()) {
                storeName = btn.dllpar1;
                pm1IsStore = true;
            }

            // 获取存储过程参数
            DbOperator.Parameter[] pmList = dbOperator.getStoreParams(storeName);

            // 处理默认参数值
            String[] defPmValues = {
                    btn.dllpar1, btn.dllpar2, btn.dllpar3, btn.dllpar4, btn.dllpar5,
                    btn.dllpar6, btn.dllpar7, btn.dllpar8, btn.dllpar9, btn.dllpar10
            };

            if (pm1IsStore) {
                defPmValues = new String[]{
                        btn.dllpar2, btn.dllpar3, btn.dllpar4, btn.dllpar5, btn.dllpar6,
                        btn.dllpar7, btn.dllpar8, btn.dllpar9, btn.dllpar10
                };
            }

            String[] pmValues = new String[Math.max(pmList.length, 10)];
            System.arraycopy(defPmValues, 0, pmValues, 0, defPmValues.length);

            // 创建输出参数
            DbOperator.Parameter msgPm = dbOperator.getParameter(
                    "@msg", "", Types.VARCHAR, 32000, 2
            );

            DbOperator.Parameter returnVal = dbOperator.getParameter(
                    "@return", -1, Types.INTEGER, 4, 2 // -1 表示返回值
            );

            DbOperator.Parameter nextSelectStepCode = null;
            DbOperator.Parameter nextSelectStepOper = null;
            DbOperator.Parameter comfirmOpers = null;
            DbOperator.Parameter direction = null;
            DbOperator.Parameter auditAdvice = null;

            // 检查是否有特定参数
            boolean isSelectOper = Arrays.stream(pmList)
                    .anyMatch(p -> p.getName().equalsIgnoreCase("@selectconfirmflag"));

            int i = 0;
            for (int j = 0; j < pmList.length; j++) {
                DbOperator.Parameter pm = pmList[j];

                if (pm.getName().equals("@return") || pm.getName().equals("p_return_code") || pm.getName().equals("rtn_code")) { // 返回值参数
                    pmList[j] = returnVal;
                } else if (pm.getName().equals("@msg") || pm.getName().equals("p_msg") || pm.getName().equals("msg")) {
                    pmList[j] = msgPm;
                } else {
                    boolean isDM = ConfigUtil.getProviderName().equals("dm");
                    String name = "";
                    // 查找参数索引
                    if (isDM) name = pm.getName().toLowerCase().replace("p_", "@");
                    else name = pm.getName().toLowerCase();
                    int pmIndex = indexOf(paramStr, name);
                    if (pmIndex > -1) {
                        i = pmIndex;
                    }

                    // 处理特定参数
                    if (pm.getName().equalsIgnoreCase("@selectconfirmflag")) {
                        pm.setValue(btn.selectConfirmFlag == null || btn.selectConfirmFlag.isEmpty() ? "0" : btn.selectConfirmFlag);
                        continue;
                    } else if (pm.getName().equalsIgnoreCase("@comfirmflag")) {
                        pm.setValue(btn.comfirm == null || btn.comfirm.isEmpty() ? "0" : btn.comfirm);
                        continue;
                    } else if (pm.getName().equalsIgnoreCase("@nextselectstepcode")) {
                        pm.setValue(btn.nextSelectStepCode);
                        nextSelectStepCode = pm;
                        continue;
                    } else if (pm.getName().equalsIgnoreCase("@nextselectstepoper")) {
                        pm.setValue(btn.nextSelectStepOper);
                        nextSelectStepOper = pm;
                        continue;
                    } else if (pm.getName().equalsIgnoreCase("@comfirm_opers")) {
                        pm.setValue(btn.comfirmOpers);
                        comfirmOpers = pm;
                        continue;
                    } else if (pm.getName().equalsIgnoreCase("@auditadvice")) {
                        if (btn.remark != null && !btn.remark.isEmpty()) {
                            pm.setValue(btn.remark);
                        } else {
                            pm.setValue(pmValues[i]);
                        }
                        auditAdvice = pm;
                    } else if (pm.getType() == Types.INTEGER) {
                        pm.setValue(parseInt(pmValues[i]));
                    } else if (pm.getType() == Types.SMALLINT) {
                        pm.setValue(parseShort(pmValues[i]));
                    } else if (pm.getType() == Types.DECIMAL) {
                        pm.setValue(parseDouble(pmValues[i]));
                    } else if (pm.getType() == Types.BOOLEAN) {
                        pm.setValue(parseBoolean(pmValues[i]));
                    } else {
                        pm.setValue(pmValues[i]);
                    }

                    if (pm.getName().equalsIgnoreCase("@direction")) {
                        direction = pm;
                    }

                    i++;
                }
            }

            // 检查方向参数的特殊情况
            if (direction != null) {
                String dirValue = direction.getValue() != null ? direction.getValue().toString().toLowerCase() : "";
                if ("z".equals(dirValue) && comfirmOpers != null &&
                        (comfirmOpers.getValue() == null || comfirmOpers.getValue().toString().isEmpty())) {
                    response.setSuccess(true);
                    response.setOther("-11");
                    return response;
                } else if ("c".equals(dirValue) && auditAdvice != null &&
                        (auditAdvice.getValue() == null || auditAdvice.getValue().toString().isEmpty())) {
                    response.setSuccess(true);
                    response.setOther("-12");
                    return response;
                }
            }
            // 执行存储过程
            List<Map<String, Object>> resultSet = dbOperator.executeDMDataSet(storeName, pmList);
            int returnValue = -1;
            String msg = "";
            if (resultSet != null && !resultSet.isEmpty()) {
                returnValue = ToInt32(resultSet.get(0).get("returncode"));
                msg = Objects.toString(
                        getIgnoreCase(resultSet.get(0), "dm_msg", "msg"),
                        ""
                );
            }

            if (returnValue == -10 && isSelectOper && nextSelectStepOper != null && nextSelectStepCode != null) {
                response.setSuccess(true);
                response.setOther(String.valueOf(returnValue));

                // 这里省略了ModuleBase相关处理，实际应用中需要实现
                String nextSelOpersStr = "";
                if (nextSelectStepOper.getValue() != null) {
                    String value = nextSelectStepOper.getValue().toString();
                    if (value.contains(";")) {
                        StringBuilder sb = new StringBuilder();
                        String[] items = value.split(";");
                        for (String item : items) {
                            String[] parts = item.split(",");
                            // 去重处理
                            List<String> uniqueParts = Arrays.stream(parts)
                                    .distinct()
                                    .collect(Collectors.toList());
                            sb.append(String.join(",", uniqueParts)).append(";");
                        }
                        nextSelOpersStr = sb.toString();
                    } else {
                        String[] parts = value.split(",");
                        List<String> uniqueParts = Arrays.stream(parts)
                                .distinct()
                                .collect(Collectors.toList());
                        nextSelOpersStr = String.join(",", uniqueParts);
                    }
                }

                // 构建响应数据对象
                Map<String, Object> data = new HashMap<>();
                data.put("moduleId", btn.getModuleId());
                data.put("idValue", btn.getIdValue());
                data.put("selectConfirmFlag", 1);
                data.put("nextSelectStepCode", nextSelectStepCode.getValue() != null ? nextSelectStepCode.getValue().toString() : "");
                data.put("nextSelectStepOper", nextSelOpersStr);
                // 这里省略了其他数据的获取，实际应用中需要实现

                response.setData(data);
            } else if (returnValue == 99) {
                response.setMsg(msg);
                response.setSuccess(true);
                response.setOther(String.valueOf(returnValue));
            } else if (returnValue == 9) {
                response.setSuccess(true);
                response.setOther(String.valueOf(returnValue));
                response.setMsg(String.format("<p style='color:red;'>%s</p>", msg.replace("\r", "<br />")));
            } else if (returnValue != -1) {
                msg = msg.replace("\r", "<br />");
                response.setSuccess(true);
                response.setMsg(msg.isEmpty() || "1".equals(msg) || "null".equals(msg) ? LanguageUtil.GetString("Success") : msg);
                response.setData(msg);
            } else {
                response.setOther(String.valueOf(returnValue));
                response.setMsg(String.format("<p style='color:red;'>%s</p>", msg.replace("\r", "<br />")));
            }

        } catch (Exception e) {
            response.setSuccess(false);
            response.setMsg("执行存储过程出错: " + e.getMessage());
            log.error("执行存储过程出错", e);
        } finally {
            dbOperator.dispose();
        }

        return response;
    }

    private static Object getIgnoreCase(Map<String, Object> map, String... keys) {
        if (map == null || keys == null) {
            return null;
        }

        for (String key : keys) {
            if (map.containsKey(key)) {
                return map.get(key);
            }

            for (Map.Entry<String, Object> entry : map.entrySet()) {
                if (entry.getKey() != null && entry.getKey().equalsIgnoreCase(key)) {
                    return entry.getValue();
                }
            }
        }

        return null;
    }

    // 辅助方法：查找数组中元素的索引
    private int indexOf(String[] array, String value) {
        for (int i = 0; i < array.length; i++) {
            if (array[i].equals(value)) {
                return i;
            }
        }
        return -1;
    }

    // 类型转换辅助方法
    private Integer parseInt(String value) {
        try {
            return value == null || value.isEmpty() ? null : Integer.parseInt(value);
        } catch (NumberFormatException e) {
            return null;
        }
    }

    private Short parseShort(String value) {
        try {
            return value == null || value.isEmpty() ? null : Short.parseShort(value);
        } catch (NumberFormatException e) {
            return null;
        }
    }

    private Double parseDouble(String value) {
        try {
            return value == null || value.isEmpty() ? null : Double.parseDouble(value);
        } catch (NumberFormatException e) {
            return null;
        }
    }

    private Boolean parseBoolean(String value) {
        return value == null ? null : Boolean.parseBoolean(value);
    }

    /**
     * 修复：仅筛选 IN/INOUT 类型参数，严格排除 OUT/RETURN_VALUE 类型
     */
    private String[] getInParamNames(List<CustomSqlParameter> paramList) {
        List<String> inParamNames = new ArrayList<>();
        for (CustomSqlParameter param : paramList) {
            ParameterDirection direction = param.getDirection();
            // 只保留 IN（输入）和 INOUT（输入输出）类型的参数
            if (direction == ParameterDirection.INPUT || direction == ParameterDirection.INPUT_OUTPUT) {
                // 打印参数名，确认是否包含 @msg（正常应不包含）
                log.debug(String.valueOf("输入参数：" + param.getName()));
                inParamNames.add(param.getName());
            }
        }
        return inParamNames.toArray(new String[0]);
    }

    public BillStateEn GetBillState(ModuleEntity module, String stepCode) throws CusException {
        return DataImpl.GetBillState(module, stepCode);
    }

    public BillStateEn GetBillState(ModuleEntity module) throws CusException {
        return DataImpl.GetBillState(module, "");
    }

    @Override
    public BaseResponse Delete() {
        return Delete(Request("ModuleId", ""), Request("ids", ""));
    }

    /**
     * 删除模块数据
     *
     * @param moduleId 模块ID
     * @param ids      待删除ID，逗号分隔
     * @return 基础响应对象
     */
    @Transactional
    public BaseResponse Delete(String moduleId, String ids) {
        BaseResponse response = new BaseResponse();
        BaseModule module = GetBaseModule(moduleId, Request("menuId", "")); // 假设menuId为类中已定义的成员变量

        if (module == null) {
            return response;
        }

        if (ids == null || ids.trim().isEmpty()) {
            return response;
        }

        String[] idArray = ids.split(",");

        for (String id : idArray) {
            StringBuilder deleteSqlBuilder = new StringBuilder();

            // 处理特殊模块的删除逻辑
            if (module.IsSpecModule) {
                // 获取左关联字段类型
                Class<?> leftUnionFieldType = DataImpl.GetTableColumnType(module.getMasterTable(), module.LeftUnionField);

                if (leftUnionFieldType != null) {
                    // 值类型处理（数字类型）
                    if (leftUnionFieldType.isPrimitive() || Number.class.isAssignableFrom(leftUnionFieldType)) {
                        deleteSqlBuilder.append(String.format("""
                                        WITH ttype(%s,%s) AS (
                                            SELECT %s, %s FROM %s WHERE %s IN ('%s')
                                            UNION ALL
                                            SELECT A.%s, A.%s FROM %s A, ttype b WHERE a.%s = b.%s
                                        )
                                        DELETE FROM %s WHERE %s IN (SELECT %s FROM ttype);
                                        """,
                                module.getIdField(), module.LeftUnionField,
                                module.getIdField(), module.LeftUnionField, module.getMasterTable(), module.LeftUnionField, id,
                                module.getIdField(), module.LeftUnionField, module.getMasterTable(), module.LeftUnionField, module.getIdField(),
                                module.getMasterTable(), module.getIdField(), module.getIdField()
                        ));
                    } else {
                        // 非值类型处理（字符串类型）
                        String querySql = String.format(
                                "SELECT %s FROM %s WHERE %s IN (?)",
                                module.LeftUnionField, module.getMasterTable(), module.getIdField()
                        );
                        List<Map<String, Object>> specTab = jdbcTemplate.queryForList(querySql, id);

                        SqlAnalyzer sqlAnalyzer = new SqlAnalyzer(module.LeftUnionFieldSql);
                        String whereClause = sqlAnalyzer.OldWhere == null || sqlAnalyzer.OldWhere.isEmpty()
                                ? "WHERE 1=1 "
                                : sqlAnalyzer.OldWhere;

                        for (Map<String, Object> row : specTab) {
                            Object specObj = row.get(module.LeftUnionField);
                            if (specObj == null) {
                                continue;
                            }
                            String spec = specObj.toString();
                            if (!spec.isEmpty()) {
                                deleteSqlBuilder.append(String.format(
                                        "DELETE FROM %s %s AND %s LIKE '%s_%%';",
                                        module.getMasterTable(), whereClause, module.LeftUnionField, spec
                                ));
                            }
                        }
                    }
                }
            }

            // 基础删除语句
            deleteSqlBuilder.append(String.format(
                    "DELETE FROM %s WHERE %s IN ('%s');",
                    module.getMasterTable(), module.getIdField(), id
            ));

            // 执行删除前事件
            module.IdValue = (id);
            getEventHandler().callBeforeModuleDataDelete(module, response);

            // 执行删除操作
            String sql = deleteSqlBuilder.toString();
            response = DataImpl.BaseDataSave(module, sql, 3, 0);

            // 记录系统日志
            String logContent = String.format(
                    "%s删除%s模块数据:%s%s@#@%s",
                    getUser().UserName, module.getTitle(), id,
                    response.isSuccess() ? "成功" : "失败",
                    sql
            );
            sysLog(logContent, "操作模块");
//            out.println(logContent + "操作模块");
            // 删除关联附件
            DelAttcFile(moduleId, id);

            // 执行删除后事件
            getEventHandler().callAfterModuleDataDelete(module, response);
        }

        return response;
    }

    /**
     * 删除模块数据关联的附件文件
     *
     * @param moduleId 模块ID
     * @param idValue  数据ID
     * @return 操作响应结果
     */
    @Transactional
    public BaseResponse DelAttcFile(String moduleId, String idValue) {
        BaseResponse response = new BaseResponse();
        response.setSuccess(false);

        try {
            // 获取文件目录信息
            String[] dirTabId = new String[1];
            String fileFolder = DataImpl.GetAcFileFolder(moduleId, idValue, "", dirTabId);

            // 查询附件文件信息
            List<Map<String, Object>> filesInfo = DataImpl.GetAttcFiles(moduleId, idValue, dirTabId[0], null, null);

            for (Map<String, Object> row : filesInfo) {
                // 获取文件名（优先vname，其次sname）
                String name = get(row, "vname", get(row, "sname", "")) + "";

                // 获取文件编号
                String specno = get(row, "speciesno", "") + "";

                // 处理多路径情况
                if (!isNullOrEmpty(specno) && org.example.Impl.DataImpl.RelativePathPmsCount > 1) {
                    fileFolder = DataImpl.GetAcFileFolder(moduleId, idValue, specno, dirTabId);
                }

                // 获取并解码文件路径
                String webPath = get(row, "webPath", "") + "";
                String filePath = URLDecoder.decode(webPath, StandardCharsets.UTF_8);

                if (!isNullOrEmpty(name)) {
                    // 删除文件（物理删除）
                    FileUtil.deleteFile(filePath, getAttcPath());
                    FileUtil.deleteFile(fileFolder + "/" + name, getAttcPath());

                    // 删除数据库中的附件记录
                    String fileId = get(row, "fileId", "") + "";
                    DataImpl.DelAttcFileInfo(dirTabId[0], moduleId, "", name, fileId);

                    // 记录系统日志
                    String logContent = String.format(
                            "%s删除模块%s下%s的附件%s",
                            getUser().UserName, moduleId, idValue, name
                    );
                    sysLog(logContent, "删除附件");
//                    out.println(logContent + "删除附件");
                    response.setSuccess(true);
                }
            }
        } catch (Exception e) {
            response.setSuccess(false);
            response.setMsg("删除附件失败：" + e.getMessage());
            // 可添加日志记录异常
            // log.error("删除附件异常", e);
        }

        return response;
    }

    @Override
    public BaseResponse GetCondition() {
        return GetCondition(Request("ModuleId", ""), Request("detailId", ""));
    }

    /**
     * 检查是否存在指定的附件文件
     *
     * @param moduleId 模块ID
     * @param idValue  ID值
     * @param specNo   规格编号
     * @param filename 文件名
     * @return 是否存在该附件文件
     */
    public boolean CheckHasAttcFile(String moduleId, String idValue, String specNo, String filename) throws
            UnsupportedEncodingException {
        // 检查配置是否启用了附件重命名功能
        String configValue = WebConfigUtil.get("NAttcReName", "0");
        if (!Boolean.parseBoolean(configValue)) {
            return false;
        }

        // 获取文件目录信息
        String[] dirTabId = new String[1];
        String filePath = DataImpl.GetAcFileFolder(moduleId, idValue, specNo, dirTabId);

        StringBuilder fileName = new StringBuilder(filename);
        StringBuilder relPath = new StringBuilder(), webPath = new StringBuilder();
        String paths = FileUtil.getFileSavePath(filePath, null, getAppDomain(), getAttcPath(), fileName, relPath,
                webPath);
        // 查询是否存在该文件
        List<Map<String, Object>> exitFileRs = DataImpl.GetAttcFileInfo(dirTabId[0], moduleId, fileName.toString(), 0);
        return exitFileRs != null && !exitFileRs.isEmpty();
    }

    @Override
    public BaseResponse GetModuleCfg() {
        BaseResponse response = new BaseResponse();
        response.setData(GetModule(Request("moduleId")));
        response.setSuccess(true);
        return response;
    }

    @Override
    public BaseResponse GetModuleRightMenu() {
        List<Object> bbItems = null;
        BaseResponse response = new BaseResponse();
        response.setData(GetRightMenu(Request("ModuleId"), 0, new Ref(bbItems)));
        response.setSuccess(true);
        return response;
    }

    /**
     * 获取基础模块所有的明细数据
     *
     * @param moduleId  模块ID
     * @param idOrPRow  ID或父行标识
     * @param detailIds 明细ID集合
     * @return 包含明细数据的基础响应对象
     */
    @Override
    public BaseResponse GetModuleDetailsData(String moduleId, String idOrPRow, String detailIds) {
        BaseResponse response = new BaseResponse();

        // 获取基础模块
        BaseModule module = GetBaseModule(moduleId, getMenuId());
        if (module == null) {
            response.setMsg("未找到模块" + moduleId + ",请检查配置");
            return response;
        }

        // 获取明细模块数据
        String fromKey = (detailIds == null || detailIds.isEmpty()) ? module.getFromkey() : "";
        List<Map<String, Object>> dtVal = DataImpl.GetBaseDetailModuel(fromKey, detailIds);

        Map<String, Object> retDatas = new HashMap<>();
        int i = 0;

        // 遍历所有明细行
        for (Map<String, Object> row : dtVal) {
            // 避免对象被污染，除了第一行外重新获取模块实例
            if (i > 0) {
                module = GetBaseModule(moduleId, getMenuId());
            }

            BaseDetailModule dModule = new BaseDetailModule(row);
            retDatas.put(dModule.getDetailId(), GetModuleDetailData(module, dModule, idOrPRow, false, false, false, ""));

            i++;
        }

        response.setData(retDatas);
        response.setSuccess(true);

        return response;
    }

    /// <summary>
    /// Gets the audit history.
    /// </summary>
    /// <param name="moduleId">The module identifier.</param>
    /// <param name="idValue">The identifier value.</param>
    /// <returns>BaseResponse.</returns>
    @Override
    public BaseResponse GetAuditHistory(String moduleId, String idValue, String _isBase) {
        boolean isBase = true;
        if (!isNullOrEmpty(_isBase)) {
            isBase = toBoolean(_isBase);
        } else {
            ModuleBaseEntity module = GetBaseModule(moduleId, getMenuId());
            if (module == null) {
                isBase = false;
            }
        }

        BaseResponse response = new BaseResponse();
        response.setData(DataImpl.GetBaseFlowStep(idValue, isBase));
        response.setSuccess(true);

        return response;
    }

    /**
     * 获取附加模块，只有基础模块才有，这个只用在新版手机端订单列表上
     *
     * @param moduleId 模块ID
     * @param isCard   是否为卡片模式
     * @return 包含附加模块列表的响应对象
     */
    public BaseResponse GetAttachModules(String moduleId, boolean isCard) {
        BaseResponse response = new BaseResponse();
        List<BaseModule> rets = new ArrayList<>();

        // 添加主模块初始化参数
        rets.add(getModuleIniParams(moduleId, "", null, null, isCard
                , false, null, false, null, null, false));

        // 记录开始时间
        LocalDateTime startTime = LocalDateTime.now();
        // 添加附加模块列表
        rets.addAll(GetAttachModules(moduleId));

        // 设置响应数据
        response.setData(rets);
        response.setSuccess(true);
        return response;
    }

    /**
     * 获取附加模块列表（基于已有代码库实现）
     */
    protected List<BaseModule> GetAttachModules(String moduleId) {
        // 调用DataImpl获取附加模块数据列表
        List<Map<String, Object>> attcModules = DataImpl.GetAttcModules(moduleId);

        // 转换并过滤有效模块
        return attcModules.stream()
                .map(row -> {
                    // 从数据行提取字段值
                    String unionModule = get(row, "unionmodule", "").toString();
                    String title = get(row, "attachname", "").toString();
                    int orderId = ToInt32(get(row, "orderid", 0));

                    // 验证关联模块是否有效
                    if (!unionModule.isEmpty()) {
                        // 获取模块初始化参数（调用已存在的方法）
                        BaseModule module = getModuleIniParams(unionModule, "", null, null, true,
                                false, null, false, null, null, false);
                        module.setMenuName(title); // 使用MenuName字段存储标题（参考ModuleEntity的MenuName属性）

                        // 设置排序ID
                        if (orderId > 0) {
                            // 假设BaseModuleEntity有orderId字段及setter方法
                            module.OrderId = (orderId);
                        }
                        return module;
                    }
                    return null;
                })
                .filter(Objects::nonNull) // 过滤空值
                .collect(Collectors.toList());
    }

    /**
     * 添加附件文件信息
     *
     * @param fInfos 文件信息哈希表
     * @return 基础响应对象
     */
    public BaseResponse AddAttcFileInfo(Map<String, Object> fInfos) throws UnsupportedEncodingException {
        log.debug(String.valueOf("addattcfileInfo"));
        BaseResponse response = new BaseResponse();
        LocalDateTime startTime = LocalDateTime.now();

        // 提取文件信息参数
        String moduleId = fInfos.getOrDefault("menucode", "") + "";
        String idValue = fInfos.getOrDefault("value", "") + "";
        String fileNo = fInfos.getOrDefault("fileNo", "") + "";
        String specNo = fInfos.getOrDefault("specNo", "") + "";
        String stepCode = fInfos.getOrDefault("stepCode", "") + "";
        String[] dirTabId = new String[1]; // 用于接收out参数
        dirTabId[0] = fInfos.getOrDefault("dirTabId", "") + "";
        String filename = fInfos.getOrDefault("filename", "") + "";
        String totTime = fInfos.getOrDefault("totTime", "") + "";
        String filePath = DataImpl.GetAcFileFolder(moduleId, idValue, specNo, dirTabId); // 注意：Java无out参数，需调整实现
        int comfirm = ToInt32(fInfos.getOrDefault("comfirm", 0));

        // 构建保存路径并检查长度
        String attcPath = getAttcPath(); // 从父类获取附件路径
//        String utf8AttcPath = new String(getAttcPath().getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
//        String utf8FilePath = new String(filePath.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
//        String utf8FileNamePart = new String(Paths.get(filename).getFileName().toString().getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
//        String _savePath = Paths.get(utf8AttcPath, utf8FilePath, utf8FileNamePart).toString();
//        String _savePath = Paths.get(getAttcPath(), filePath, Paths.get(filename).getFileName().toString()).toString();
        String _savePath = String.format("%s/%s%s", attcPath, filePath, filename);
        String[] msgHolder = new String[1]; // 用于接收错误信息

        if (!FileUtil.checkSaveViewFolderLength(_savePath, msgHolder)) {
            response.setSuccess(false);
            response.setMsg(msgHolder[0]);
            return response;
        }

        // 处理文件名
        StringBuilder fileName = new StringBuilder(filename);
//        StringBuilder fileName;
//        boolean renameFlag = toBoolean(WebConfigUtil.get("NAttcReName", "0"));
//        if (renameFlag) {
//            fileName = new StringBuilder(filename);
//        } else {
//            if (isNullOrEmpty(fileNo)) {
//                String fullDirPath = Paths.get(WebConfigUtil_web.getFilePath(), filePath).toString();
//                fileName = new StringBuilder(CreateAttcFileName(fullDirPath, Paths.get(filename).getFileName().toString()));
//            } else {
//                fileName = new StringBuilder(StringFormat.format("{0}_{1}", fileNo, Paths.get(filename).getFileName().toString()));
//            }
//        }
        String sourceFilePath = fInfos.getOrDefault("fPath", "") + "",
                fileSize = fInfos.getOrDefault("filesize", "") + "";
        // 处理文件编号
        if (isNullOrEmpty(specNo)) {
            specNo = DataImpl.GetAttcBmpSpec(moduleId);
        }
        StringBuilder relPath = new StringBuilder(), webPath = new StringBuilder();
        // 获取文件保存路径信息
        String savepath = FileUtil.getFileSavePath(filePath, null, getAppDomain(), getAttcPath(), fileName, relPath, webPath);

        // 移动文件
//        response = FileUtil.moveTo(
//                sourceFilePath,
//                filePath,
//                fileName.toString(),
//                getAttcPath(),
//                comfirm == 1
//        );

//        startTime = LocalDateTime.now();
        response.setSuccess(new java.io.File(_savePath).exists());

        boolean moveSuccess = response.isSuccess();
        if (moveSuccess) {
            // 删除空目录
//            FileUtil.deleteEmptyDir(sourceFilePath);

            // 处理Web相对路径
            String webRelPath = FileUtil.urlEncode(relPath.toString(), false);

            // 构建附件信息
            Hashtable<String, Object> attcInfos = new Hashtable<>();
            attcInfos.put("dllcoid", moduleId);
            attcInfos.put("sname", fileName);
            attcInfos.put("vname", fileName);
            attcInfos.put("parentid", dirTabId[0]);
            attcInfos.put("filesize", fileSize);
            attcInfos.put("creator", getUser().UserId);
            attcInfos.put("speciesno", specNo);
            attcInfos.put("fileno", getFirstPartBeforeUnderscore(fileName.toString()));
            attcInfos.put("stepcode", stepCode);
            attcInfos.put("webpath", webRelPath);
            attcInfos.put("tottime", totTime);
//
            ModuleBaseEntity module = GetModule(moduleId);
            if (module != null && !isNullOrEmpty(idValue)) {
                SetUpdateRowforf(module, null, idValue);
//                if(module.Updrow != null)
//                {
//                    attcInfos.put("keyvalue1",
//                            isNullOrEmpty(module.getAttcField1())
//                                    ? ""
//                                    : module.Updrow.get(trimDoubleBrace(module.getAttcField1()))
//                    );
//                    attcInfos.put("keyvalue2",
//                            isNullOrEmpty(module.getAttcField2())
//                                    ? ""
//                                    : module.Updrow.get(trimDoubleBrace(module.getAttcField2()))
//                    );
//                    attcInfos.put("keyvalue3",
//                            isNullOrEmpty(module.getAttcField3())
//                                    ? ""
//                                    : module.Updrow.get(trimDoubleBrace(module.getAttcField3()))
//                    );
//                }
            }
            ;

            int fileId = 0;
            if (comfirm == 1) {
                List<Map<String, Object>> exitFileList = DataImpl.GetAttcFileInfo(dirTabId[0], moduleId, fileName.toString(), 0);
                if (!exitFileList.isEmpty()) {
                    fileId = ToInt32(exitFileList.get(0).getOrDefault("fileId", 0));
                    attcInfos.put("fileid", fileId);
                }
            }

            // 新增或更新附件记录
            List<Map<String, Object>> attcList = new ArrayList<>();
            attcList.add(attcInfos);
            response = AddOrUpdTable(attcList, "P_fm_FileTab", "fileid", fileId == 0, null);

            // 记录系统日志
            sysLog(String.format(
                    "%s在模块%s记录%s上传附件%s%s",
                    getUser().UserName,
                    moduleId,
                    idValue,
                    fileName,
                    response.isSuccess() ? "成功" : "失败"
            ), "上传附件");

            if (response.isSuccess()) {
                // 设置返回的路径信息
                FileUtil.PathInfo pathInfo = new FileUtil.PathInfo();
                pathInfo.WebPath = (webPath.toString());
                pathInfo.WebRelPath = (webRelPath);
                pathInfo.RelativePath = (relPath.toString());
                pathInfo.SavePath = (_savePath);
                pathInfo.OldFileName = (filename);
                pathInfo.FileName = (fileName.toString());
                pathInfo.UserName = (getUser().UserName);
                log.debug(String.valueOf("pathInfo : " + pathInfo.fileId));
                log.debug(String.valueOf("response.getOther() : " + response.getOther()));
                pathInfo.fileId = ((response.getOther() + ""));
                response.setData(pathInfo);

                // 触发上传后事件
                FileUtil.PathInfo eventPathInfo = new FileUtil.PathInfo();
                eventPathInfo.WebPath = (webPath.toString());
                eventPathInfo.SavePath = (_savePath);
                eventPathInfo.fileId = ((response.getOther() + ""));
                eventPathInfo.ModuleId = (moduleId);
                eventPathInfo.IdValue = (idValue);
                eventPathInfo.SpecNo = (specNo);
                eventPathInfo.TotSize = (ToInt64(fileSize));
                eventPathInfo.StepCode = (stepCode);
                eventPathInfo.FileName = (fileName.toString());
                eventPathInfo.OldFileName = (filename);
                getEventHandler().callAfterUploadFile(eventPathInfo, response);
            } else if (moveSuccess) {
                // 保存失败时删除已移动的文件
                FileUtil.deleteFile(Paths.get(filePath, fileName.toString()).toString(), getAttcPath());
            }
        } else {
            response.setMsg("保存失败，目标文件不存在！");
        }
        // 调试日志：记录保存附件耗时
        log.warn(String.valueOf(String.format("保存附件耗时 %s ms", calculateDurationMs(startTime, LocalDateTime.now()))));
        return response;
    }

    // 专门处理双 { 符号的修剪方法
    private static String trimDoubleBrace(String str) {
        // 先去除首尾的 { 符号，连续调用两次 replace 模拟 C# 的两次 Trim('{')
        String result = str.trim(); // 先做常规的空白字符修剪（可选，根据你的业务需求）
        // 第一次去除首尾的 {
        result = result.replaceAll("^\\{+", "").replaceAll("\\}+$", "");
        // 第二次去除首尾的 {（模拟 C# 连续两次 Trim('{')）
        result = result.replaceAll("^\\{+", "").replaceAll("\\}+$", "");
        return result;
    }

    /**
     * 计算两个时间点之间的毫秒差
     *
     * @param start 开始时间
     * @param end   结束时间
     * @return 毫秒差
     */
    private long calculateDurationMs(LocalDateTime start, LocalDateTime end) {
        return Duration.between(start, end).toMillis();
    }

    /**
     * 根据根据目录和文件名，生成新的文件名.
     *
     * @param localPath 文件存储目录
     * @param fileName  原始文件名
     * @param index     开始查找的索引
     * @return 生成的新文件名
     */
    private String CreateAttcFileName(String localPath, String fileName, int index) {
        // 处理默认索引值，对应C#的可选参数
        if (index <= 0) {
            index = 1;
        }

        File directory = new File(localPath);
        if (directory.exists() && directory.isDirectory()) {
            try {
                // 获取目录下所有文件，提取并处理文件名中的索引部分
                List<Integer> fileIndexes = new ArrayList<>();
                File[] files = directory.listFiles();
                if (files != null) {
                    fileIndexes = Arrays.stream(files)
                            .map(file -> {
                                // 处理文件名，提取下划线前的部分
                                String fileNamePart = file.getName().split("_")[0];
                                // 去除前导零
                                fileNamePart = fileNamePart.replaceAll("^0+", "");
                                // 如果长度超过4，取后4位
                                if (fileNamePart.length() > 4) {
                                    fileNamePart = fileNamePart.substring(fileNamePart.length() - 4);
                                }
                                // 转换为整数，转换失败则返回-1（不参与索引检查）
                                try {
                                    return Integer.parseInt(fileNamePart);
                                } catch (NumberFormatException e) {
                                    return -1;
                                }
                            })
                            .filter(idx -> idx != -1) // 过滤无效索引
                            .sorted() // 排序
                            .collect(Collectors.toList());
                }

                // 找到不重复的最小索引
                while (fileIndexes.contains(index)) {
                    index++;
                }
            } catch (Exception e) {
                // 捕获所有异常，不做处理（保持与原C#代码行为一致）
            }
        }

        // 生成格式为"00{index}_{fileName}"的新文件名
        return StringFormat.format("00{0}_{1}", index, fileName);
    }

    private String CreateAttcFileName(String localPath, String fileName) {
        return CreateAttcFileName(localPath, fileName, 1);
    }

    /**
     * 获取文件名中下划线前的部分
     *
     * @param fileName 文件名
     * @return 下划线前的部分
     */
    private String getFirstPartBeforeUnderscore(String fileName) {
        if (isNullOrEmpty(fileName)) {
            return "";
        }
        int underscoreIndex = fileName.indexOf("_");
        return underscoreIndex > 0 ? fileName.substring(0, underscoreIndex) : fileName;
    }

    public BaseResponse GetSpecificAttachModules(String moduleId) {
        BaseResponse response = new BaseResponse();
        List<Object> rets = GetSpecificModules(moduleId);
        response.setData(rets);
        response.setSuccess(true);
        return response;
    }

    protected List<Object> GetSpecificModules(String moduleId) {
        // 假设dataImpl.getAttcModules(moduleId)返回List<Map<String, Object>>（对应DataTable）
        List<Map<String, Object>> moduleList = DataImpl.GetAttcModules(moduleId);

        return moduleList.stream()
                .filter(row -> {
                    // 过滤unionmodule和attachType不为空的行
                    String unionModule = row.getOrDefault("unionmodule", "").toString().trim();
                    String attachType = row.getOrDefault("attachType", "").toString().trim();
                    return !unionModule.isEmpty() && !attachType.isEmpty();
                })
                .map(row -> {
                    final String ounionModule = row.getOrDefault("unionmodule", "").toString();
                    final String otitle = row.getOrDefault("attachname", "").toString();
                    final String oattachType = row.getOrDefault("attachType", "").toString();
                    BaseModule omodule = null;

                    if ("平铺".equals(oattachType)) {
                        omodule = GetBaseModule(ounionModule, "");
                        omodule.IsCard = (true);
                        setMain(omodule);
                    }
                    // 使用匿名内部类或自定义DTO替代匿名对象
                    BaseModule finalOmodule = omodule;
                    return new Object() {
                        public final String title = otitle;
                        public final Object icoSrc = row.getOrDefault("attachimg", "");
                        public final String unionModule = ounionModule;
                        public final String attachType = oattachType;
                        public final BaseModule module = finalOmodule;
                    };
                })
                .filter(module -> module != null)
                .collect(Collectors.toList());
    }

    /**
     * 获取单据明细数据
     *
     * @param moduleId 模块ID
     * @param idValue  主键值
     * @return 包含明细数据的响应对象
     */
    public BaseResponse GetBillDetailData(String moduleId, String idValue) {
        BaseResponse response = new BaseResponse();
        response.setSuccess(false);

        // 获取单据模块信息
        BillModule module = GetBillModule(moduleId, getMenuId());
        if (module == null) {
            return response;
        }
        // 处理查询SQL，替换参数占位符
        String result = String.format("{\"%s\":\"%s\"}", module.getIdField(), idValue);
        String detailSql = dealQuerySql(
                module.getDetailSql(),
                result,
                null,
                null,
                "",
                "", false, false, false
        );
        // 执行SQL查询并转换结果格式
        if (databaseType.equals("dm")) detailSql = RegexUtil.processDmServerSql(detailSql);
        List<Map<String, Object>> dataTable = toHashTable(jdbcTemplate.queryForList(detailSql), false);
//        List<Map<String, Object>> hashTableData = DataTableUtil.toHashTable(List<Map<String,Object>>, false);
        // 设置响应数据
        response.setData(dataTable);
        response.setSuccess(true);
        return response;
    }

    /**
     * 保存单据数据
     *
     * @param moduleId      模块ID
     * @param master        主表数据(JSON字符串：{a:b})
     * @param detail        明细数据(JSON数组：[{a:b}])
     * @param leftRecord    父记录数据
     * @param isAdd         是否为新增操作
     * @param rtagid        红蓝字标识（0：蓝字，1：红字）
     * @param comfirmFlag   确认标识（0：默认保存，1：提示后确认保存）
     * @param auditFlag     审核标识
     * @param comfirmRemark 确认备注
     * @return 保存结果响应对象
     */
    public BaseResponse SaveBill(
            String moduleId,
            String master,
            String detail,
            String leftRecord,
            boolean isAdd,
            int rtagid,
            int comfirmFlag,
            int auditFlag,
            String comfirmRemark) {

        BaseResponse response = new BaseResponse();
        response.setSuccess(false);

        // 获取单据模块信息
        BillModule module = GetBillModule(moduleId, getMenuId());
        if (module == null) {
            response.setMsg("未找到模块配置信息");
            return response;
        }

        // 设置模块参数
        module.Rtagid = (rtagid);
        module.AuditFlag = (auditFlag);
        module.setComfirmFlag(comfirmFlag);
        module.ComfirmRemark = (comfirmRemark);

        // 验证主表数据
        if (isNullOrEmpty(master)) {
            response.setMsg("主表数据不能为空");
            return response;
        }

        try {
            // 解析主表数据
            Map<String, Object> masterData = (Map<String, Object>) JSON.Decode(master);

            String idValue = null;
            if (masterData != null) {
                idValue = masterData.getOrDefault(module.getIdField(), "").toString();
            }
            module.DetailGuid = (UUID.randomUUID().toString());
            module.IdValue = (idValue);

            // 验证ID有效性（非新增操作）
            if (isNullOrEmpty(idValue) && !isAdd) {
                response.setMsg(String.format("%s%s", module.getIdField(), LanguageUtil.NotNull));
                return response;
            }

            // 解析明细数据
            ArrayList<Map<String, Object>> detailDataList = new ArrayList<>();
            if (!isNullOrEmpty(detail)) {
                detailDataList = (ArrayList<Map<String, Object>>) JSON.Decode(detail);
            }

            // 新增操作验证
            if (isAdd) {
                // 验证明细数据
                if ((detailDataList == null || detailDataList.isEmpty()) && !module.getEmptyDetailSaveAble()) {
                    response.setMsg(LanguageUtil.GetString("BillDetailNotNull"));
                    return response;
                }

                // 处理单据编号
                if (ExistBillNo(module, idValue)) {
                    idValue = GetNewBillNo(module.getBillSeq());
                    if (!isNullOrEmpty(idValue)) {
                        masterData.put(module.getIdField(), idValue);
                        module.IdValue = (idValue);
                    } else {
                        masterData.put(module.getIdField(), "");
                        module.IdValue = ("");
                        if (!module.getNewVer()) {
                            response.setMsg("旧版单据，单据编号不能为空！");
                            return response;
                        }
                    }
                }
            }

            // 解析父记录
            Map<String, Object> parentRecord = null;
            if (!isNullOrEmpty(leftRecord)) {
                parentRecord = (Map<String, Object>) JSON.Decode(leftRecord);
            }

            // 处理特殊字符串字段
            Map<String, List<FieldModel>> updStrFieldList = new HashMap<>();
            Map<String, List<FieldModel>> detailUpdStrFieldList = new HashMap<>();

            // 获取主表信息
            String masterTable = module.getMasterTable();
            List<Map<String, Object>> masterTableInfos = DataImpl.GetTableInfo(masterTable);

            // 新版单据处理
            if (module.getNewVer()) {
                Optional<Map<String, Object>> idFieldRow = masterTableInfos.stream()
                        .filter(row -> row.get("name").toString().equalsIgnoreCase(module.getIdField()))
                        .findFirst();
                idFieldRow.ifPresent(row -> row.put("isnullable", 0));
            }

            // 获取主表列配置
            List<Map<String, Object>> masterColumns = DataImpl.GetBillMasterRows(module.getModuleId(), getUser().UserName, 0);
            String masterIdentityField = GetIdentityField(masterTableInfos);

            // 获取主表字段列表并验证
            List<FieldModel> fieldList = GetAOUFields_TabInfo(
                    module, masterData, masterColumns, parentRecord,
                    masterTableInfos, masterIdentityField, isAdd, null, 0
            );

            // 验证字段错误信息
            String errMsg = fieldList.stream()
                    .filter(field -> !isNullOrEmpty(field.ValidMsg))
                    .map(F -> F.ValidMsg)
                    .collect(Collectors.joining("\n"));

            if (!isNullOrEmpty(errMsg)) {
                response.setMsg("主表字段\n" + errMsg);
                return response;
            }

            // 构建主表保存SQL
            StringBuilder masterUpdStrKey = new StringBuilder();
            String masterSql = BuildSaveSql(
                    module, masterTable, masterIdentityField,
                    fieldList, isAdd, masterUpdStrKey
            );
            log.debug(String.valueOf("masterSql111 :+ " + masterSql));

            if (!isNullOrEmpty(masterUpdStrKey.toString())) {
                updStrFieldList.put(masterUpdStrKey.toString(), fieldList);
            }

            // 构建明细SQL
            BaseResponse[] res = new BaseResponse[1];
            res[0] = response;
            StringBuilder detailSqlBder = BuildBillDetailSql(
                    module, idValue, parentRecord, detailDataList,
                    res[0], detailUpdStrFieldList
            );

            response = res[0];
            if (detailSqlBder == null) {
                return response;
            }

            // 保存模块数据引用
            module.MasterData = (masterData);
            module.DetailData = (detailDataList);

            // 执行明细SQL
            if (detailSqlBder != null && detailSqlBder.length() > 0) {
                log.debug(String.valueOf("detailSqlBder : " + detailSqlBder));

                if ("dm".equalsIgnoreCase(databaseType)) {
                    String fullSql = detailSqlBder.toString();

                    // 按分号拆分所有 SQL
                    String[] sqlParts = fullSql.split(";");

                    int totalRowsAffected = 0;

                    try {
                        for (String sqlPart : sqlParts) {
                            String sql = sqlPart.trim();

                            // 跳过空 SQL
                            if (sql.isEmpty()) {
                                continue;
                            }

                            String upperSql = sql.toUpperCase();

                            // 处理 INSERT
                            if (upperSql.startsWith("INSERT")) {
                                int rowsAffected = jdbcTemplate.update(sql);
                                totalRowsAffected += rowsAffected;

                                log.debug(String.valueOf("执行INSERT：" + sql));
                                log.debug(String.valueOf("INSERT受影响行数：" + rowsAffected));

                                if (rowsAffected <= 0) {
                                    log.debug(String.valueOf("INSERT无数据插入，行数：" + rowsAffected));
                                    return response;
                                }
                            }

                            // 处理 SELECT
                            else if (upperSql.startsWith("SELECT")) {
                                Map<String, Object> selectResult = jdbcTemplate.queryForMap(sql);

                                log.debug(String.valueOf("执行SELECT：" + sql));
                                log.debug(String.valueOf("SELECT返回结果：" + selectResult));

                                // 这里根据你的业务处理 Id
                                Object idObj = selectResult.get("Id");
                                if (idObj != null) {
                                    Long id = Long.valueOf(idObj.toString());
                                    log.debug(String.valueOf("当前插入数据ID：" + id));
                                }
                            }

                            // 其他 SQL 类型
                            else {
                                log.debug(String.valueOf("未识别SQL，跳过：" + sql));
                            }
                        }

                        if (totalRowsAffected <= 0) {
                            log.debug(String.valueOf("没有任何明细数据插入"));
                            return response;
                        }

                        log.debug(String.valueOf("明细总插入行数：" + totalRowsAffected));

                    } catch (Exception e) {
                        log.debug(String.valueOf("SQL执行异常：" + e.getMessage()));
                        log.error("Exception caught", e);
                        return response;
                    }

                } else {
                    int rowsAffected = jdbcTemplate.update(detailSqlBder.toString());
                    if (rowsAffected <= 0) {
                        return response;
                    }
                }
            }

            // 处理主表SQL（非新增且主表SQL为空时构建空更新SQL）
            if (!isAdd && isNullOrEmpty(masterSql)) {
                masterSql = String.format(
                        "update %s set %s='%s' where %s='%s'",
                        module.getMasterTable(), module.getIdField(), module.IdValue,
                        module.getIdField(), module.IdValue
                );
            }

            // 执行单据保存
            log.debug(String.valueOf("执行单据保存 masterSql222 :+ " + masterSql));
            response = BillDataSave(module, masterSql, isAdd ? SystemEnums.ActionType.Add : SystemEnums.ActionType.Update);

            // 记录系统日志
            sysLog(String.format(
                    "%s->%s%s%s%s@#@%s;明细：%s",
                    module.getMenuName(), getUser().UserName,
                    isAdd ? "添加" : "修改", module.IdValue,
                    response.isSuccess() ? "成功" : ("失败：" + response.getMsg()),
                    masterSql, detailSqlBder
            ), "操作模块");

            if (!response.isSuccess()) {
                getEventHandler().callAfterModuleDataChange(module, isAdd ? SystemEnums.ActionType.Add : SystemEnums.ActionType.Update, response);
                return response;
            }

            // 处理附件临时ID更新
            String attcTempId = masterData.getOrDefault("temp_attachment_id", "").toString();
            if (!isNullOrEmpty(attcTempId)) {
                UpdateTempAttcInfo(module.getModuleId(), attcTempId, idValue);
            }

            // 处理特殊字符串字段保存结果
            @SuppressWarnings("unchecked")
            Map<String, UpdStrModule> saveResult = (Map<String, UpdStrModule>) response.getOther();
            response.setOther(isNullOrEmpty(response.getMsg()) ? module.IdValue : response.getMsg());
            response.setMsg("");

            idValue = response.getOther().toString();

            if (saveResult != null) {
                for (Map.Entry<String, List<FieldModel>> entry : updStrFieldList.entrySet()) {
                    UpdStrModule updStrModule = saveResult.get(entry.getKey());
                    SaveStrFieldByPms(entry.getValue(), updStrModule);
                    if (updStrModule != null && updStrModule.Valide()) {
                        module.IdValue = (updStrModule.getIdValue());
                        module.IdentityId = (updStrModule.getIdentityId());
                    }
                }
            }

            // 触发数据变更后事件
            getEventHandler().callAfterModuleDataChange(module, isAdd ? SystemEnums.ActionType.Add : SystemEnums.ActionType.Update, response);
            response.setSuccess(true);

        } catch (Exception e) {
            response.setMsg("保存失败：" + e.getMessage());
            log.error("保存单据异常", e);
        }

        return response;
    }

    protected StringBuilder BuildBillDetailSql(BillModule module, String
                                                       idValue, Map<String, Object> parentRecord,
                                               ArrayList<Map<String, Object>> detaildata, BaseResponse response,
                                               Map<String, List<FieldModel>> detailUpdStrFieldList) throws UnsupportedEncodingException {
        long startTime;
        double startT = 0, sqlT = 0;

        StringBuilder detailSqlBder = new StringBuilder();
        String detailIdentityField = GetIdentityField(module.getDetailTable() + "_temp"); // 获取自动增长列
//        out.println("module.getDetailTable() + \"_temp\" " + module.getDetailTable() + "_temp");
//        out.println("detailIdentityField " + detailIdentityField);
        startTime = System.currentTimeMillis();
        // 获取表信息
        List<Map<String, Object>> detailTableInfos = DataImpl.GetTableInfo(module.getDetailTable());
        // 获取临时表信息（240315 都不允许为空）
        String sql = getDetailJDBC().BuildBillDetailSqlSql(module.getDetailTable());
        List<Map<String, Object>> tempDetailTableInfos = jdbcTemplate.queryForList(sql);
        // 获取单据明细列信息
        List<Map<String, Object>> detialColumns = DataImpl.GetBillDetailColumns(module.getModuleId(), getUser().UserId, getUser().UserName, 0);

        List<ColInfos> colInfos = BuildBillDetailColInfos(detailIdentityField, detialColumns, tempDetailTableInfos, detailTableInfos);
        startT += (System.currentTimeMillis() - startTime);

        for (int i = 0; i < detaildata.size(); i++) {
            Map<String, Object> detailData = detaildata.get(i);
            BillDetailModule detailModule = new BillDetailModule();

            // 设置明细数据关联主表ID和系统标识
            detailData.put(module.getDetailPrefix() + "billdocument_id", idValue);
            detailData.put("sysstr", module.DetailGuid);

            startTime = System.currentTimeMillis();
            StringBuilder errBuilder = new StringBuilder();
            List<FieldModel> detailFieldList = GetAOUFields_ColInfo(
                    detailModule, colInfos, detailData, parentRecord, "", detailIdentityField, true, errBuilder
            );
            startT += (System.currentTimeMillis() - startTime);

            // 处理验证错误
            if (!errBuilder.isEmpty()) {
                response.setMsg("明细第" + (i + 1) + "行\n" + errBuilder);
                response.setSuccess(false);
                return null;
            }

            startTime = System.currentTimeMillis();
            StringBuilder detailUpdStrKey = new StringBuilder();
            // 构建保存SQL并获取更新键
            detailSqlBder.append(BuildSaveSql(
                    detailModule, module.getDetailTable() + "_temp", detailIdentityField,
                    detailFieldList, true, detailUpdStrKey
            ));
            sqlT += (System.currentTimeMillis() - startTime);

            // 保存更新字段列表
            if (detailUpdStrKey != null && !detailUpdStrKey.isEmpty()) {
                detailUpdStrFieldList.put(detailUpdStrKey.toString(), detailFieldList);
            }
        }

        return detailSqlBder;
    }

    /**
     * 获取自增列字段名
     *
     * @param tabname 表名
     * @return 自增列字段名
     */
    public String GetIdentityField(String tabname) {
        // 委托给dataImpl处理获取自增列的逻辑
        return DataImpl.GetIdentityField(tabname);
    }

    /**
     * 处理单据数据保存
     *
     * @param module    单据模块实体
     * @param masterSql 主表SQL
     * @param aType     操作类型：1新增，2修改，3删除，4作废，5作废恢复
     * @return 基础响应对象
     */
    private BaseResponse BillDataSave(BillModule module, String masterSql, SystemEnums.ActionType aType) {
        BaseResponse response = new BaseResponse();
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            // 1. 打印参数（保留原调试逻辑）
            // 2. 触发事件、确定存储过程名（保留原逻辑）
            getEventHandler().callBeforeModuleDataChange(module, aType, null);
            String proName = "P_BillSavePr70";
            if (!DataImpl.IsExitPro(proName)) {
                proName = "P_BillSavePr_3";
            }

            // 3. 核心：参数转义（避免SQL注入+适配人大金仓语法）
            // 3.1 字符串参数转义：单引号替换为两个单引号
            String escapedMasterSql = Objects.requireNonNullElse(masterSql, "").replace("'", "''"); // 关键：转义insert语句内的单引号
            String escapedIdValue = Objects.requireNonNullElse(module.IdValue, "").replace("'", "''");
            String escapedDetailGuid = Objects.requireNonNullElse(module.DetailGuid, "").replace("'", "''");
            String escapedBillSeq = Objects.requireNonNullElse(module.getBillSeq(), "").replace("'", "''");
            // 3.2 数值参数（直接转成字符串，无需单引号）
            int userId = ToInt32(getUser().UserId);
            int actionTypeValue = aType.getValue();
            int auditFlag = module.AuditFlag;
            int comfirmFlag = module.getComfirmFlag();

            // 4. 拼接SQL字符串（与之前验证通过的SQL格式完全一致）
            StringBuilder sqlBuilder;
            // 4.1 声明变量（返回值+输出参数）
            sqlBuilder = getDetailJDBC().BillDataSave(proName, escapedMasterSql, escapedIdValue, escapedDetailGuid, escapedBillSeq, userId, actionTypeValue, auditFlag, comfirmFlag);
            // 最终拼接好的SQL
            String finalSql = sqlBuilder.toString();
            log.debug(String.valueOf("拼接后的完整SQL：" + finalSql)); // 调试用，可查看最终SQL


            connection = jdbcTemplate.getDataSource().getConnection();
            // 创建Statement时指定ResultSet保持策略，避免切换结果集时自动关闭
            statement = connection.createStatement(
                    ResultSet.TYPE_SCROLL_INSENSITIVE,
                    ResultSet.CONCUR_READ_ONLY,
                    ResultSet.HOLD_CURSORS_OVER_COMMIT
            );

            // 5.1 执行SQL
            boolean hasResultSet = statement.execute(finalSql);
            List<Map<String, Object>> resultList = new ArrayList<>();
            boolean foundTargetResultSet = false; // 标记是否找到目标结果集

            // 5.2 循环遍历所有结果集/更新计数
            while (true) {
                if (hasResultSet) {
                    resultSet = statement.getResultSet();
                    ResultSetMetaData metaData = resultSet.getMetaData();
                    int columnCount = metaData.getColumnCount();
                    boolean isTargetResultSet = false;

                    // 检查当前结果集是否包含returnValue列（目标结果集标识）
                    for (int i = 1; i <= columnCount; i++) {
                        String columnName = metaData.getColumnName(i).toLowerCase();
                        if ("returnvalue".equals(columnName)) {
                            isTargetResultSet = true;
                            foundTargetResultSet = true;
                            break;
                        }
                    }

                    if (isTargetResultSet) {
                        // 立即处理目标结果集，避免被后续操作关闭
                        if (resultSet.next()) {
                            int returnValue = resultSet.getInt("returnValue");
                            String msg = resultSet.getString("outputMsg");
                            msg = Objects.requireNonNullElse(msg, "");

                            // 6. 组装返回结果（保留原业务逻辑）
                            response.setData(returnValue);
                            if (returnValue == -1) {
                                // 失败场景
                                response.setSuccess(false);
                                response.setMsg(msg.replace("\r", "<br />"));
                                String deleteTempSql = String.format(
                                        "DELETE %s_temp WHERE sysstr = '%s'",
                                        module.getDetailTable().replace("'", "''"),
                                        module.DetailGuid.replace("'", "''")
                                );
                                jdbcTemplate.update(deleteTempSql);
                            } else {
                                // 成功场景
                                response.setSuccess(true);
                                response.setMsg(msg.isEmpty() ? "" : msg.replace("\r", "<br />"));
                                List<List<Map<String, Object>>> set = new ArrayList<>();
                                set.add(resultList);
                                response.setOther(DataImpl.DecodeSaveResult(set));
                            }
                        }
                        resultSet.close(); // 处理完目标结果集后关闭
                        break; // 找到目标后退出循环
                    } else {
                        // 处理普通结果集（存储过程返回的业务数据）
                        while (resultSet.next()) {
                            Map<String, Object> rowMap = new HashMap<>();
                            for (int i = 1; i <= columnCount; i++) {
                                String columnName = metaData.getColumnName(i).toLowerCase();
                                Object columnValue = resultSet.getObject(i);
                                columnValue = Objects.requireNonNullElse(columnValue, "");
                                rowMap.put(columnName, columnValue);
                            }
                            resultList.add(rowMap);
                        }
                        resultSet.close(); // 及时关闭非目标结果集
                    }
                } else {
                    // 处理更新计数（如INSERT/UPDATE的受影响行数），无数据则跳过
                    int updateCount = statement.getUpdateCount();
                    if (updateCount == -1) {
                        // 没有更多结果，退出循环
                        break;
                    }
                }

                // 切换到下一个结果集/更新计数
                hasResultSet = statement.getMoreResults();
                // 无更多结果时退出循环
                if (!hasResultSet && statement.getUpdateCount() == -1) {
                    break;
                }
            }

            // 若未找到目标结果集（异常场景兜底）
            if (!foundTargetResultSet) {
                response.setSuccess(false);
                response.setMsg("未获取到存储过程返回结果");
                response.setData(0);
            }

        } catch (Exception e) {
            // 异常处理（保留原逻辑）
            response.setSuccess(false);
            response.setMsg(e.getMessage().replace("\r", "<br />"));
            response.setData(0);
            log.error("Exception caught", e);
        } finally {
            // 关闭资源（严格按顺序：ResultSet → Statement → Connection）
            if (resultSet != null) {
                try {
                    if (!resultSet.isClosed()) {
                        resultSet.close();
                    }
                } catch (SQLException e) {
                    log.error("Exception caught", e);
                }
            }
            if (statement != null) {
                try {
                    if (!statement.isClosed()) {
                        statement.close();
                    }
                } catch (SQLException e) {
                    log.error("Exception caught", e);
                }
            }
            if (connection != null) {
                try {
                    if (!connection.isClosed()) {
                        connection.close();
                    }
                } catch (SQLException e) {
                    log.error("Exception caught", e);
                }
            }
        }
        return response;

        // 5. 执行SQL并处理结果
//            connection = jdbcTemplate.getDataSource().getConnection();
//            statement = connection.createStatement();
//
//
//
//
//            // 5.1 执行SQL（存储过程可能返回ResultSet，需先处理）
//            boolean hasResultSet = statement.execute(finalSql);
//            List<Map<String, Object>> resultList = new ArrayList<>();
//
//            // 5.2 处理存储过程返回的ResultSet（如存在）
//            if (hasResultSet) {
//                resultSet = statement.getResultSet();
//                ResultSetMetaData metaData = resultSet.getMetaData();
//                int columnCount = metaData.getColumnCount();
//                while (resultSet.next()) {
//                    Map<String, Object> rowMap = new HashMap<>();
//                    for (int i = 1; i <= columnCount; i++) {
//                        String columnName = metaData.getColumnName(i).toLowerCase();
//                        Object columnValue = resultSet.getObject(i);
//                        columnValue = Objects.requireNonNullElse(columnValue, "");
//                        rowMap.put(columnName, columnValue);
//                    }
//                    resultList.add(rowMap);
//                }
//                // 移动到下一个结果集（获取returnValue和outputMsg）
//                statement.getMoreResults();
//            }
//
//            // 5.3 获取返回值和输出参数（第二个结果集）
//            Map<String, Object> resultMap = null;
//            resultSet = statement.getResultSet();
//            if (resultSet.next()) {
//                int returnValue = resultSet.getInt("returnValue");
//                String msg = resultSet.getString("outputMsg");
//                msg = Objects.requireNonNullElse(msg, "");
//
//                // 6. 组装返回结果（保留原逻辑）
//                response.setData(returnValue);
//                if (returnValue == -1) {
//                    // 失败场景
//                    response.setSuccess(false);
//                    response.setMsg(msg.replace("\r", "<br />"));
//                    String deleteTempSql = String.format(
//                            "DELETE %s_temp WHERE sysstr = '%s'",
//                            module.getDetailTable().replace("'", "''"), // 转义表名
//                            module.DetailGuid.replace("'", "''") // 转义条件值
//                    );
//                    jdbcTemplate.update(deleteTempSql);
//                } else {
//                    // 成功场景（使用解析好的resultList）
//                    response.setSuccess(true);
//                    response.setMsg(msg.isEmpty() ? "" : msg.replace("\r", "<br />"));
//                    List<List<Map<String, Object>>> set = new ArrayList<>();
//                    set.add(resultList);
//                    response.setOther(DataImpl.DecodeSaveResult(set));
//                }
//            }
//
//        } catch (Exception e) {
//            // 异常处理（保留原逻辑）
//            response.setSuccess(false);
//            response.setMsg(e.getMessage().replace("\r", "<br />"));
//            response.setData(0);
//            e.printStackTrace();
//        } finally {
//            // 关闭资源（顺序：ResultSet → Statement → Connection）
//            if (resultSet != null) {
//                try {
//                    if (!resultSet.isClosed()) resultSet.close();
//                } catch (SQLException e) {
//                    e.printStackTrace();
//                }
//            }
//            if (statement != null) {
//                try {
//                    if (!statement.isClosed()) statement.close();
//                } catch (SQLException e) {
//                    e.printStackTrace();
//                }
//            }
//            if (connection != null) {
//                try {
//                    if (!connection.isClosed()) connection.close();
//                } catch (SQLException e) {
//                    e.printStackTrace();
//                }
//            }
//        }
//        return response;
    }


    /**
     * 单据提交/撤回操作
     *
     * @param moduleId  模块ID
     * @param idValue   主键值
     * @param stateEn   单据状态实体（可为null）
     * @param applyType 提交类型：1-提交，2-撤回
     * @return 基础响应对象
     */
    public BaseResponse BillApply(String moduleId, String idValue, BillStateEn stateEn, int applyType) throws
            CusException {
        // 处理默认参数
        if (applyType < 1 || applyType > 2) {
            applyType = 1; // 默认提交
        }

        // 获取单据模块信息
        BillModule module = GetBillModule(moduleId, getMenuId());
        module.IdValue = (idValue);

        // 获取单据状态
        BillStateEn state = GetBillState(module, "");

        // 系统日志记录
        sysLog(String.format("%s->%s%s%s",
                        module.getMenuName(),
                        getUser().UserName,
                        applyType == 1 ? "提交" : "撤回",
                        module.IdValue),
                "提交数据");

        state.StepCode = ("0");

        // 复制状态参数（如果传入了stateEn）
        if (stateEn != null) {
            state.setSelectConfirmFlag(stateEn.getSelectConfirmFlag());
            state.nextSelectStepCode = (stateEn.nextSelectStepCode);
            state.nextSelectStepOper = (stateEn.nextSelectStepOper);
            state.comfirmFlag = (stateEn.comfirmFlag);
        }

        // 确定操作类型
        SystemEnums.ActionType actionType = applyType == 1
                ? SystemEnums.ActionType.Submit
                : SystemEnums.ActionType.EscSubmit;

        // 触发状态变更前事件
        getEventHandler().callBeforeModuleStateChange(module, actionType, null);

        // 执行提交/撤回处理
        BaseResponse response = Deal99Response(
                DataImpl.BillApply(module, state, applyType),
                null,
                moduleId
        );

        // 触发状态变更后事件
        getEventHandler().callAfterModuleStateChange(module, actionType, response);

        return response;
    }

    /**
     * 单据审核处理方法
     *
     * @param module  单据模块实体
     * @param master  主表数据
     * @param detail  明细表数据
     * @param stateEn 单据状态实体
     * @return 基础响应对象
     */
    public BaseResponse BillAudit(BillModule module, String master, String detail, BillStateEn stateEn) {
        final BaseResponse[] response = {new BaseResponse()};
        if (stateEn != null && module != null) {
            // 判断是否为正向审核（Direction为"F"）
            boolean isAudit = "F".equalsIgnoreCase(stateEn.Direction);
            boolean goOn = true;

            // 如果是审核操作且主表数据不为空且未确认，则先保存单据
            if (isAudit && !isNullOrEmpty(master) && module.getComfirmFlag() == 0) {
                Integer comfirmFlag = module.getComfirmFlag() != null ? module.getComfirmFlag() : 0;
                response[0] = SaveBill(
                        module.getModuleId(),
                        master,
                        detail,
                        null,
                        false,
                        0,
                        comfirmFlag,
                        1, ""
                );
                goOn = response[0].isSuccess();
            }

            // 继续执行审核流程
            if (goOn) {
                // 获取相关实例
//                PushImpl pushImpl = new PushImpl(); // 假设通过合适的方式获取实例
                ModuleEventImpl eventHandler = getEventHandler();
//                DataImpl dataImpl = new DataImpl(); // 假设通过合适的方式获取实例

                // 执行推送并处理审核逻辑
                getpushImpl().billPush(
                        module.IdValue,
                        stateEn.StepCode,
                        getMenuId(), // 从父类BaseHandler获取MenuId
                        () -> {
                            // 触发审核状态变更前事件
                            eventHandler.callBeforeModuleAuditStateChange(module, stateEn, null);

                            // 执行审核操作并处理99响应
                            try {
                                response[0] = Deal99Response(DataImpl.AuditBill(module, stateEn), null, module.getModuleId());
                            } catch (CusException e) {
                                throw new RuntimeException(e);
                            }

                            // 触发审核状态变更后事件
                            eventHandler.callAfterModuleAuditStateChange(module, stateEn, response[0]);

                            // 记录系统日志
                            String operationResult = response[0].isSuccess() ? "成功" : ("失败：" + response[0].getMsg());
                            String logContent = String.format(
                                    "%s->%s%s%s%s",
                                    module.getMenuName(),
                                    getUser().UserName,
                                    "审核",
                                    module.IdValue,
                                    operationResult
                            );
                            sysLog(logContent, "操作模块");

                            return response[0].isSuccess();
                        }
                );
            }
        }
        return response[0];
    }

    public BaseResponse GetTaskColumns(String moduleId, String taskModuleId) {
        BaseResponse response = new BaseResponse();
        response.setData(GetAuditStepColumns(taskModuleId, "", "998", DataImpl.IsBaseModule(taskModuleId)));
        response.setSuccess(true);
        return response;
    }

    /**
     * 获取字段关联数据
     *
     * @param fieldId    字段ID
     * @param record     记录数据
     * @param leftRecord 左侧记录数据
     * @param pams       参数
     * @param fdtype     字段类型
     * @return 基础响应对象
     */
    public BaseResponse GetFieldUnionData(int fieldId, String record, String leftRecord, String pams, int fdtype) {
        BaseResponse response = new BaseResponse();
        response.setSuccess(true);

        // 字段ID无效时直接返回
        if (fieldId <= 0) {
            return response;
        }

        // 根据fdtype获取对应的字段数据表（使用List<Map>模拟DataTable）
        LoginUserInfo user = getUser();
        List<Map<String, Object>> fieldTab = null;
        switch (fdtype) {
            case 1:
                fieldTab = DataImpl.GetCondition(null, fieldId);
                break;
            case 2:
                fieldTab = DataImpl.GetBillDetailColumns(null, user.getUserId(), user.getUserName(), fieldId);
                break;
            case 3:
                BillModule module = GetBillModule(getModuleCode(), getMenuId());
                fieldTab = DataImpl.GetControlRows(module.getFromkey(), user.getUserName(), null, fieldId);
                break;
            case 4:
                fieldTab = DataImpl.GetBillMasterRows(null, user.getUserName(), fieldId);
                break;
            case 5:
                String unionSql = DataImpl.getSchemeUnionSql(fieldId);
                if (!isNullOrEmpty(unionSql)) {
                    String querySql = DealQuerySql(unionSql, record, leftRecord, pams, null, null);
                    response.setData(toHashTable(dbOperator.executeDataTable(querySql, getStartsize(), getPageSize(), tot), false));
                    response.setTot(tot[0]);
                    response.setSuccess(true);
                }
                return response;
            default:
                fieldTab = GetColumnRows(null, fieldId);
                break;
        }

        // 处理字段数据
        if (fieldTab != null && !fieldTab.isEmpty()) {
            // 获取第一条记录（对应DataTable的Rows[0]）
            Map<String, Object> fieldRow = fieldTab.get(0);
            Field field = new Field(fieldRow, createControl);

            // 处理关联SQL
            if (field.getUnionSQL() != null && !field.getUnionSQL().isEmpty()) {
                // 处理查询SQL（替换参数等逻辑）
                String querySql = dealQuerySql(
                        field.getUnionSQL(),
                        record,
                        leftRecord,
                        pams,
                        null,
                        null, false, false, false
                );

                // 执行分页查询并获取总数
                int[] totHolder = new int[1]; // 用于接收总数的容器
                List<Map<String, Object>> resultData = getDbOperator().executeDataTable(
                        querySql,
                        getStartsize(),
                        getPageSize(),
                        totHolder
                );

                // 转换为哈希表格式（模拟ToHashTable(false)）
//                List<Map<String, Object>> hashTableData = DataTableUtil.toHashTable(resultData, false);

                if (!isNullOrEmpty(resultData)) resultData = DataTableUtil.toLowerColumnName(resultData);
                // 设置响应数据
                response.setData(resultData);
                response.setTot(totHolder[0] != 0 ? totHolder[0] : resultData.size());
                response.setSuccess(true);
            }
        }

        return response;
    }

    /**
     * 执行加密的查询SQL
     *
     * @param enSql      加密的SQL
     * @param record     自身替换键值对
     * @param leftRecord 父级替换键值对
     * @param pams       参数
     * @param keyField   键字段
     * @param keyValue   键值
     * @return 基础响应对象
     */
    public BaseResponse ExcQuerySql(String enSql, String record, String leftRecord, String pams, String
            keyField, String keyValue) {
        BaseResponse response = new BaseResponse();
        try {
            // 解密SQL（假设使用AESUtil解密，根据实际加密方式调整）
            String querySql = AESUtil.decrypt(enSql);

            // 处理查询SQL，替换参数
            querySql = dealQuerySql(querySql, record, leftRecord, pams, keyField, keyValue, false, false, false);

            // 执行分页查询并获取总数
            int[] totHolder = new int[1]; // 用于接收总数的容器
            List<Map<String, Object>> resultData = getDbOperator().executeDataTable(
                    querySql,
                    getStartsize(),
                    getPageSize(),
                    totHolder
            );

            // 转换为哈希表格式（模拟ToHashTable()）
//            List<Map<String, Object>> hashTableData = DataTableUtil.toHashTable(resultData);

            // 设置响应数据
            response.setData(resultData);
            response.setTot(totHolder[0] != 0 ? totHolder[0] : resultData.size());
            response.setSuccess(true);
        } catch (Exception e) {
            response.setSuccess(false);
            response.setMsg("执行查询SQL失败: " + e.getMessage());
            // 记录异常日志
            sysLog("ExcQuerySql error", e.getMessage());
        }
        return response;
    }

    /**
     * 处理右键菜单点击事件
     *
     * @param menuid   菜单ID
     * @param moduleId 模块ID
     * @param rec      记录数据（JSON字符串）
     * @param leftRec  左侧记录数据（JSON字符串）
     * @return 基础响应对象
     */
    public BaseResponse ContextMenuClick(int menuid, String moduleId, String rec, String leftRec) throws
            CusException {
        // 解析记录数据JSON
        Map<String, Object> record = (Map<String, Object>) JSON.Decode(rec);

        // 解析左侧记录数据JSON（仅当不为空且以{开头时）
        Map<String, Object> leftRecord = null;
        if (!isNullOrEmpty(leftRec) && leftRec.trim().startsWith("{")) {
            leftRecord = (Map<String, Object>) JSON.Decode(leftRec);
        }

        // 获取右键菜单按钮对象
        SysPoPupMenuBtn btn = GetContextMenuBtn(menuid, record, leftRecord);

        // 执行菜单点击处理逻辑
        return GoContextMenuClick(btn, record, leftRecord, moduleId, menuid);
    }

    /**
     * 缓存错误的待办条件SQL信息
     */
    private static Map<String, Boolean> taskModuleCondCache = new HashMap<>();

    /**
     * 获取待办任务数据
     *
     * @param moduleId     模块ID
     * @param taskModuleId 任务模块ID
     * @param leftRecord   左侧记录数据（JSON字符串）
     * @param pms          参数（JSON字符串）
     * @return 基础响应对象
     */
    public BaseResponse GetTaskData(String moduleId, String taskModuleId, String leftRecord, String pms,
                                    int taskType) {
        BaseResponse response = new BaseResponse();
        BaseModule module = GetBaseModule(moduleId, "");
        boolean isBase = true;
        ModuleBaseEntity taskModule = null;
        if (moduleId == taskModuleId && module != null && taskType <= 0) {
            taskModule = module;
        } else {
            TaskType ttype = TaskType.DB;
            ttype = TaskType.fromValue(taskType);

            List<Map<String, Object>> taskDt = DataImpl.GetTaskModuleInfo(taskModuleId, ttype);
            if (taskDt != null && !taskDt.isEmpty()) {
                isBase = toBoolean(get(taskDt.get(0), "isbase"));
                taskModule = new ModuleBaseEntity(taskDt.get(0));

            }
        }
        // 确定任务模块（优先使用当前模块，否则获取指定任务模块）
        //ModuleBaseEntity taskModule = (moduleId.equals(taskModuleId) && module != null) ? module : GetModule(taskModuleId);

        if (taskModule == null) {
            response.setMsg(String.format("未找到待办模块%s，请检查！", taskModuleId));
            return response;
        }

        String taskSql = taskModule.getTaskSql();

        // 处理特殊场景：未配置任务SQL且满足特定条件时构建默认待办SQL
        if (isNullOrEmpty(taskSql) && moduleId.equals(taskModuleId)) {
            if (module == null || !isNullOrEmpty(module.LeftUnionField) || !isWindowsDirver()) {
                String mulitAuditCond = "";
                String serverIdCol = "0";

                // 处理多审核条件
                if (DataImpl.CheckIsMulitAudit()) {
                    mulitAuditCond = String.format("and CHARINDEX(',%s,',','+isnull(a.auditoperators,'')+',')<=0", getUser().UserName);
                }

                // 处理服务器ID字段
                if (org.example.Impl.DataImpl.HasServerIdCol) {
                    serverIdCol = "a.serverId";
                }

                // 构建默认待办查询SQL
                taskSql = String.format("select a.*,case when a.stepover=1 then '已终审' else  '需：'+P_GetFlowNextName(a.typeCode,a.stepcode) end  as endflow from ( " +
                                "select po.employeename,a.keyvalue Billdocument_Id,a.stepcode,1 lx ,a.OperatorId,a.operatedate," +
                                "isnull(a1.stepover,a2.stepover) stepover,isnull( a1.billtype,a2.billtype) billtype,REPLACE(a.operators,'管理员,','') oper,a.typeCode, %s serverId " +
                                "from wms_billflowOperView a " +
                                "left join wms_billflowOper a1 on a.modid=a1.modid and a.keyvalue=a1.keyvalue " +
                                "left join p_baseflowOper a2 on a.modid=a2.modid and a.keyvalue=a2.keyvalue " +
                                "left join P_employeetab  po on a.OperatorId=po.employeeid " +
                                "where CHARINDEX(',%s,',','+a.operators+',')>0 %s and typecode = '%s' " +
                                ") a " +
                                "where isnull(stepover,0)=0 order by operatedate desc",
                        serverIdCol, getUser().UserName, mulitAuditCond, moduleId);
            }
        }

        // 处理未配置任务SQL的情况
        if (isNullOrEmpty(taskSql)) {
            if (module == null || isNullOrEmpty(module.getMasterSql())) {
                response.setMsg("未配置模块任务语句，请检查！");
                return response;
            }
            // 使用模块主SQL获取数据
            response = GetModuleData(module, null, leftRecord, pms, null, false, false, 0, 0, true);
        } else {
            // 处理任务SQL查询逻辑
            Map<String, Object> record = null;
            Map<String, Object> leftRec = null;
            Map<String, Object> params = null;
            List<Object> leftRecordsList = null;
            String querySql = taskSql;

            // 解析参数
            if (!isNullOrEmpty(pms)) {
                params = (Map<String, Object>) JSON.Decode(pms, Map.class);
            }

            // 构建默认查询条件
            String defaultWhere = buildDefaultWhere(params);

            // 解析左侧记录
            if (!isNullOrEmpty(leftRecord)) {
                // 确保JSON数组格式
                if (!leftRecord.startsWith("[")) {
                    leftRecord = String.format("[%s]", leftRecord);
                }
                leftRecordsList = (List<Object>) JSON.Decode(leftRecord, List.class);

                // 取第一个元素作为左侧记录
                if (leftRecordsList != null && !leftRecordsList.isEmpty()) {
                    leftRec = (Map<String, Object>) leftRecordsList.get(0);
                }
            }

            // 处理查询SQL（替换参数等）
            querySql = dealQuerySql(querySql, record, leftRec, null, null, null, false, false, false);

            // 插入默认查询条件
            if (!isNullOrEmpty(defaultWhere)) {
                Map<String, String> whereDict = new HashMap<>();
                whereDict.put("$where", defaultWhere);
                querySql = new SqlAnalyzer(querySql).InsertWhere(whereDict, false, false, false);
            }

            // 保存无条件SQL用于错误重试
            String noCondSql = querySql;

            // 应用查询条件（根据缓存判断是否使用条件）
            if (!(module == null || moduleId.equals(taskModuleId))) {
                boolean useCondition = !taskModuleCondCache.containsKey(moduleId) || taskModuleCondCache.get(moduleId);
                if (useCondition) {
                    querySql = reqSearchCondition(module.getCondKey(), querySql, params, leftRec, false);
                }
            }
            try {
                // 执行查询并获取结果
                // 用于接收总数的容器
                List<Map<String, Object>> data = getDbOperator().executeDataTable(querySql, getStartsize(), getPageSize(), tot);
                response.setData(toHashTable(data));
                taskModuleCondCache.put(moduleId, true);
            } catch (Exception e) {
                // 执行出错时重试无条件SQL
                taskModuleCondCache.put(moduleId, false);
                log.error("待办sql执行错误，预计为条件字段不匹配!已尝试执行无条件sql!", e);
                List<Map<String, Object>> data = getDbOperator().executeDataTable(noCondSql, getStartsize(), getPageSize(), tot);
                response.setData(toHashTable(data));
            }
        }

        // 设置响应结果
        response.setSuccess(true);
        if (isWindowsDirver()) {
            BaseResponse otherRese = GetTaskColumns(moduleId, taskModuleId);
            if (getCurrPage() == 1 && taskModule.getWebCardTpl() != true) {
                response.attc = GetTaskCardGroup(taskModuleId, isBase, true, false);
            }
            response.setOther(otherRese.getData());

        } else if (moduleId != taskModuleId)//手机端待办总模块 taskmoduleindex 弃用
        {
            // response.attc =  GetTaskCardGroup(moduleId, isBase, false, true);
        }
        response.setTot(tot[0]); // tot变量需在当前类中定义为成员变量

        return response;
    }

    private Object GetTaskCardGroup(String moduleId, boolean isBase, boolean cellTpl, boolean defaultGroup) {
        // 1. 查询卡片配置表
        List<Map<String, Object>> cardTab = DataImpl.GetTaskMobileCardColumn(moduleId, isBase, cellTpl);
        Object mobileCards = null;

        // 2. 有数据时处理分组
        if (!cardTab.isEmpty()) {
            // 2.1 cellTpl=true：按ColName分组，返回Map（对应C#的Dictionary）
            if (cellTpl) {
                Map<String, List<MobileCard>> groupMap = cardTab.stream()
                        .map(MobileCard::new)
                        .collect(Collectors.groupingBy(MobileCard::getColName));
                return groupMap;
            }

            // 2.2 cellTpl=false：按GroupName分组，用Map封装匿名对象
            List<Map<String, Object>> groupList = cardTab.stream()
                    .map(MobileCard::new)
                    .collect(Collectors.groupingBy(MobileCard::getGroupName))
                    .entrySet().stream()
                    .map(entry -> {
                        String groupKey = entry.getKey();
                        List<MobileCard> cardList = entry.getValue();

                        // 获取第一个卡片
                        MobileCard firstCard = cardList.stream().findFirst().orElse(null);
                        if (firstCard == null) {
                            return null;
                        }

                        // 用Map模拟C#的匿名对象 { name, showText, mxId, items }
                        Map<String, Object> groupMap = new HashMap<>();
                        groupMap.put("name", groupKey); // 对应name = group.Key
                        groupMap.put("showText", firstCard.isGroupVisible()); // 对应showText = fCard.GroupVisible
                        groupMap.put("mxId", firstCard.getMxId()); // 对应mxId = fCard.MxId
                        groupMap.put("items", cardList); // 对应items = group.ToList()
                        return groupMap;
                    })
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());

            mobileCards = groupList;
        }
        // 3. 无数据且defaultGroup=true：构建默认分组（用Map封装）
        else if (defaultGroup) {
            // 构建默认卡片列表
            List<MobileCard> defaultItems = new ArrayList<>();

            // 卡片1：编号
            MobileCard card1 = new MobileCard();
            card1.setBold(true);
            card1.setContent("编号：{billdocument_id}");
            card1.setFontSize(16);
            card1.setRowHeight(40);
            card1.setRowId(1);
            card1.setSplitLine(true);
            defaultItems.add(card1);

            // 卡片2：申请人员
            MobileCard card2 = new MobileCard();
            card2.setContent("申请人员：{employeename}");
            card2.setFontSize(13);
            card2.setRowHeight(36);
            card2.setRowId(2);
            defaultItems.add(card2);

            // 卡片3：申请时间
            MobileCard card3 = new MobileCard();
            card3.setContent("申请时间：{operatedate}");
            card3.setFontSize(13);
            card3.setRowHeight(36);
            card3.setRowId(3);
            defaultItems.add(card3);

            // 卡片4：状态
            MobileCard card4 = new MobileCard();
            card4.setContent("状态：{endflow}");
            card4.setFontSize(13);
            card4.setRowHeight(36);
            card4.setRowId(4);
            defaultItems.add(card4);

            // 用Map封装默认分组的匿名对象
            Map<String, Object> defaultGroupMap = new HashMap<>();
            defaultGroupMap.put("name", "分组");
            defaultGroupMap.put("showText", false);
            defaultGroupMap.put("mxId", 0);
            defaultGroupMap.put("items", defaultItems);

            // 封装到List中（对应C#的ArrayList）
            List<Map<String, Object>> defaultGroupList = new ArrayList<>();
            defaultGroupList.add(defaultGroupMap);
            mobileCards = defaultGroupList;
        }

        return mobileCards;
    }

    /**
     * 获取待办配置，手机端使用
     *
     * @param moduleId 模块ID
     * @return 包含待办配置信息的BaseResponse
     */
    public BaseResponse GetTaskIniParams(String moduleId) {
        BaseResponse response = new BaseResponse();
        ModuleBaseEntity taskModule = GetModule(moduleId);

        if (taskModule == null) {
            response.setMsg(String.format("无效的目标模块%s，请检查！", moduleId));
            return response;
        }

        boolean isBase = taskModule instanceof BaseModule;
        List<Component> columns = GetAuditStepColumns(moduleId, "", "998", isBase);

        if (taskModule.getMuitlAudit()) {
            List<Map<String, Object>> dt = DataImpl.GetAuditStepInfos(moduleId, 0, isBase, "");
            AuditStep step = null;
            if (dt != null && !dt.isEmpty()) {
                Map<String, Object> row = dt.get(0);
                step = new AuditStep(row);
            }
            if (step != null) {
                taskModule.StepApplyText = step.getStepApplyText();
                taskModule.StepBackText = step.getStepBackText();
            }
        }
        //= operAble ? detail == null ? "审核" : detail.StepApplyText : "",
        //    stepBackText = operAble ? detail == null ? "反退" : detail.StepBackText : "",

        // 处理默认列信息
        if (columns == null || columns.isEmpty()) {
            columns = new ArrayList<>();
            columns.add(new Column() {{
                dataIndex = "billdocument_id";
            }});
            columns.add(new Column() {{
                dataIndex = "endflow";
            }});
            columns.add(new Column() {{
                dataIndex = "employeename";
            }});
            columns.add(new Column() {{
                dataIndex = "operatedate";
            }});
        }

        // 获取移动端卡片列配置
        // List<Map<String, Object>> cardTab = DataImpl.GetTaskMobileCardColumn(moduleId, isBase);
        Object MobileCards = GetTaskCardGroup(moduleId, isBase, false, true);
        List<Object> bbarList = new ArrayList<>();
        GridPanel maingrid = new GridPanel();
        maingrid.setColumns(columns);
        maingrid.MobileCards = MobileCards;
        DataStore ds = new DataStore();
        Map<String, Object> map = new HashMap<>();
        map.put("moduleId", moduleId);
        map.put("TaskModuleId", moduleId);
        ds.extraParams = map;
        maingrid.setStore(ds);
        //taskModule.Main = maingrid;
        response.setData(maingrid);
        response.setOther(taskModule);
        response.setSuccess(true);
        return response;
    }

    public BaseResponse DeleteProject(String menuId, String projectName) {
        LoginUserInfo user = getUser();
        return DataImpl.DeleteProject(menuId, projectName, user.UserId);
    }

    public BaseResponse SaveProject(String projectName, String projectValue, String menuId) {
        // 转换为存储格式：去除首尾的{}，将:替换为=，将,替换为|
        if (projectValue != null) {
            projectValue = projectValue.trim()
                    .replaceAll("^\\{|\\}$", "") // 移除首尾的{}
                    .replace(":", "=")
                    .replace(",", "|");
        }
        LoginUserInfo user = getUser();
        return DataImpl.SaveProject(projectName, projectValue, menuId, user.UserId);
    }

    /**
     * 获取方案列表
     *
     * @param menuId 菜单ID
     * @return 方案列表，每个元素为包含"name"和"vals"的Hashtable
     */
    public List<Hashtable<String, Object>> GetSchemesList(String menuId) {
        // 调用数据层获取方案列表数据（返回List<Map>模拟DataTable）
        List<Map<String, Object>> dataList = DataImpl.GetSchemesList(menuId, getUser().UserId);

        List<Hashtable<String, Object>> result = new ArrayList<>();
        for (Map<String, Object> row : dataList) {
            Hashtable<String, Object> hashtable = new Hashtable<>();
            // 设置方案名称
            hashtable.put("name", row.get("projectname"));
            // 转换搜索值为JSON格式并设置
            String searchValue = row.get("searchvalue") != null ? row.get("searchvalue").toString() : "";
            hashtable.put("vals", ctJson(searchValue));
            result.add(hashtable);
        }
        return result;
    }

    /**
     * 将字符串转换为JSON格式
     * 例如：a=b|c=d 转换为 {"a":"b","c":"d"}
     *
     * @param vals 原始字符串
     * @return JSON格式字符串
     */
    private String ctJson(String vals) {
        // 替换等号为冒号，竖线为逗号，再包裹大括号包裹
        return "{" + vals.replace("=", "\":\"").replace("|", "\",\"") + "}";
    }

    /**
     * 获取审批界面配置
     *
     * @param moduleId   模块ID
     * @param stepId     步骤ID
     * @param idValue    主键值
     * @param leftRecord 父记录
     * @param isBase     是否为基础模块
     * @param stepCode   步骤编码
     * @param ver        版本号
     * @param tpl        是否使用模板
     * @return 基础响应对象
     */
    public BaseResponse GetAuditInfo(String moduleId, int stepId, String idValue, String leftRecord,
                                     boolean isBase, String stepCode, int ver, boolean tpl) throws CusException {
        BaseResponse response = new BaseResponse();
        ModuleBaseEntity module = null;
        AuditStep detail = null;
        List<Object> bbItems = null;

        // 获取模块信息
        if (isBase) {
            module = GetBaseModule(moduleId, getMenuId());
        }
        if (module == null) {
            module = GetBillModule(moduleId, getMenuId());
            isBase = false;
        }
        if (module == null) {
            response.setMsg(LanguageUtil.GetString("ModuleNotFound"));
            return response;
        }

        // 处理模板
        if (tpl) {
            boolean isBaseModule = module instanceof BaseModule;
            module.CusAddTpl = (DataImpl.GetModuleAddTpl(module.getModuleId(), isBaseModule));
        }

        // 设置模块参数
        module.IdValue = (idValue);
        if (!isNullOrEmpty(leftRecord)) {
            module.setLeftRecord((Hashtable<String, Object>) JSON.Decode(leftRecord));
        }
        stepCode = "";//20251224改来直接从对应单据信息中查询stepcode,避免误传
        BillStateEn stateEn = GetBillState(module, stepCode);
        if (isNullOrEmpty(stepCode)) {
            stepCode = stateEn.StepCode;
        }
        // 获取审核步骤信息
        detail = GetAuditStep(stepId, isBase, moduleId, stepCode);
        List<Field> cols = GetAuditMainCols(detail, module, isBase, idValue);

        // 验证审核数据是否存在
        if (module.Updrow == null) {
            String msg = String.format("未找到编号为【%s】的审核数据，请确认是否拥有审核权限、模板编号为【%s】的审核步骤【%s】的SQL是否配置正确！",
                    idValue, moduleId, stepCode);
            response.setSuccess(false);
            response.setMsg(msg);
            return response;
        }

        // 处理单据状态
        //BillStateEn stateEn = GetBillState(module, stepCode);
//        out.println(JSON.Encode(stateEn));
        stepId = (stepId <= 0 && stateEn != null) ? stateEn.StepId : stepId;

        String editFields = "";
        String detailEditFields = "";
        String requireFields;
        String requiredDetailFiedls = "";

        module.BillType = (stateEn != null ? stateEn.BillType : null);

        if (stateEn != null && stateEn.Finished) {
            requireFields = "";
            stepCode = "999";
            stateEn.StepCode = stepCode;
        } else {
            if (stateEn != null && !stepCode.equals(stateEn.StepCode) && detail != null) {
                detail = GetAuditStep(stepId, isBase, moduleId, stateEn.StepCode);
            }

            // 处理字段权限配置
            requireFields = (detail != null) ? "," + detail.getRequiredFields().toLowerCase() + "," : "";
            editFields = (detail != null) ? detail.getBillModifyFields() +
                                            (isNullOrEmpty(requireFields) ? "" : requireFields) : "";
            editFields = isNullOrEmpty(editFields) ?
                    "" : "," + editFields.trim().toLowerCase() + ",";

            requiredDetailFiedls = (detail != null) ? "," + detail.getRequiredDetailFields().toLowerCase() + "," : "";
            detailEditFields = (detail != null) ? "," + detail.getDetailModifyFields() +
                                                  (isNullOrEmpty(requiredDetailFiedls) ? "" : requiredDetailFiedls) + "," : "";
            detailEditFields = isNullOrEmpty(detailEditFields) ?
                    "" : "," + detailEditFields.trim().toLowerCase() + ",";

            stepCode = (detail != null) ? detail.getStepCode() : "999";
        }

        // 处理主表字段权限
        if (cols != null) {
            String finalEditFields = editFields;
            cols = cols.stream().map(com -> {
                com.setReadOnly(true);
                if (finalEditFields.contains("," + com.getName() + ",") &&
                        stateEn != null && !stateEn.Finished) {
                    com.setReadOnly(false);
                    if (requireFields.contains("," + com.getName() + ",")) {
                        ((Field) com).setAllowBlank(false);
                    }
                }
                return com;
            }).collect(Collectors.toList());
        }

        // 处理明细数据
        Object main = cols;
        Object details;

        if (ver > 1) {
            // 审批的附加明细（高版本）
            ModuleBaseEntity finalModule = module;
            details = DataImpl.GetAuditInfoDetails(moduleId, stepCode).stream()
                    .map(row -> GetOneModelDetail(row, finalModule, null, true))
                    .collect(Collectors.toList());
        } else {
            // 审批的附加明细（低版本）
            details = DataImpl.GetAuditInfoDetails(moduleId, stepCode).stream()
                    .map(row -> {
                        Map<String, Object> detailMap = new HashMap<>();
                        detailMap.put("title", row.get("attachname").toString());

                        List<Component> items = new ArrayList<>();
                        GridPanel gridPanel = new GridPanel();

                        // 处理列配置
                        List<Component> columns = DataImpl.GetAuditInfoDetailColumns(row.get("id").toString()).stream()
                                .map(colRow -> new RowColumn(colRow, true, createControl))
                                .collect(Collectors.toList());
                        gridPanel.setColumns(columns);

                        // 处理数据源
                        DataStore dataStore = new DataStore();
                        Map<String, Object> extraParams = new HashMap<>();
                        extraParams.put("detailId", row.get("id"));
                        dataStore.extraParams = (extraParams);
                        gridPanel.setStore(dataStore);

                        // 处理右键菜单
                        gridPanel.RightMenu = (GetRightMenu(row.get("formkey").toString(), 0, new Ref(bbItems), "RightMenuClick"));

                        items.add(gridPanel);
                        detailMap.put("items", items);
                        return detailMap;
                    }).collect(Collectors.toList());
        }

        // 处理基础明细
        List<Component> baseDetails = new ArrayList<>();
        if (!isBase) {
            // 单据的审批
            GridPanel billGrid = GetBillDetail((BillModule) module, "",
                    stateEn != null && !stateEn.Finished,
                    (stateEn != null && !stateEn.Finished) ? detailEditFields : "-null",
                    (stateEn != null && !stateEn.Finished) ? requiredDetailFiedls : "-null");

            billGrid.TbarItems = (cols);
            main = billGrid;
        } else {
            // 基础模块的审批
            List<Map<String, Object>> infoDT = DataImpl.GetAuditBaseDetails(moduleId, stepCode);
            if (infoDT != null && !infoDT.isEmpty()) {
                List<String> detailKeys = infoDT.stream()
                        .map(row -> row.get("detailKey").toString())
                        .collect(Collectors.toList());

                List<Map<String, Object>> baseDetailsDT = DataImpl.GetBaesModuleDetailsByFromkey(
                        String.join("','", detailKeys));

                for (Map<String, Object> baseRow : baseDetailsDT) {
                    Hashtable<String, Object> elseValHS = new Hashtable<>();

                    // 查找匹配的信息行
                    Map<String, Object> selInfo = infoDT.stream()
                            .filter(row -> row.get("detailKey").toString().equalsIgnoreCase(
                                    baseRow.get("formkey").toString()))
                            .findFirst()
                            .orElse(null);

                    if (selInfo != null) {
                        String modifyFields = Objects.toString(get(selInfo, "modifyFields"), "");
                        int displayMode = ToInt32(selInfo.get("displayMode"));
                        int addShowMode = ToInt32(selInfo.get("addShowMode"));

                        modifyFields = isNullOrEmpty(modifyFields) ? "," : modifyFields;
                        elseValHS.put("modifyFields", modifyFields);
                        elseValHS.put("displayMode", displayMode);
                        elseValHS.put("addShowMode", addShowMode);

                        Component oneCmp = GetOneModelDetail(baseRow, (BaseModule) module, elseValHS, false);
                        if (oneCmp != null) {
                            baseDetails.add(oneCmp);
                        }
                    }
                }
            }
        }

        // 处理窗口尺寸
        List<Map<String, Object>> winSizeTb = DataImpl.GetAuditWindowSize(module.getModuleId());
        Object width = null, height = null;
        if (!winSizeTb.isEmpty()) {
            width = winSizeTb.get(0).get("width");
            height = winSizeTb.get(0).get("height");
        }
        Object nomalmenu = GetRightMenu(module.getModuleId(), 1, new Ref(bbItems), "RightMenuClick");
        // List<object> bbItems = new List<object>();
        Object rightMenu = isBase ? GetRightMenu(module.getModuleId(), 0, new Ref(bbItems), "toolClick")
                : nomalmenu;
        Map<String, Object> responseData = new HashMap<>();
        responseData.put("module", module);
        responseData.put("RightMenu", rightMenu);
        responseData.put("main", main);
        responseData.put("details", details);
        responseData.put("baseDetails", baseDetails);
        responseData.put("stepId", stepId);
        responseData.put("stepCode", stepCode);
        responseData.put("stepName", (detail != null ? detail.getStepName() : null));
        responseData.put("idValue", idValue);
        responseData.put("width", width);
        responseData.put("height", height);
        // 处理附加信息
        boolean operAble = stateEn != null && stateEn.OperAble;
        boolean disjcp = detail == null ? false : detail.getDisJCP();
        Map<String, Object> otherData = new HashMap<>();

        // 审核意见数据源
        DataStore boxStore = new DataStore();
        boxStore.data = (DataImpl.GetAuditRemark());
        otherData.put("boxStore", boxStore);

        // 步骤历史数据源
        DataStore gridStore = new DataStore();
        gridStore.data = GetAuditStepAndHis(module, module.getModuleId(), idValue, stateEn == null ? 0 : ToInt32(stateEn.StepCode), isBase, module.BillType);//DataImpl.GetBaseFlowStep(idValue, isBase));
        otherData.put("gridStore", gridStore);

        // 右键菜单
        otherData.put("normalmenu", nomalmenu);// GetRightMenu(module.getModuleId(), 1, new Ref(bbItems), "RightMenuClick"));

        // 操作按钮文本
        otherData.put("SaveText", operAble ? (isNullOrEmpty(editFields) ? "" : "保存") : "");
        otherData.put("stepApplyText", operAble ? (detail == null ? "审核" : detail.getStepApplyText()) : "");
        otherData.put("stepBackText", operAble ? (detail == null ? "反退" : detail.getStepBackText()) : "");
        otherData.put("StepClosed", operAble ? (stateEn == null ? false : stateEn.StepClosed) : false);
        otherData.put("StepCloseText", operAble ? (detail == null ? "关闭" : detail.getStepCloseText()) : "");
        otherData.put("StepCloseContent", operAble ? (detail == null ? "关闭" : detail.getStepCloseContent()) : "");
        otherData.put("StepCloseTip", operAble ? (detail == null ? "是否关闭？" : detail.getStepCloseTip()) : "");
        otherData.put("auditContent", detail == null ? "" : detail.getAuditContent());
        otherData.put("disjcp", disjcp);
        otherData.put("isBase", isBase);

        response.setData(responseData);
        response.setOther(otherData);
        response.setSuccess(true);

        // 记录系统日志
        sysLog(String.format("%s进入%s模块", getUser().UserName, module.getMenuName()), "进入模块");

        return response;
    }

    private List<Field> GetAuditMainCols(AuditStep detail, ModuleBaseEntity module, boolean isBase, String idValue) {
        // 20240412确认所有审批界面取值取主表的sql
        String stepSql = ""; // detail == null ? "" : detail.getStepSql();
        if (isBase) {
            BaseResponse fieldResponse = GetAddOrUpdFields(module, idValue, 0, stepSql, false, false);
            return (List<Field>) fieldResponse.getData();
        } else {
            return GetBillMasterFields((BillModule) module, stepSql);
        }
    }

    /**
     * 获取审批附加明细的数据
     *
     * @param detailId   明细ID
     * @param record     记录数据
     * @param leftRecord 左侧记录数据
     * @param pams       参数
     * @return 基础响应对象
     */
    public BaseResponse GetAuditDetailData(int detailId, String record, String leftRecord, String pams) {
        BaseResponse response = new BaseResponse();
        if (detailId <= 0) {
            return response;
        }

        // 获取审批信息明细
        List<Map<String, Object>> dtVal = DataImpl.GetAuditInfoDetails(null, null, detailId);
        if (!dtVal.isEmpty()) {
            Map<String, Object> row = dtVal.get(0);
            String querySql = (String) get(row, "attachsql", "");
            String unionmodule = (String) get(row, "unionmodule", "");
            String unionCond = (String) get(row, "unioncond", "");

            if (!isNullOrEmpty(querySql)) {
                // 处理查询SQL
                querySql = dealQuerySql(querySql, record, leftRecord, pams, null, null, false, false, false);

                // 执行查询并获取总数
                int[] totHolder = new int[1]; // 用于接收总数的数组
                List<Map<String, Object>> _dtVal = getDbOperator().executeDataTable(
                        querySql, getStartsize(), getPageSize(), totHolder);
                if (_dtVal != null && !_dtVal.isEmpty() && !_dtVal.get(0).isEmpty()) {
                    // 1. 提取所有列名（从第一行Map中获取key，对应C#的DataColumn集合）
                    Map<String, Object> firstRow = _dtVal.get(0);
                    // 2. 转换列信息：列名 → {dataIndex:列名, xtype:类型}（对应C#的Select逻辑）
                    List<Map<String, Object>> mainDataColumns = firstRow.keySet().stream()
                            .map(columnName -> {
                                // 取第一行该列的值，用于推导类型（模拟C#的col.DataType）
                                Object columnValue = firstRow.get(columnName);
                                // 直接构建Map（key-value直写，无需中间变量）
                                return new HashMap<String, Object>() {{
                                    put("dataIndex", columnName); // 对应dataIndex = col.ColumnName
                                    put("xtype", PublicUtil.TypeToColumnType(columnValue.getClass())); // 对应xtype = TypeToColumnType
                                }};
                            })
                            .collect(Collectors.toList());

                    // 3. 封装外层Map（对应C#的匿名对象 new { MainDataColumns = ... }）
                    Map<String, Object> otherMap = new HashMap<>();
                    otherMap.put("MainDataColumns", mainDataColumns);

                    // 4. 赋值给response.other（和原逻辑一致）
                    response.setOther(otherMap);
                }
                response.setData(toHashTable(_dtVal));
                response.setTot(totHolder[0]);
                response.setSuccess(true);
            } else if (!isNullOrEmpty(unionmodule)) {
                // 处理关联模块逻辑
                BaseModule module = GetBaseModule(unionmodule, "");
                BaseDetailModule dModule = new BaseDetailModule(row);
                return GetModuleDetailData(module, dModule, leftRecord, false, false, true, "");
            } else {
                response.setMsg("获取明细数据失败，未配置数据源sql或关联模块！");
            }
        }
        return response;
    }

    /**
     * 审核基础模块
     *
     * @param module  基础模块实体
     * @param data    数据
     * @param details 明细数据
     * @param stateEn 状态实体
     * @return 基础响应对象
     */
    public BaseResponse BaseAudit(BaseModule module, String data, String details, BillStateEn stateEn) throws
            UnsupportedEncodingException, CusException {
        final BaseResponse[] response = {new BaseResponse()};
        if (stateEn != null) {
            boolean isAudit = "F".equalsIgnoreCase(stateEn.Direction);
            boolean goOn = true;

            // 去掉事务，选人的时候会回滚，会报错
            if (isAudit && !isNullOrEmpty(data) && stateEn.comfirmFlag == 0 && stateEn.getSelectConfirmFlag() == 0) {
                response[0] = AddOrUpd(module.getModuleId(), data, details, null, false, null, stateEn.comfirmFlag, false);
                goOn = response[0].isSuccess() || LanguageUtil.GetString("NoneField").equals(response[0].getMsg());
            }

            if (goOn) {
                getpushImpl().basePush(module.IdValue, stateEn.StepCode, getMenuId(), () -> {
                    getEventHandler().callBeforeModuleAuditStateChange(module, stateEn, null);
                    try {
                        response[0] = Deal99Response(DataImpl.BaseAudit(module, stateEn), module.getLeftRecord(), module.getModuleId());
                    } catch (CusException e) {
                        throw new RuntimeException(e);
                    }
                    getEventHandler().callAfterModuleAuditStateChange(module, stateEn, response[0]);

                    String logMsg = String.format("%s->%s%s%s%s",
                            module.getTitle(), getUser().UserName, "审核",
                            module.IdValue, response[0].isSuccess() ? "成功" : ("失败：" + response[0].getMsg()));
                    sysLog(logMsg, "操作模块");

                    return response[0].isSuccess();
                });

                if (!response[0].isSuccess() && !isNullOrEmpty(details)) {
                    response[0].setMsg(response[0].getMsg() + "<br />提交失败，但明细数据修改成功，请重新打开该页面避免重复提交信息！");
                }
            }
        }

        return response[0];
    }

    public BaseResponse BatchAudit(String moduleId, String direction, String datas) throws
            UnsupportedEncodingException, CusException {
        BaseResponse response = new BaseResponse();
        BaseResponse oneResponse = new BaseResponse();
        // 假设使用Jackson进行JSON解析
        List<Map<String, Object>> auditDatas = (List<Map<String, Object>>) JSON.Decode(datas, Map.class);
        ModuleEntity module = GetModule(moduleId);

        if (module == null || auditDatas == null || auditDatas.isEmpty()) {
            response.setMsg("未找到对应模块或者没有可审核的数据！");
            return response;
        }

        StringBuilder errs = new StringBuilder();
        int successTot = 0;

        for (Map<String, Object> data : auditDatas) {
            String stepCode = String.valueOf(data.getOrDefault("stepcode", ""));
            String idValue = String.valueOf(data.getOrDefault("idValue", ""));

            if (isNullOrEmpty(stepCode) || isNullOrEmpty(idValue)) {
                errs.append(String.format("单据【%s】，没有步骤码或主键值，信息不足,审核失败！</br>", module.IdValue));
                continue;
            }

            module.IdValue = (idValue);
            BillStateEn stateEn = GetBillState(module, stepCode);

            if (stateEn == null) {
                errs.append(String.format("单据【%s】，审核失败，未找到对应信息！</br>", module.IdValue));
                continue;
            }

            // 设置状态实体属性
            stateEn.Direction = (direction);
            stateEn.IsBack = (false);
            stateEn.comfirmFlag = (0);
            stateEn.Remark = ("批量审批");
            stateEn.BackStepCode = ("");
            stateEn.setSelectConfirmFlag(0);
            stateEn.nextSelectStepCode = ("");
            stateEn.nextSelectStepOper = ("");
            stateEn.comfirmOpers = ("");

            // 执行审核操作
            if (module instanceof BaseModule) {
                oneResponse = BaseAudit((BaseModule) module, "", "", stateEn);
            } else {
                oneResponse = BillAudit((BillModule) module, "", "", stateEn);
            }

            if (oneResponse.isSuccess()) {
                successTot++;
            }

            // 如果只有一条数据，直接返回该条数据的审核结果
            if (auditDatas.size() == 1) {
                return oneResponse;
            }

            if (!oneResponse.isSuccess()) {
                errs.append(String.format("单据【%s】，审核失败，%s！</br>", module.IdValue, oneResponse.getMsg()));
            }
        }

        // 设置批量审核结果
        response.setSuccess(successTot > 0);
        if (response.isSuccess()) {
            response.setMsg("操作成功" + successTot + "条");
        } else {
            response.setMsg("操作失败" + (!errs.isEmpty() ? ("," + errs) : ""));
        }

        return response;
    }

    public BaseResponse GetAuditBackSteps(String moduleId, String idValue, String stepCode, boolean isBase) throws
            CusException {
        BaseResponse response = new BaseResponse();
        ModuleBaseEntity module = GetModule(moduleId);
        module.IdValue = idValue;
        BillStateEn stateEn = GetBillState(module, stepCode);
        response.setData(DataImpl.GetAuditBackSteps(moduleId, idValue, stepCode, stateEn == null ? "" : stateEn.BillType + "", isBase));
        response.setOther(module.getNewWFVer() ? 1 : 0);
        response.setSuccess(true);
        return response;
    }

    /**
     * 获取审批主界面配置
     *
     * @param moduleId 模块ID
     * @param isBase   是否为基础模块
     * @return 基础响应对象
     */
    public BaseResponse GetAuditIniParams(String moduleId, boolean isBase) {
        BaseResponse response = new BaseResponse();
        ModuleBaseEntity module = null;

        // 根据是否为基础模块获取对应的模块信息
        if (isBase) {
            module = GetBaseModule(moduleId, getMenuId());
        }
        // 如果基础模块未找到，尝试获取单据模块
        if (module == null) {
            module = GetBillModule(moduleId, getMenuId());
            isBase = false;
        }

        // 模块未找到的错误处理
        if (module == null) {
            String errorMsg = LanguageUtil.GetString("InvalidCode");
            errorMsg += " 未找到模块号【" + moduleId + "】以及MenuId【" + getMenuId() + "】对应的(单据or基础模块)数据，请检查相应模块的配置是否正确！可能是多余的空格、回车，数字字母写错，模块暂未配置等情况！";
            response.setMsg(errorMsg);
            return response;
        }

        // 获取审核步骤信息（DataTable转为List<Map>）
        List<Map<String, Object>> auditStepInfos = DataImpl.GetAuditStepInfos(moduleId, 0, isBase, "");
        String idField = module.getIdField();

        if (auditStepInfos != null && !auditStepInfos.isEmpty()) {
            // 按步骤组分组处理
            Map<Object, List<Map<String, Object>>> groupedByStepGroup = new HashMap<>();
            for (Map<String, Object> row : auditStepInfos) {
                Object stepGroup = row.get("stepgroup");
                groupedByStepGroup.computeIfAbsent(stepGroup, k -> new ArrayList<>()).add(row);
            }

            // 构建tabs数据
            List<Object> tabs = new ArrayList<>();
            for (Map.Entry<Object, List<Map<String, Object>>> entry : groupedByStepGroup.entrySet()) {
                String stepGroupName = entry.getKey() != null ? entry.getKey().toString() : "";
                List<Map<String, Object>> groupRows = entry.getValue();

                // 构建当前组的items
                List<Component> items = new ArrayList<>();
                for (Map<String, Object> row : groupRows) {
                    String stepName = row.get("stepname") != null ? row.get("stepname").toString() : "";
                    String stepId = row.get("id") != null ? row.get("id").toString() : "";
                    String stepCode = row.get("stepcode") != null ? row.get("stepcode").toString() : "";

                    // 创建网格面板
                    GridPanel gridPanel = new GridPanel();
                    gridPanel.title = (stepName);
                    gridPanel.IdField = (idField);
                    gridPanel.setColumns(GetAuditStepColumns(moduleId, stepId, stepCode, isBase));

                    // 移动端卡片（非Windows驱动时设置）
                    if (!isWindowsDirver()) {
                        gridPanel.MobileCards = (GetAuditStepColumnCards(moduleId, stepCode, isBase));
                    }

                    // 设置数据存储
                    DataStore dataStore = new DataStore();
                    Map<String, Object> extraParams = new HashMap<>();
                    extraParams.put("moduleId", moduleId);
                    extraParams.put("stepId", row.get("id"));
                    extraParams.put("stepCode", stepCode);
                    dataStore.extraParams = (extraParams);
                    gridPanel.setStore(dataStore);

                    items.add(gridPanel);
                }

                // 添加分组到tabs
                ModuleBaseEntity finalModule = module;
                tabs.add(new HashMap<String, Object>() {{
                    put("text", stepGroupName);
                    put("count", GetStepDataCount(groupRows, finalModule, stepGroupName));
                    put("items", items);
                }});
            }

            // 添加"已完成单据"标签
            ModuleBaseEntity finalModule1 = module;
            tabs.add(new HashMap<String, Object>() {{
                put("text", "已完成单据");
                put("count", GetStepDataCount(null, finalModule1, "已完成单据"));
                put("items", GetAuditHistoryInfo(finalModule1));
            }});

            // 设置响应数据
            response.setData(tabs);
            response.setOther(module);
            response.setSuccess(true);
        } else {
            response.setMsg(" 未找到模块号【" + moduleId + "】对应的审批步骤数据，请检查相应模块的配置是否正确！可能模块编号重复！");
        }

        return response;
    }

    /**
     * 获取附件文件路径列表
     *
     * @param moduleId 模块ID
     * @param idValue  主键值
     * @param specNo   规格编号
     * @param stepCode 步骤编码
     * @param dot      连接符，默认"&"
     * @return 基础响应对象，包含文件路径列表和权限检查结果
     */
    public BaseResponse GetAttcFilePaths(String moduleId, String idValue, String specNo, String stepCode, String
            dot) {
        // 设置默认连接符
        if (dot == null || dot.isEmpty()) {
            dot = "&";
        }

        BaseResponse response = new BaseResponse();
        String[] dirTabId = new String[1];
        // 获取文件目录
        String fileFolder = DataImpl.GetAcFileFolder(moduleId, idValue, specNo, dirTabId);

        // 获取附件文件列表
        List<Map<String, Object>> fileList = DataImpl.GetAttcFiles(moduleId, idValue, dirTabId[0], specNo, stepCode);
        List<String> paths = new ArrayList<>();

        for (Map<String, Object> row : fileList) {
            // 获取文件名相关信息
            String sname = row.get("sname") != null ? row.get("sname").toString() : "";
            String vname = row.get("vname") != null ? row.get("vname").toString() : sname;
            StringBuilder fileName = new StringBuilder(vname);
            String tempFileName = fileName.toString();
            StringBuilder webPath = new StringBuilder(row.get("webpath") != null ? row.get("webpath").toString() : "");
            StringBuilder relPath = new StringBuilder();
            String createTime = row.get("CreateTime") != null ? row.get("CreateTime").toString() : "";
            String userName = row.get("username") != null ? row.get("username").toString() : "";

            // 处理webPath为空的情况
            if (webPath.isEmpty()) {
                try {
                    String appDomain = getAppDomain();
                    String attcPath = getAttcPath();
                    // 调用工具类获取文件保存路径
                    FileUtil.getFileSavePath(fileFolder, null, appDomain, attcPath, fileName, relPath, webPath);

                    // 处理webPath中的域名部分
                    if (!webPath.isEmpty()) {
                        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                        String scheme = request.getScheme();
                        String authority = request.getServerName() + ":" + request.getServerPort();
                        String baseUrl = scheme + "://" + authority;
                        webPath = new StringBuilder(webPath.toString().replace(baseUrl, "").replace("#", "%23"));
                    }
                } catch (UnsupportedEncodingException e) {
                    // 处理编码异常
                    log.error("Exception caught", e);
                }
            }

            // 处理文件名包含空格的情况
            if (fileName != null && tempFileName.contains(" ")) {
                try {
                    String encodedFileName = FileUtil.urlEncode(fileName.toString(), false, true);
                    String encodedTempFileName = FileUtil.urlEncode(tempFileName, false, true);
                    webPath = new StringBuilder(webPath.toString().replace(encodedFileName, encodedTempFileName));
                } catch (UnsupportedEncodingException e) {
                    log.error("Exception caught", e);
                }
            }

            // 拼接最终的webPath（包含用户和时间信息）
            webPath = new StringBuilder(String.format("%s?%s%s%s%s", webPath, dot,
                    URLEncoder.encode(userName, StandardCharsets.UTF_8),
                    dot, URLEncoder.encode(createTime, StandardCharsets.UTF_8)));
            paths.add(webPath.toString());
        }

        // 设置响应数据
        response.setData(String.join("", paths)); // 假设SJoin()是直接拼接，若有分隔符需调整
        response.setSuccess(true);
        // 设置权限检查结果（1表示有权限，0表示无权限）
        response.setOther(CheckAttcAuthory(moduleId, idValue));

        return response;
    }

    public BaseResponse GetAttcFilePaths(String moduleId, String idValue, String specNo, String stepCode) {
        return GetAttcFilePaths(moduleId, idValue, specNo, stepCode, "&");
    }

    public BaseResponse GetAcctTreeData(String moduleId, String specNo) {
        BaseResponse response = new BaseResponse();
        response.setData(DataImpl.GetAttTreeData(moduleId, specNo));
        response.setSuccess(true);
        return response;
    }

    /**
     * 获取流程图数据
     *
     * @param moduleId   模块ID
     * @param moduleType 模块类型
     * @param billType   单据类型
     * @param idValue    主键值
     * @param autoStep   是否自动步骤
     * @return 基础响应对象，包含流程图数据和相关信息
     */
    public BaseResponse GetFlowChartData(String moduleId, int moduleType, String billType, String idValue,
                                         boolean autoStep) throws CusException {
        BaseResponse response = new BaseResponse();
        Map<String, Object> other = new HashMap<>();

        // 获取模块信息
        ModuleBaseEntity module = GetModule(moduleId);
        if (module == null) {
            response.setMsg(String.format("无效模块%s", moduleId));
            return response;
        }

        // 确定模块类型（基础模块0/单据模块1）
        moduleType = (module instanceof BaseModule) ? 0 : 1;
        module.BillType = (NativeExtensionUtils.parseInt(billType, 0));

        int atStepCode = 0;
        boolean stepOver = false;

        // 处理主键值不为空的情况
        if (!isNullOrEmpty(idValue)) {
            module.IdValue = (idValue);
            BillStateEn stenEn = GetBillState(module);

            if (stenEn != null) {
                module.BillType = (stenEn.BillType);
                stepOver = stenEn.Finished;
                atStepCode = NativeExtensionUtils.parseInt(stenEn.StepCode, 0);
                other.put("atStepCode", atStepCode);
                other.put("stepover", stepOver ? 1 : 0);
            }
        }

        // 获取审批历史数据
        List<Map<String, Object>> hisDtVal;
        if (!isNullOrEmpty(idValue)) {
            hisDtVal = DataImpl.GetFlowStepHis(idValue, module instanceof BaseModule);
        } else {
            hisDtVal = null;
        }

        // 获取流程图配置数据并处理列名小写
        List<Map<String, Object>> allDtVal = DataImpl.GetFlowChartData(module);
        allDtVal = ConversionUtils.toLowerColumnName(allDtVal);

        // 筛选符合条件的步骤行（排除自动步骤）
        List<Map<String, Object>> filteredRows = null;
        if (!allDtVal.isEmpty()) {
            filteredRows = allDtVal.stream()
                    .filter(row -> autoStep ||
                            !toBoolean(row.get("autoStep")) &&
                                    (hisDtVal == null || hisDtVal.isEmpty() ||
                                            !hisDtVal.stream().anyMatch(_r ->
                                                    Objects.equals(_r.get("stepcode"), row.get("stepcode")) &&
                                                            toBoolean(_r.get("autoStep"))
                                            ))
                    )
                    .collect(Collectors.toList());
        }

        // 处理无有效步骤的情况
        if (filteredRows == null || filteredRows.isEmpty()) {
            response.setSuccess(true);
            response.setMsg("没有设置审批步骤或审批步骤都为自动审核！");
            return response;
        }

        // 处理自动步骤
        List<Map<String, Object>> autoRows = allDtVal.stream()
                .filter(row -> toBoolean(row.get("autoStep")) ||
                        (hisDtVal != null && !hisDtVal.isEmpty() &&
                                hisDtVal.stream().anyMatch(_r ->
                                        Objects.equals(_r.get("stepcode"), row.get("stepcode")) &&
                                                toBoolean(_r.get("autoStep"))
                                ))
                )
                .collect(Collectors.toList());

        //if (!autoRows.isEmpty())
        {
            // 获取最大步骤编码
            int maxStepCode = filteredRows.stream()
                    .mapToInt(row -> NativeExtensionUtils.parseInt(row.get("stepcode") + "", 0))
                    .max()
                    .orElse(0);

            for (Map<String, Object> row : filteredRows) {
                if (module.getNewWFVer()) {
                    // 处理新工作流版本的下一步骤
                    ArrayList<String> nextSteps = FindNewWFVerNotAutoNSteps(row, autoRows);
                    if (nextSteps.size() > 0) {
                        row.put("nextstepcode", sJoin(nextSteps, ","));
                    }
                    int currentStepCode = NativeExtensionUtils.parseInt(row.get("stepcode") + "", 0);

                    // 自动修正未设置的下一步骤
                    if (isNullOrEmpty(row.get("nextstepcode") + "") && currentStepCode < maxStepCode) {
                        // 2. 精准复刻C# LINQ逻辑：Where + Min（无结果时抛异常）
                        int minNextStep = allDtVal.stream()
                                // 转换为int（对应C# ToInt32()）
                                .mapToInt(_row -> NativeExtensionUtils.parseInt(_row.get("stepcode") + "", 0))
                                // 筛选：stepcode > 当前值（对应C# Where）
                                .filter(code -> code > currentStepCode)
                                // 取最小值（无结果时抛出NoSuchElementException，和C# Min抛异常一致）
                                .min()
                                // 移除orElse(0)，完全对齐C#无默认值的逻辑
                                .getAsInt();

                        // 3. 赋值（对应C# row["nextstepcode"] = ...）
                        row.put("nextstepcode", minNextStep);
                    }

                    // 拼接下一步骤编码
//                    String nextStepCodes = nextSteps.stream()
//                            .map(String::valueOf)
//                            .collect(Collectors.joining(","));
//                    row.put("nextstepcode", nextStepCodes);
                } else {
                    // 处理旧工作流版本的父步骤
                    String pcode = FindNotAutoPCode(row, autoRows);
                    if (!Objects.equals(pcode, row.get("pstepcode") + "")) {
                        row.put("pstepcode", NativeExtensionUtils.parseInt(pcode, 0));
                    }
                }
            }
        }

        // 处理已完成步骤
        List<Map<String, Object>> overRows = filteredRows.stream()
                .filter(row -> toBoolean(row.get("stepover")))
                .collect(Collectors.toList());

        if (!overRows.isEmpty()) {
            // 计算当前步骤编码
            int maxOverStepCode = overRows.stream()
                    .mapToInt(row -> NativeExtensionUtils.parseInt(row.get("stepcode") + "", 0))
                    .max()
                    .orElse(0);
            atStepCode = Math.max(atStepCode, maxOverStepCode);

            // 获取当前步骤的下一步骤
            int finalAtStepCode = atStepCode;
            Map<String, Object> currentStepRow = filteredRows.stream()
                    .filter(row -> NativeExtensionUtils.parseInt(row.get("stepcode") + "", 0) == finalAtStepCode)
                    .findFirst()
                    .orElse(null);

            String[] atStepNextCode = new String[0];
            if (currentStepRow != null && !isNullOrEmpty(currentStepRow.get("nextstepcode"))) {
                atStepNextCode = currentStepRow.get("nextstepcode").toString().split(",");
                atStepNextCode = Arrays.stream(atStepNextCode)
                        .filter(s -> !isNullOrEmpty(s))
                        .toArray(String[]::new);
            }

            // 处理下一步骤
            List<Map<String, Object>> nextStepCodeRow = null;
            if (atStepNextCode.length > 0) {
                String[] finalAtStepNextCode = atStepNextCode;
                nextStepCodeRow = filteredRows.stream()
                        .filter(row -> Arrays.asList(finalAtStepNextCode).contains(row.get("stepcode") + ""))
                        .collect(Collectors.toList());
            }

            int _atStepCode = 0;
            if (nextStepCodeRow != null && !nextStepCodeRow.isEmpty()) {
                _atStepCode = nextStepCodeRow.stream()
                        .mapToInt(row -> NativeExtensionUtils.parseInt(row.get("stepcode") + "", 0))
                        .min()
                        .orElse(0);
            } else if (nextStepCodeRow != null && atStepNextCode.length > 0) {
                int firstNextCode = NativeExtensionUtils.parseInt(atStepNextCode[0], 0);
                nextStepCodeRow = filteredRows.stream()
                        .filter(row -> NativeExtensionUtils.parseInt(row.get("stepcode") + "", 0) > firstNextCode)
                        .collect(Collectors.toList());

                if (!nextStepCodeRow.isEmpty()) {
                    _atStepCode = nextStepCodeRow.stream()
                            .mapToInt(row -> NativeExtensionUtils.parseInt(row.get("stepcode") + "", 0))
                            .min()
                            .orElse(0);
                }
            }

            // 处理虚拟节点
            String stepCodeStr = String.valueOf(_atStepCode);
            if (stepCodeStr.length() == 6 && stepCodeStr.startsWith("999")) {
                int final_atStepCode = _atStepCode;
                Map<String, Object> virtualStepRow = filteredRows.stream()
                        .filter(row -> NativeExtensionUtils.parseInt(row.getOrDefault("stepcode", "").toString(), 0) == final_atStepCode)
                        .findFirst()
                        .orElse(null);

                if (virtualStepRow != null) {
                    String _atStepNextCode = virtualStepRow.get("nextstepcode") + "";
                    if (!isNullOrEmpty(_atStepNextCode)) {
                        atStepNextCode = _atStepNextCode.split(",");
                        _atStepCode = Arrays.stream(atStepNextCode)
                                .mapToInt(code -> NativeExtensionUtils.parseInt(code, 0))
                                .min()
                                .orElse(0);
                    }
                }
            } else if (_atStepCode == 0) {
                _atStepCode = atStepCode + 100;
            }

            atStepCode = _atStepCode;
            other.put("atStepCode", atStepCode);
            other.put("atStepNextCode", Arrays.stream(atStepNextCode)
                    .mapToInt(Integer::parseInt)
                    .toArray());
        }

        // 处理审批人信息（替换SQL变量）
        if (module != null && !isNullOrEmpty(idValue)) {
            SetUpdateRow(module, "", idValue);
            Map<String, Object> actRow = module.Updrow;

            for (Map<String, Object> row : filteredRows) {
                // 获取操作人
                String operuser = DataImpl.GetSqlUser(
                        row.get("operoperators") + "",
                        getUser().UserId,
                        getUser().UserName,
                        actRow
                );

                // 处理隐藏的审批人信息
                String auditInfoNoView = WebConfigUtil.get("AuditInfoNoView", "");
                if (!isNullOrEmpty(auditInfoNoView)) {
                    for (String name : auditInfoNoView.split(",")) {
                        if (!isNullOrEmpty(name)) {
                            operuser = operuser.replace(name, "");
                        }
                    }
                    operuser = operuser.replace(",,", ",");
                }

                row.put("operUser", operuser);
                row.put("viewUser", DataImpl.GetSqlUser(
                        row.get("operbrowsers") + "",
                        getUser().UserId,
                        getUser().UserName,
                        actRow
                ));
            }
        }

        // 处理审批历史和状态信息
        if (!isNullOrEmpty(idValue)) {
            other.put("history", hisDtVal);
            other.put("hisData", DataImpl.GetBaseFlowStep(idValue, module instanceof BaseModule));
            other.put("status", DataImpl.GetFlowStepState(moduleId, idValue, module instanceof BaseModule));
        }

        // 设置响应数据
        other.put("newver", module.getNewWFVer());
        response.setData(ToFlowStepInfo(module, filteredRows, hisDtVal, stepOver, atStepCode));
        response.setOther(other);
        response.setSuccess(true);

        return response;
    }

    /**
     * 查找下一步，排除掉隐藏的步骤，新版审核
     *
     * @param row      当前步骤数据行（对应C#的DataRow，用Map模拟）
     * @param autoRows 自动步骤数据行集合（对应C#的IEnumerable<DataRow>，用List<Map>模拟）
     * @return 下一步骤代码列表
     */
    private ArrayList<String> FindNewWFVerNotAutoNSteps
    (Map<String, Object> row, List<Map<String, Object>> autoRows) {
        // 获取当前步骤的下一步骤代码，分割并过滤空字符串
        String nextStepCode = row.get("NextStepCode") != null ? row.get("NextStepCode").toString() : "";
        String[] nextCodes = nextStepCode.split(",");
        List<String> validNextCodes = new ArrayList<>();
        for (String code : nextCodes) {
            if (!code.trim().isEmpty()) {
                validNextCodes.add(code.trim());
            }
        }

        ArrayList<String> nCodes = new ArrayList<>();
        for (String code : validNextCodes) {
            // 查找自动步骤中是否存在当前步骤代码
            Map<String, Object> autoRow = autoRows.stream()
                    .filter(r -> {
                        String stepCode = r.get("stepcode") != null ? r.get("stepcode").toString() : "";
                        return stepCode.equals(code);
                    })
                    .findFirst()
                    .orElse(null);

            if (autoRow != null) {
                // 递归查找子步骤
                nCodes.addAll(FindNewWFVerNotAutoNSteps(autoRow, autoRows));
            } else {
                // 非自动步骤直接添加
                nCodes.add(code);
            }
        }
        return nCodes;
    }

    /**
     * 重新设置上一步骤，老版本审核
     *
     * @param row      当前步骤数据行（对应C#的DataRow，用Map模拟）
     * @param autoRows 自动步骤数据行集合（对应C#的IEnumerable<DataRow>，用List<Map>模拟）
     * @return 非自动的上一步骤代码
     */
    private String FindNotAutoPCode(Map<String, Object> row, List<Map<String, Object>> autoRows) {
        // 获取当前步骤的上一步骤代码
        String pStepCode = DataTableUtil.getStringValue(row, "pStepCode", "");
        if (isNullOrEmpty(pStepCode)) {
            return "";
        }

        // 查找自动步骤中是否存在当前上一步骤代码
        Map<String, Object> autoRow = autoRows.stream()
                .filter(r -> {
                    String stepCode = DataTableUtil.getStringValue(r, "stepcode", "");
                    return stepCode.equals(pStepCode);
                })
                .findFirst()
                .orElse(null);

        if (autoRow != null) {
            // 递归查找上一步骤的非自动步骤
            return FindNotAutoPCode(autoRow, autoRows);
        }

        return pStepCode;
    }

    /**
     * 将审批流程转换为前端需要的数据结构
     *
     * @param module     模块基础信息
     * @param source     源流程步骤数据（List<Map>模拟DataTable）
     * @param his        审批历史数据（List<Map>模拟DataTable）
     * @param stepOver   是否步骤结束
     * @param atStepCode 当前步骤编码
     * @return 转换后的流程步骤信息列表
     */
    private List<FlowStepInfo> ToFlowStepInfo(ModuleBaseEntity module, List<Map<String, Object>> source,
                                              List<Map<String, Object>> his, boolean stepOver, int atStepCode) {
        List<FlowStepInfo> steps = new ArrayList<>();

        if (module.getNewWFVer()) {
            // 新版流程处理逻辑
            for (Map<String, Object> row : source) {
                int stepCode = ToInt32(row.get("stepcode"));
                boolean stepover = toBoolean(row.get("stepover"), false) || atStepCode > stepCode;
                // 创建流程步骤信息（结合历史记录）
                FlowStepInfo info = new FlowStepInfo(row, GetAuditHisRow(stepCode, his, stepOver || stepover, atStepCode));
                steps.add(info);
            }
        } else {
            // 旧版流程处理逻辑
            Map<Integer, List<FlowStepInfo>> groupDict = new HashMap<>();
            FlowStepInfo pStepInfo = null;

            for (Map<String, Object> row : source) {
                int stepCode = ToInt32(row.get("stepcode"));
                int pStepCode = ToInt32(row.get("pStepCode"));
                String overSteps = (row.get("oversteps") + "");
                boolean stepover = toBoolean(row.get("stepover"), false) || atStepCode > stepCode;

                // 创建流程步骤信息（结合历史记录）
                FlowStepInfo info = new FlowStepInfo(row, GetAuditHisRow(stepCode, his, stepOver || stepover, atStepCode));

                if (pStepCode > 0) {
                    // 处理有父步骤的情况
                    // 查找同属父步骤的最大步骤编码
                    int endStepCode = source.stream()
                            .filter(r -> ToInt32(r.get("pStepCode")) == pStepCode)
                            .mapToInt(r -> ToInt32(r.get("stepcode")))
                            .max()
                            .orElse(999);

                    // 查找下一阶段的最小步骤编码
                    int nextStepCode = source.stream()
                            .filter(r -> ToInt32(r.get("stepcode")) > endStepCode)
                            .mapToInt(r -> ToInt32(r.get("stepcode")))
                            .min()
                            .orElse(999);

                    info.setNextSteps(String.valueOf(nextStepCode));

                    // 更新父步骤的下一步信息
                    if (pStepInfo != null) {
                        String nextSteps = pStepInfo.getNextSteps();
                        nextSteps = (nextSteps == null ? "" : nextSteps) + stepCode + ",";
                        pStepInfo.setNextSteps(nextSteps);
                    }
                } else {
                    // 处理无父步骤的情况
                    if (pStepInfo != null && isNullOrEmpty(pStepInfo.getNextSteps())) {
                        pStepInfo.setNextSteps(String.valueOf(stepCode));
                    }
                    pStepInfo = info;
                }

                steps.add(info);
            }
        }

        return steps;
    }

    /**
     * 获取指定步骤的审核历史记录
     *
     * @param stepCode   目标步骤编码
     * @param hisSource  审核历史数据源
     * @param stepOver   是否允许步骤结束状态
     * @param atStepCode 当前步骤编码
     * @return 符合条件的审核历史记录数组，无结果时返回空数组而非null
     */
    private Map<String, Object>[] GetAuditHisRow(int stepCode, List<Map<String, Object>> hisSource,
                                                 boolean stepOver, int atStepCode) {
        // 空数据源直接返回空数组
        if (hisSource == null || hisSource.isEmpty()) {
            return new Map[0];
        }

        // 未结束状态且目标步骤大于等于当前步骤时返回空数组
        if (!stepOver && stepCode >= atStepCode) {
            return new Map[0];
        }

        List<Map<String, Object>> resultList = new ArrayList<>();
        for (Map<String, Object> row : hisSource) {
            // 提取并转换步骤编码，默认值为-1（无效步骤）
            int hisStepCode = ToInt32(get(row, "stepcode", null));

            // 历史步骤小于目标步骤时终止循环（假设数据按步骤倒序排列）
            if (hisStepCode < stepCode) {
                break;
            }

            // 匹配目标步骤时添加到结果集
            if (hisStepCode == stepCode) {
                resultList.add(row);
            }
        }

        // 转换为数组并返回（避免ClassCastException）
        @SuppressWarnings("unchecked")
        Map<String, Object>[] resultArray = resultList.toArray(new Map[0]);
        return resultArray;
    }

    public BaseResponse GetFlowChartOption(String moduleId, int moduleType, String billType) {
        BaseResponse response = new BaseResponse();
        // 假设getFlowChartOption返回的是List<Map>，模拟DataTable结构
        List<Map<String, Object>> dtVal = DataImpl.GetFlowChartOption(moduleId, moduleType, billType);

        Map<String, Object> privateCfg = new HashMap<>();

        for (Map<String, Object> row : dtVal) {
            try {
                // 将option字段的JSON字符串转换为Map
                String optionJson = String.valueOf(row.get("option"));
                Map<String, Object> op = (Map<String, Object>) JSON.Decode(optionJson);

                // 获取配置名称
                String cfgName = String.valueOf(row.get("cfgName"));
                // 添加到私有配置
                privateCfg.put(cfgName, op);
            } catch (Exception e) {
                // 处理JSON解析异常或空值异常
                response.setSuccess(false);
                response.setMsg("解析流程图配置失败: " + e.getMessage());
                return response;
            }
        }

        response.setData(privateCfg);
        response.setSuccess(true);
        return response;
    }

    /**
     * 更新流程图配置（包含全局配置和状态配置分离处理）
     *
     * @param moduleId   模块ID
     * @param moduleType 模块类型
     * @param billType   单据类型
     * @param options    配置JSON字符串
     * @return 响应结果
     */
    public BaseResponse UpdateFlowChartOption(String moduleId, int moduleType, String billType, String options) {
        BaseResponse response = new BaseResponse();
        try {
            // 将JSON字符串转换为Map（替代C#的Hashtable）
            Map<String, Object> ops = (Map<String, Object>) JSON.Decode(options);

            // 分离全局配置和状态配置
            Map<String, Object> globalCfg = new HashMap<>();
            Map<String, Object> stateCfg = new HashMap<>();

            Set<String> keys = null;
            if (ops != null) {
                keys = ops.keySet();
            }
            if (keys != null) {
                for (String key : keys) {
                    if (key.contains("flowstate")) {
                        // 处理状态配置（嵌套Map）
                        Object cfgObj = ops.get(key);
                        if (cfgObj instanceof Map) {
                            @SuppressWarnings("unchecked")
                            Map<String, Object> cfgs = (Map<String, Object>) cfgObj;
                            stateCfg.putAll(cfgs);
                        }
                    } else {
                        // 处理全局配置
                        globalCfg.put(key, ops.get(key));
                    }
                }
            }

            // 将全局配置添加到状态配置中
            stateCfg.put("globalCfg", globalCfg);

            // 调用数据层更新方法
            response = DataImpl.UpdateFlowChartOption(moduleId, moduleType, billType, stateCfg, true);

        } catch (Exception e) {
            response.setSuccess(false);
            response.setMsg("更新流程图配置失败: " + e.getMessage());
        }
        return response;
    }

    /**
     * 部分更新流程图配置（直接传递原始配置）
     *
     * @param moduleId   模块ID
     * @param moduleType 模块类型
     * @param billType   单据类型
     * @param options    配置JSON字符串
     * @return 响应结果
     */
    public BaseResponse UpdatePartFlowChartOption(String moduleId, int moduleType, String billType, String options) {
        BaseResponse response = new BaseResponse();
        try {
            // 将JSON字符串转换为Map
            Map<String, Object> ops = (Map<String, Object>) JSON.Decode(options);

            // 直接调用数据层更新方法（不做配置分离）
            response = DataImpl.UpdateFlowChartOption(moduleId, moduleType, billType, ops, false);

        } catch (Exception e) {
            response.setSuccess(false);
            response.setMsg("部分更新流程图配置失败: " + e.getMessage());
        }
        return response;
    }

    /**
     * 获取需要生成流程图的模块列表
     *
     * @return 响应结果
     */
    public BaseResponse GetUpdFlowChartList() {
        BaseResponse response = new BaseResponse();
        try {
            // 调用数据层获取列表
            Map<String, Object> dtVal = DataImpl.GetUpdFlowChartList();

            response.setData(dtVal);
            response.setSuccess(true);
            response.setMsg("成功获取了需要生成流程图的模块列表。");

        } catch (Exception e) {
            response.setSuccess(false);
            response.setMsg("获取流程图模块列表失败: " + e.getMessage());
        }
        return response;
    }

    public BaseResponse GetTimeEvent() {
        BaseResponse response = new BaseResponse();
        try {
            //【1126】BS里程碑事件：
            List<Map<String, Object>> result = jdbcTemplate.queryForList("select * from P_SystemDevHisTab");
            response.setData(result);
            response.setSuccess(true);
            return response;
        } catch (Exception ex) {
            response.setSuccess(true);
            response.setData(false);
            response.setMsg("数据库中无【里程碑事件】表P_SystemDevHisTab");
            return response;
        }
    }

    /**
     * 获取甘特图模块数据
     *
     * @param moduleId      模块ID
     * @param record        记录参数
     * @param leftRecord    左侧记录参数
     * @param pams          额外参数
     * @param where         条件语句
     * @param multi         是否多记录
     * @param grid          是否网格模式
     * @param contextMenuId 上下文菜单ID
     * @return 响应结果
     */
    public BaseResponse GanttGetModuleData(String moduleId, String record, String leftRecord,
                                           String pams, String where, boolean multi, boolean grid,
                                           int contextMenuId) {
        BaseResponse response = new BaseResponse();

        // 获取基础模块信息
        BaseModule module = GetBaseModule(moduleId, getMenuId());
        if (module == null) {
            response.setSuccess(false);
            response.setMsg("未找到该模块编号对应模块！");
            return response;
        }

        // 【=1=】获取第一级数据（资源ID相关）
        List<Map<String, Object>> fieldsDT = GetColumnRows(moduleId);
        String firstSql = "";

        for (Map<String, Object> oneField : fieldsDT) {
            String resourceId = DataTableUtil.getRowVal(oneField, "FieldName", "") + "";
            if (resourceId.toLowerCase().contains("resourceid")) {
                firstSql = DataTableUtil.getRowVal(oneField, "fieldsql", "") + "";
                break;
            }
        }

        // 【++ 1】v1版本处理（未找到第一级SQL时直接返回基础数据）
        if (firstSql == null || firstSql.isEmpty()) {
            response = GetModuleData(module, record, leftRecord, pams, where, multi, grid, contextMenuId, 0, false);
            response.setMsg("v1");
            return response;
        }

        // 执行第一级SQL查询并转换为哈希表列表
        List<Map<String, Object>> firstDataList = jdbcTemplate.queryForList(firstSql);
        List<Map<String, Object>> firstHS = new ArrayList<>();
        for (Map<String, Object> row : firstDataList) {
            firstHS.add(new HashMap<>(row)); // 转换为可修改的HashMap
        }

        // 【=2=】获取第二级数据
        BaseResponse secondRP = GetModuleData(module, record, leftRecord, pams, where, multi, grid, contextMenuId, 0, false);
        BaseModule theOther = (BaseModule) secondRP.getOther();
        String prefix = theOther.getMenuPrefix(); // 例如：billdocument_id
        String resourceIdStr = (prefix + "resourceid").toLowerCase();

        // 初始化第一级数据的子项和树ID
        for (int i = 0; i < firstHS.size(); i++) {
            Map<String, Object> oneFirst = firstHS.get(i);
            oneFirst.put("items", new ArrayList<Map<String, Object>>());
            oneFirst.put("treeId", "_" + (i + 1));
            oneFirst.put(prefix + "resourceid", DataTableUtil.getRowVal(oneFirst, "speciesno", ""));
            oneFirst.put(prefix + "subject", DataTableUtil.getRowVal(oneFirst, "speciesname", ""));
        }

        // 关联第二级数据到第一级
        boolean isV2 = false;
        @SuppressWarnings("unchecked")
        List<Map<String, Object>> secondOrigin = (List<Map<String, Object>>) secondRP.getData();

        for (int i = 0; i < secondOrigin.size(); i++) {
            Map<String, Object> oneSecond = secondOrigin.get(i);
            String subId = DataTableUtil.getRowVal(oneSecond, resourceIdStr, "") + "";

            for (Map<String, Object> oneFirst : firstHS) {
                String topId = DataTableUtil.getRowVal(oneFirst, "speciesno", "") + "";
                if (subId.contains(topId)) {
                    isV2 = true;
                    oneSecond.put("treeId", oneFirst.get("treeId") + "_" + (i + 1));
                    @SuppressWarnings("unchecked")
                    List<Map<String, Object>> items = (List<Map<String, Object>>) oneFirst.get("items");
                    items.add(oneSecond);
                    break;
                }
            }
        }

        // 组装响应结果
        if (isV2) {
            response.setSuccess(true);
            response.setData(firstHS);
            response.setTot(firstHS.size());
        } else {
            response = secondRP;
            response.setMsg("v1");
        }

        return response;
    }

    public BaseResponse GetPrintInfo(String moduleId, String printName, String recStr) {
        if (UpdateImpl.getVersion() < 1035) {
            return GetPrintInfo_old(moduleId, printName, recStr);
        }
        BaseResponse response = new BaseResponse();
        Map<String, Object> record = null;
        if (!isNullOrEmpty(recStr)) {
            record = (Map<String, Object>) JSON.Decode(recStr);
        }
        response.setData(GetPrintInfo(moduleId, printName, record));
        response.setSuccess(true);
        return response;
    }

    /**
     * 获取旧版打印信息
     *
     * @param moduleId  模块ID
     * @param printName 打印名称
     * @param recStr    记录字符串（JSON格式）
     * @return 包含打印信息的响应对象
     */
    private BaseResponse GetPrintInfo_old(String moduleId, String printName, String recStr) {
        BaseResponse response = new BaseResponse();
        Map<String, Object> retData = new HashMap<>();
        Map<String, Object> saveRec = new HashMap<>();
        Map<String, Object> record = null;

        // 解析记录JSON字符串
        if (recStr != null && !recStr.isEmpty()) {
            record = (Map<String, Object>) JSON.Decode(recStr, Map.class);
        }

        // 获取打印SQL信息
        boolean isBaseModule = DataImpl.IsBaseModule(moduleId);
        List<Map<String, Object>> printSqls = DataImpl.GetPrintSqls(moduleId, recStr, isBaseModule);
        Map<String, Object> dtVal = printSqls.isEmpty() ? null : printSqls.get(0);

        // 设置基础打印信息
        retData.put("dllcoid", moduleId);
        retData.put("name", printName);
        retData.put("dbServer", WebConfigUtil.get("dbServer"));
        retData.put("dbName", WebConfigUtil.get("dbName"));
        retData.put("operatorname", getUser().UserName);

        // 处理SQL语句
        if (dtVal != null) {
            for (Map.Entry<String, Object> entry : dtVal.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue() != null ? entry.getValue().toString() : "";

                // 处理以"sql"开头的键对应的SQL语句
                if (key.startsWith("sql") && !value.isEmpty()) {
                    value = dealQuerySql(value, record, null, null, null, null, false, false, false);
                    retData.put(key, value);
                }
            }
        }

        // 设置响应数据
        response.setData(retData);
        response.setSuccess(true);
        return response;
    }

    /**
     * <summary>
     * /// 获取web打印状态，判断依据为数据存在则没打印，反之已打印，应用于20240830后的打印程序
     * /// </summary>
     * /// <param name="pid"></param>
     * /// <returns></returns>
     **/
    public BaseResponse GetPrintSta(int pid) {
        BaseResponse response = new BaseResponse();
        response.setSuccess(true);
        response.setData(DataImpl.GetPrintSta(pid));
        return response;
    }

    public BaseResponse GetWebPrintInfo(String moduleId, String printName, String recStr, boolean isBase) {
        BaseResponse response = new BaseResponse();
        List<Map<String, Object>> dtval = DataImpl.GetWebPrintInfo(moduleId, printName);
        response.setSuccess(true);
        response.setData(dtval);
        response.setOther(GetPrintData(moduleId, recStr, DataImpl.IsBaseModule(moduleId)));
        return response;
    }

    /**
     * 获取打印数据
     *
     * @param moduleId 模块标识
     * @param recStr   记录字符串（JSON格式）
     * @param isBase   是否为基础模块
     * @return 打印数据列表（每个元素为查询结果的Map集合）
     */
    private List<List<Map<String, Object>>> GetPrintData(String moduleId, String recStr, boolean isBase) {
        // 解析记录JSON字符串
        Map<String, Object> record = null;
        if (recStr != null && !recStr.isEmpty()) {
            record = (Map<String, Object>) JSON.Decode(recStr, Map.class);
        }

        // 获取打印SQL信息
        List<Map<String, Object>> printSqls = DataImpl.GetPrintSqls(moduleId, recStr, isBase);
        Map<String, Object> dtVal = printSqls.isEmpty() ? null : printSqls.get(0);

        List<List<Map<String, Object>>> retDict = new ArrayList<>();
        if (dtVal != null) {
            for (Map.Entry<String, Object> entry : dtVal.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue() != null ? entry.getValue().toString() : "";

                // 处理以"sql"开头的有效SQL语句
                if (key.startsWith("sql") && !value.isEmpty()) {
                    // 处理SQL语句（替换参数等）
                    value = dealQuerySql(value, record, null, null, null, null);

                    try {
                        // 执行SQL查询并将结果转换为Map集合列表
                        List<Map<String, Object>> queryResult = jdbcTemplate.queryForList(value);
                        retDict.add(queryResult);
                    } catch (Exception e) {
                        // 忽略异常，对应原代码的空catch块
                    }
                }
            }
        }
        return retDict;
    }

    /// <summary>
    /// 添加或修改web打印模板
    /// </summary>
    /// <param name="moduleId">The module identifier.</param>
    /// <param name="printName">Name of the print.</param>
    /// <param name="content">The content.</param>
    /// <param name="id">The identifier.</param>
    /// <returns>System.String.</returns>
    /// <remarks>
    /// </remarks>
    public String AddOrUpdWebPrint(String moduleId, String printName, String content, String id) {
        return DataImpl.AddOrUpdWebPrint(moduleId, printName, content, id);
    }

    /**
     * 增加下载计数
     *
     * @param moduleId 模块ID
     * @param ids      记录ID集合（逗号分隔）
     */
    public void AddDownloadCount(String moduleId, String ids) {
        // 获取模块信息
        ModuleBaseEntity module = GetModule(moduleId);

        // 验证模块信息及必要字段
        if (module != null && !isNullOrEmpty(module.getMasterTable()) && !isNullOrEmpty(module.getIdField())) {
            // 处理ID集合，替换逗号为','','格式（适配SQL的IN条件）
            String formattedIds = ids.replace(",", "','");

            // 构建SQL：检查字段是否存在，存在则更新下载计数
            String sql = String.format(
                    "if col_length('%s', 'downloadCount') is not null " +
                            "exec('update %s set downloadCount = isnull(downloadCount, 0) + 1 where %s in (''%s'')')",
                    module.getMasterTable(),
                    module.getMasterTable(),
                    module.getIdField(),
                    formattedIds
            );

            // 执行SQL
            jdbcTemplate.execute(sql);
        }
    }

    /**
     * 查询主表明细数据用于导出
     *
     * @param moduleId    模块ID
     * @param detailId    明细ID
     * @param mainData    主数据列表
     * @param index       分批索引
     * @param multi       是否多记录
     * @param grid        是否表格视图
     * @param isAuditAttc 是否为审核附件
     * @return 包含导出数据的BaseResponse
     */
    public BaseResponse GetExportData(String moduleId, int detailId, List<Map<String, Object>> mainData, int index,
                                      boolean multi, boolean grid, boolean isAuditAttc) {
        BaseResponse response = new BaseResponse();
        BaseModule module = GetBaseModule(moduleId, getMenuId());
        BaseDetailModule dModule = GetBaseDetailModuel(detailId, isAuditAttc);

        if (module == null || dModule == null) {
            return response;
        }

        BaseModule oldModule = module;
        // 处理联合模块
        if (module.getModuleId().equals(dModule.getUnionMenuCode())) {
            module = GetBaseModule(dModule.getUnionKey(), getMenuId());
        }

        // 构建明细SQL
        String detailSql = isAuditAttc ? module.getMasterSql() : dModule.getUnionSql();
        String dformkey = dModule.getFromkey();
        String dmoduleId = dModule.getModuleId();

        // 处理联合菜单代码
        if (dModule.getUnionMenuCode() != null && !dModule.getUnionMenuCode().isEmpty()) {
            BaseModule unionModule = GetBaseModule(dModule.getUnionMenuCode(), getMenuId());
            if (unionModule == null || unionModule.getMasterSql() == null || unionModule.getMasterSql().isEmpty()) {
                return response;
            }
            detailSql = unionModule.getMasterSql();
            dformkey = unionModule.getModuleId();
            dmoduleId = unionModule.getModuleId();
        }

        if (detailSql == null || detailSql.isEmpty()) {
            response.setMsg("noDetailSql");
            return response;
        }

        boolean isChart = dModule.getUnionType() == 1;
        String detailIdKey = trimBraces(dModule.getUnionField());
        String mainDataIdKey = (dModule.getUnionParentField() == null || dModule.getUnionParentField().isEmpty())
                ? module.getIdField()
                : trimBraces(dModule.getUnionParentField());

        Map<String, Object> responseData = new HashMap<>();
        // 总记录数

        // 存在明细SQL但不存在关联字段的情况
        if (detailIdKey == null || detailIdKey.isEmpty()) {
            for (int i = 0; i < mainData.size(); i++) {
                int indexVal = index > 0 ? i + (index - 1) * 2000 : i;
                String sql = detailSql;
                // 处理查询SQL（假设DealQuerySql已实现）
                sql = dealQuerySql(sql, mainData.get(i), mainData.get(i), null, null, null);

                // 执行查询获取明细数据
                List<Map<String, Object>> dtVal = dbOperator.executeDataTable(sql, 0, -1, tot);
                if (dtVal != null && !dtVal.isEmpty()) {
                    // 检查数据量限制，超过则中断
                    if (dtVal.size() > 50 && mainData.size() > 1000) {
                        response.setOther(true);
                        responseData.put("stopexport", true);
                        response.setData(responseData);
                        break; // 替代goto
                    }

                    // 转换为哈希表列表并添加关联键
//                    List<Map<String, Object>> hs = dtVal.toHashTable();
                    for (Map<String, Object> h : dtVal) {
                        h.put("uniondetailkey", indexVal);
                    }

                    // 合并明细数据
                    if (responseData.containsKey("detailData")) {
                        List<Map<String, Object>> existing = (List<Map<String, Object>>) responseData.get("detailData");
                        existing.addAll(dtVal);
                        responseData.put("detailData", existing);
                    } else {
                        responseData.put("detailData", new ArrayList<>(dtVal));
                    }
                }

                // 为主数据添加关联键
                mainData.get(i).put("unionmainkey", indexVal);
            }
            responseData.put("mainData", mainData);
            response.setData(responseData);
            response.setOther(true);
        }
        // 存在关联字段的情况
        else {
            Map<String, Object> leftRecord = new HashMap<>();
            detailSql = dealQuerySql(detailSql, leftRecord, leftRecord, null, null, null);

            // 处理WHERE条件
            boolean hasWhere = detailSql.contains("where") || detailSql.contains("Where");
            if (!hasWhere) {
                detailSql += " where 1=1 ";
            }

            // 拼接IN条件
            StringBuilder strIn = new StringBuilder(" and " + detailIdKey + " in ( ");
            for (Map<String, Object> data : mainData) {
                Object idValue = data.get(mainDataIdKey);
                strIn.append("'").append(idValue).append("',");
            }
            // 移除最后一个逗号
            if (!strIn.isEmpty() && strIn.charAt(strIn.length() - 1) == ',') {
                strIn.setLength(strIn.length() - 1);
            }
            strIn.append(" )");
            detailSql += strIn.toString();

            // 执行查询
            List<Map<String, Object>> dtVal = dbOperator.executeDataTable(detailSql, 0, -1, tot);

            // 处理下拉框字段
            List<ComboBox> boxs = new ArrayList<>();
            List<Map<String, Object>> colorAndboxCols = DataImpl.GetColorAndBoxColumns(dmoduleId);
            if (colorAndboxCols != null) {
                BaseModule finalModule = module;
                boxs = colorAndboxCols.stream()
                        .filter(row -> {
                            Object fieldsql = row.get("fieldsql");
                            return fieldsql != null && !fieldsql.toString().isEmpty();
                        })
                        .map(row -> createControl.createControl(row, finalModule, false))
                        .filter(control -> control instanceof ComboBox)
                        .map(control -> (ComboBox) control)
                        .filter(box -> !box.getValueField().equals(box.getDisplayField()))
                        .collect(Collectors.toList());
            }

            // 处理查询结果
            if (dtVal != null && !dtVal.isEmpty()) {
                // 设置主数据列信息
                Map<String, Object> firstRow = dtVal.get(0);
                // 转换列信息为 MainDataColumns
                module.MainDataColumns = firstRow.keySet().stream()
                        .map(columnName -> {
                            Map<String, Object> colInfo = new HashMap<>();
                            // 设置 dataIndex 为列名
                            colInfo.put("dataIndex", columnName);
                            // 获取列值类型，转换为 xtype（假设 PublicUtil 有 TypeToColumnType 方法）
                            Object value = firstRow.get(columnName);
                            Class<?> dataType = value != null ? value.getClass() : Object.class;
                            colInfo.put("xtype", PublicUtil.TypeToColumnType(dataType));
                            return colInfo;
                        })
                        .collect(Collectors.toList());

                // 转换为哈希表列表并处理下拉框值
//                List<Map<String, Object>> tbs = toHashTable(dtVal);
                response.setData(convertBoxVal(dtVal, boxs,
                        module.getModuleId() + (dmoduleId == null || dmoduleId.isEmpty() ? "" : "=>" + dmoduleId)));
            }
        }

        response.setTot(tot[0]);
        response.setSuccess(true);
        return response;
    }

    // 辅助方法：去除字符串中的大括号
    private String trimBraces(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        return str.replaceAll("[{}]", "").trim();
    }

    /**
     * 获取审核消息列表
     *
     * @param userId 用户ID
     * @return 包含消息列表的响应对象
     */
    public BaseResponse GetAuditMsgTab(String userId) {
        BaseResponse response = new BaseResponse();
        // 假设DataImpl的getAuditMsgTab返回List<Map>模拟DataTable
        List<Map<String, Object>> dtVal = DataImpl.GetAuditMsgTab(userId);

        if (dtVal != null && !dtVal.isEmpty()) {
            for (Map<String, Object> row : dtVal) {
                String dllname = (row.get("dllname") != null) ? row.get("dllname").toString() : "";
                if (!dllname.isEmpty()) {
                    boolean[] tempBool = new boolean[1]; // 用数组存储基本类型的返回值
                    String moduleName = SystemMenu.convertToModuleName(dllname, 1, tempBool);
                    row.put("dllname", moduleName);
                }
            }
        }

        response.setData(dtVal);
        response.setSuccess(true);
        return response;
    }

    /**
     * 查看单条审核消息
     *
     * @param userId 用户ID
     * @param record 消息记录参数
     * @return 包含单条消息详情的响应对象
     */
    public BaseResponse SeeOneAuditMsg(String userId, Map<String, Object> record) {
        BaseResponse response = new BaseResponse();
        // 调用数据层方法获取单条消息详情
        Object data = DataImpl.SeeOneAuditMsg(getUser().UserId, record);
        response.setData(data);
        response.setSuccess(true);
        return response;
    }

    /**
     * 查看所有审核消息
     *
     * @return 包含所有消息的响应对象
     */
    public BaseResponse SeeAllAuditMsg() {
        BaseResponse response = new BaseResponse();
        // 调用数据层方法获取所有消息
        Object data = DataImpl.SeeAllAuditMsg(getUser().UserId);
        response.setData(data);
        response.setSuccess(true);
        return response;
    }

    /**
     * 确认消息
     *
     * @param moduleId 模块ID
     * @param idValue  消息ID
     * @param remark   备注
     * @return 确认是否成功
     */
    public boolean ComfirmMsg(String moduleId, String idValue, String remark) {
        // 调用数据层方法，返回影响行数，大于0则表示成功
        int affectedRows = DataImpl.ConfirmMsg(moduleId, idValue, getUser().UserId, remark);
        return affectedRows > 0;
    }

    public BaseResponse GetMsg(int id, int type) {
        BaseResponse response = new BaseResponse();
        // 调用DataImpl的getMsg方法获取消息数据（假设返回List<Map>模拟DataTable）
        List<Map<String, Object>> msgData = DataImpl.GetMsg(id, type);
        Hashtable<Object, Object> retTab = null;

        if (msgData != null && !msgData.isEmpty()) {
            // 取第一行数据转换为Hashtable
            Map<String, Object> firstRow = msgData.get(0);
            retTab = new Hashtable<>();
            for (Map.Entry<String, Object> entry : firstRow.entrySet()) {
                retTab.put(entry.getKey(), entry.getValue());
            }

            // 处理dllname相关逻辑
            String dllname = retTab.get("dllname") != null ? retTab.get("dllname").toString() : "";
            boolean[] isurl = new boolean[1]; // 用数组存储引用类型结果
            Object xtype = SystemMenu.convertToModuleName(dllname, 0, isurl);
            retTab.put("xtype", xtype);
            retTab.put("ModuleId", retTab.get("moduleid"));
            retTab.put("idValue", retTab.get("idvalue"));
            retTab.put("title", retTab.get("modulename"));

            // 提取用户信息
            String userId = retTab.get("userid") != null ? retTab.get("userid").toString() : "";
            String username = retTab.get("username") != null ? retTab.get("username").toString() : "";
            String loginaccount = retTab.get("loginaccount") != null ? retTab.get("loginaccount").toString() : "";

            // 构建登录用户信息
            LoginUserInfo luser = new LoginUserInfo();
            luser.UserId = (userId);
            luser.UserCode = (loginaccount);
            luser.UserName = (username);
            response.setOther(luser);

            // 检查当前用户与消息所属用户是否一致，不一致则处理
            LoginUserInfo currentUser = getUser();
            if (currentUser != null && !currentUser.UserId.equals(luser.UserId)) {
                SysUserImpl sysUserImpl = new SysUserImpl();
                // 调用登录成功处理方法（注意：Java中无ref参数，通过返回值处理）
                sysUserImpl.OnLoginSuccess(
                        null,
                        luser,
                        "0",
                        0,
                        luser.UserId,
                        dbOperator.getConnectionString(),
                        response,
                        false
                );
                // 注释：如需设置会话，可添加如下代码
                // setSessionVal(getUserSessionName(), luser);
            }

            response.setSuccess(true);
        } else {
            response.setMsg("消息已失效！");
        }

        response.setData(retTab);
        return response;
    }

    public List<Map<String, Object>> GetRoles() {
        return DataImpl.GetRoles();
    }

    public BaseResponse DelRoles(String ids) {
        return DataImpl.DelRoles(ids);
    }

    public List<Map<String, Object>> GetRoleUsers(String roleId) {
        return DataImpl.GetRoleUsers(roleId);
    }

    public List<Map<String, Object>> GetUsers(String name, String code) {
        return DataImpl.GetUsers(name, code, 0);
    }

    public BaseResponse GetUserPrev(String userId) {
        return DataImpl.GetUserPrev(userId, 0);
    }

    /**
     * 保存角色权限
     */
    public BaseResponse SaveRolePrev(String data) {
        BaseResponse response = new BaseResponse();
        // 确保数据以数组格式存在
        if (!data.startsWith("[")) {
            data = "[" + data + "]";
        }

        try {
            // 解析JSON为Map列表
            List<Map<String, Object>> datas = (List<Map<String, Object>>) JSON.Decode(data);

            if (datas == null || datas.isEmpty()) {
                return response;
            }

            for (Map<String, Object> tab : datas) {
                String roleId = String.valueOf(tab.getOrDefault("id", ""));
                String readPrev = String.valueOf(tab.getOrDefault("readpurview", ""));
                String editPrev = String.valueOf(tab.getOrDefault("editpurview", ""));

                if (roleId.isEmpty()) {
                    continue;
                }

                // 构建更新SQL
                String sql = String.format(
                        "update p_systemRoleSetTab set ReadPurview='%s', EditPurview='%s' where id = '%s'",
                        readPrev, editPrev, roleId
                );

                // 执行更新
                int rowsAffected = jdbcTemplate.queryForObject(sql, Integer.class);
                response.setSuccess(rowsAffected > 0);

                if (response.isSuccess()) {
                    response.setMsg(DataImpl.UpdRolePurview(roleId).getMsg());
                }
            }
        } catch (Exception e) {
            response.setSuccess(false);
            response.setMsg("保存角色权限失败：" + e.getMessage());
        }

        return response;
    }

    /**
     * 保存用户权限
     */
    public BaseResponse SaveUserPrev(String data) {
        BaseResponse response = new BaseResponse();
        // 确保数据以数组格式存在
        if (!data.startsWith("[")) {
            data = "[" + data + "]";
        }

        try {
            // 解析JSON为Map列表
            List<Map<String, Object>> datas = (List<Map<String, Object>>) JSON.Decode(data);

            if (datas == null || datas.isEmpty()) {
                return response;
            }

            // 获取当前登录用户信息（假设从上下文获取）
            LoginUserInfo user = getUser();
            if (user == null) {
                response.setSuccess(false);
                response.setMsg("用户未登录");
                return response;
            }

            for (Map<String, Object> tab : datas) {
                String userId = String.valueOf(tab.getOrDefault("员工id", ""));
                String readPrev = String.valueOf(tab.getOrDefault("readpurview", "")).replaceAll(",$", "").replaceAll("^,", "");
                String editPrev = String.valueOf(tab.getOrDefault("editpurview", "")).replaceAll(",$", "").replaceAll("^,", "");

                if (userId.isEmpty()) {
                    continue;
                }

                // 查询用户信息
                List<Map<String, Object>> userTab = DataImpl.GetUsers("", "", Integer.parseInt(userId));
                if (userTab.isEmpty()) {
                    continue;
                }

                // 获取所属部门
                String department = String.valueOf(userTab.get(0).getOrDefault("所属部门", ""));
                LocalDateTime now = LocalDateTime.now();

                // 构建SQL（存在则更新，不存在则插入）
                String sql = String.format(
                        "if exists (select * from p_systemRoleOperSetTab s where s.roleOperatorId='%s') " +
                                "update p_systemRoleOperSetTab set ReadPurview='%s', EditPurview='%s' where roleOperatorId = '%s' " +
                                "else " +
                                "insert into p_systemRoleOperSetTab " +
                                "(roleId, roleOperatorId, operatorid, operatorname, operatedate, department, ReadPurview, EditPurview) " +
                                "values ('0', '%s', '%s', '%s', '%s', '%s', '%s', '%s')",
                        userId, readPrev, editPrev, userId,
                        userId, user.UserId, user.UserName, now, department, readPrev, editPrev
                );

                // 执行SQL
                int rowsAffected = jdbcTemplate.queryForObject(sql, Integer.class);
                response.setSuccess(rowsAffected > 0);

                if (response.isSuccess()) {
                    response.setMsg(DataImpl.UpdUserPurview(userId).getMsg());
                }
            }
        } catch (Exception e) {
            response.setSuccess(false);
            response.setMsg("保存用户权限失败：" + e.getMessage());
        }

        return response;
    }

    public BaseResponse DelRoleUser(String ids) {
        return DataImpl.DelRoleUser(ids);
    }

//    public List<Map<String, Object>> GetCommentList(String moduleId, String idVal, String stepCode) {
//        return DataImpl.GetCommentList(moduleId, idVal, stepCode);
//    }

    /**
     * 获取评论列表
     *
     * @param moduleId 模块ID
     * @param idVal    ID值
     * @param stepCode 步骤编码
     * @return 评论列表（HashMap列表）
     */
    public List<Map<String, Object>> GetCommentList(String moduleId, String idVal, String stepCode) {
        // 获取基础模块
        BaseModule module = GetBaseModule("Comment_001", "");

        if (module != null) {
            // 第一种获取方式：通过模块SQL查询
            String querySql = module.getMasterSql();
            HashMap<String, Object> paramMap = new HashMap<>();
            paramMap.put("idvalue", idVal);
            paramMap.put("dllcoid", moduleId);
            paramMap.put("stepcode", stepCode);

            // 处理SQL参数替换
            querySql = dealQuerySql(querySql, paramMap, paramMap, null, null, null);

            // 执行查询并转换为HashMap列表
            return jdbcTemplate.queryForList(querySql);
        } else {
            // 第二种获取方式：通过数据接口获取
            List<Map<String, Object>> dataTable = DataImpl.GetCommentList(moduleId, idVal, stepCode);

            List<Map<String, Object>> tbs = toHashTable(
                    dataTable,
                    null,
                    null,
                    null,
                    true,
                    true,
                    false,
                    (row, name, value) -> {
                        try {
                            return fillFilePath(new String[]{"atts"}, name, value);
                        } catch (UnsupportedEncodingException e) {
                            throw new RuntimeException(e);
                        }
                    }
            );

            return tbs;
        }
    }

    /**
     * 填充/替换文件路径（完整还原原C#逻辑）
     *
     * @param picFieldNames 需要处理的字段名数组（如{"atts"}）
     * @param name          当前处理的字段名
     * @param v             字段原始值
     * @return 处理后的文件路径值
     */
    protected Object fillFilePath(String[] picFieldNames, String name, Object v) throws UnsupportedEncodingException {
        // 1. 将值转为字符串（对应C#的$"{v}"）
        String val = String.valueOf(v);

        // 2. 核心判断条件（完整还原原逻辑）
        // 条件：值非空 + 不含http + 字段名在目标数组中 + (配置使用DB附件 且 非默认服务器) 或 AppDomain非空
        boolean condition1 = !isNullOrEmpty(val) && val.indexOf("http") < 0;
        boolean condition2 = ArrayUtils.contains(picFieldNames, name); // 数组包含判断
        boolean condition3 = (toBoolean(WebConfigUtil.get("useDbAttc")) && !DataImpl.isDefaultServer())
                || !isNullOrEmpty(getAppDomain());

        if (condition1 && condition2 && condition3) {
            // 3. 分割值为数组（对应C#的Split(',')）
            String[] vals = val.split(",");
            String[] newVals = new String[vals.length];

            // 4. 构建DB版本路径（对应C#的$"{WebConfigUtil.FileVPath}_{user.ServerId}"）
            String dbVerPath = WebConfigUtil.getFileVPath() + "_" + getUser().getServerId();

            // 5. 遍历替换每个路径
            for (int i = 0; i < vals.length; i++) {
                if (SiteUtil.containsVirtualDirectory(dbVerPath)) {
                    // 替换原有文件虚拟路径
                    newVals[i] = vals[i].replace(WebConfigUtil.getFileVPath(), dbVerPath);
                } else {
                    // 拼接AppDomain路径（去除末尾的/）
                    String trimmedAppDomain = stripEnd(getAppDomain(), "/");
                    newVals[i] = trimmedAppDomain + vals[i];
                }
            }

            // 6. 拼接回字符串返回
            return join(newVals, ",");
        }

        // 不满足条件则返回原始值
        return v;
    }

    /**
     * 保存角色用户关系
     */
    public BaseResponse SaveRoleUser(String data) {
        BaseResponse response = new BaseResponse();
        // 检查数据是否为空或空数组
        if (data == null || data.trim().isEmpty() || "[]".equals(data.trim())) {
            return response;
        }

        // 确保数据以数组格式存在
        if (!data.startsWith("[")) {
            data = "[" + data + "]";
        }

        try {
            // 解析JSON为Map列表
            List<Map<String, Object>> datas = (List<Map<String, Object>>) JSON.Decode(data);

            // 调用AddOrUpdTable方法处理数据（假设在BaseImpl中实现了该方法）
            response = AddOrUpdTable(data, "p_systemRoleOperSetTab", "id", true, null);

            // 如果操作成功，更新角色权限
            if (response.isSuccess()) {
                if (datas != null) {
                    for (Map<String, Object> fRow : datas) {
                        String roleId = String.valueOf(fRow.getOrDefault("roleid", ""));
                        if (!roleId.isEmpty()) {
                            DataImpl.UpdRolePurview(roleId);
                        }
                    }
                }
            }
        } catch (Exception e) {
            response.setSuccess(false);
            response.setMsg("保存角色用户关系失败：" + e.getMessage());
        }

        return response;
    }

    /**
     * 判断是单据还是基础档案
     *
     * @param moduleId 模块ID
     * @return 基础响应对象，data包含isbase字段（1：基础档案；2：单据；-1：均不是）
     */
    public BaseResponse CheckModuleOrBill(String moduleId) {
        BaseResponse response = new BaseResponse();
        Map<String, Object> resultMap = new HashMap<>();

        // 检查基础档案
        ModuleBaseEntity oneMod = GetBaseModule(moduleId, "");
        resultMap.put("isbase", 1);

        // 若基础档案不存在，检查单据
        if (oneMod == null) {
            oneMod = GetBillModule(moduleId, "");
            resultMap.put("isbase", 2);
        }

        // 若均不存在，设置为-1
        if (oneMod == null) {
            resultMap.put("isbase", -1);
        }

        response.setData(resultMap);
        response.setSuccess(true);
        return response;
    }

    /**
     * DLL名称转换
     *
     * @param dllName DLL名称
     * @return 基础响应对象，data包含转换后的模块名称
     */
    public BaseResponse ConvertDllName(String dllName) {
        BaseResponse response = new BaseResponse();
        Map<String, Object> resultMap = new HashMap<>();

        // 调用SystemMenuEntity的转换方法（模拟out参数使用数组）
        boolean[] tempBool = new boolean[1]; // 用数组存储布尔值，模拟引用传递
        String moduleName = SystemMenu.convertToModuleName(dllName, 1, tempBool);

        resultMap.put("moduleName", moduleName);
        response.setData(resultMap);
        response.setSuccess(true);
        return response;
    }

    /**
     * 获取桌面模块左侧数据
     *
     * @return 包含左侧模块数据的基础响应对象
     */
    public BaseResponse GetDesktopModuleLeft() {
        BaseResponse response = new BaseResponse();
        // 获取左侧模块数据（DataImpl返回List<Map>模拟DataTable）
        List<Map<String, Object>> itemsList = DataImpl.GetDesktopModuleLeft();
        // 转换为哈希表结构（保持与C# ToHashTable一致的格式）
        response.setData(itemsList);
        response.setTot(itemsList.size());
        response.setSuccess(true);
        return response;
    }

    /**
     * 获取桌面模块主数据
     *
     * @param dllcoid 模块标识
     * @return 包含主模块数据的基础响应对象
     */
    public BaseResponse GetDesktopModuleMain(String dllcoid) {
        BaseResponse response = new BaseResponse();

        // 检查并填充个人配置（若不存在则生成默认配置）
        DataImpl.CheckAndFillDesktopModuleMain(dllcoid);

        // 获取当前用户信息
        String userId = getUser().UserId;
        String userName = getUser().UserName;

        // 获取主模块数据
        List<Map<String, Object>> itemsList = DataImpl.GetDesktopModuleMain(userId, userName, dllcoid);
        // 转换为哈希表结构
        response.setData(itemsList);
        response.setTot(itemsList.size());
        response.setSuccess(true);
        return response;
    }

    /**
     * 对常用工具添加或删除
     *
     * @param cardId 卡片ID
     * @param adds   要添加的菜单ID字符串（逗号分隔）
     * @param dels   要删除的菜单ID字符串（逗号分隔）
     * @return 操作结果响应
     */
    public BaseResponse UpdCommonUse(int cardId, String adds, String dels) {
        BaseResponse response = new BaseResponse();

        // 若添加和删除列表都为空，直接返回空响应
        if ((adds == null || adds.isEmpty()) && (dels == null || dels.isEmpty())) {
            return response;
        }

        // SQL模板定义
        final String addSqlTemplate = "insert into P_MessageToolLinkDllTab " +
                "(cardId, EmployeeID, LMenuid, Lsubsysid, ObjDll, DllShowCaption, GroupTagid, ItemTagid, LinkModeTag) " +
                "select '%d', '%s', '%s', m.subsysid, m.dllfilename, m.menucaption, '2', '999', '1' " +
                "from p_formmenuconfigtab m where menuid = '%s' " +
                "and isnull(m.urlparams, '') <> '' " +
                "and (isnull(m.dllfilename1, '') <> '' or isnull(m.dllfilename, '') <> '') " +
                "and (isnull(m.targetmode, 0) = 0 or m.targetmode = 3 or m.targetmode=2) " +
                "and isnull(m.useFlag, 1) = 1;";

        final String delSqlTemplate = "delete from P_MessageToolLinkDllTab " +
                "where LMenuid = '%s' and employeeid = '%s' and cardId = '%d';";

        // 构建SQL语句
        StringJoiner sqlJoiner = new StringJoiner(" ");

        // 处理添加操作
        if (adds != null && !adds.isEmpty()) {
            String[] addMenuIds = adds.split(",");
            for (String menuId : addMenuIds) {
                if (menuId == null || menuId.trim().isEmpty()) {
                    continue;
                }
                // 拼接添加SQL（假设user对象存在userId属性）
                String addSql = String.format(
                        addSqlTemplate,
                        cardId,
                        getUser().UserId,
                        menuId,
                        menuId
                );
                sqlJoiner.add(addSql);
            }
        }

        // 处理删除操作
        if (dels != null && !dels.isEmpty()) {
            String[] delMenuIds = dels.split(",");
            for (String menuId : delMenuIds) {
                if (menuId == null || menuId.trim().isEmpty()) {
                    continue;
                }
                // 拼接删除SQL（假设user对象存在userId属性）
                String delSql = String.format(
                        delSqlTemplate,
                        menuId,
                        getUser().UserId,
                        cardId
                );
                sqlJoiner.add(delSql);
            }
        }

        // 若没有生成有效的SQL，直接返回
        if (sqlJoiner.length() == 0) {
            return response;
        }

        // 执行SQL并设置响应结果
        int affectedRows = jdbcTemplate.update(sqlJoiner.toString());
        response.setSuccess(true);
        response.setData(affectedRows);
        // 假设存在获取桌面常用工具列表的方法
        response.setOther(GetDeskTopCommonUse(cardId));

        return response;
    }

    /**
     * 按文件ID删除附件（对应C# DelAttcFile(string fileId)）
     *
     * @param fileId 文件ID
     * @return 统一响应结果（BaseResponse）
     */
    public BaseResponse DelAttcFile(String fileId) {
        BaseResponse response = new BaseResponse();
        String filePath = ""; // 初始为空，对应C#注释的dataImpl.GetAbsFilePath(fileId)

        // 1. 查询文件信息（获取webpath）
        // C#：dataImpl.GetAttcFileInfo("", "", "", fileId.ToInt32())
        List<Map<String, Object>> fileTb = DataImpl.GetAttcFileInfo("", "", "", ToInt32(fileId));

        // 2. 提取文件路径（webpath）
        if (fileTb != null && !fileTb.isEmpty()) {
            Map<String, Object> firstRow = fileTb.get(0);
            // 转换row["webpath"]为字符串（空值返回""，对齐C# row["webpath"] + ""）
            filePath = Objects.toString(firstRow.get("webpath"), "");
        }
        log.debug(String.valueOf("filePath : " + filePath));
        // 3. 路径为空校验（返回错误信息）
        if (filePath == null || filePath.isEmpty()) {
            response.setMsg(String.format(
                    "未找到文件的路径信息,id:【%s】，请检查函数fun_fm_getAbsolutePath，确保返回正确的文件路径！",
                    fileId
            ));
            return response;
        }

        // 4. 提取文件名（从webpath中截取，对应C# LastIndexOf("/")逻辑）
        int lastIndex = filePath.lastIndexOf("/");
        String fileName = lastIndex > 0 ? filePath.substring(lastIndex + 1) : filePath;

        // 5. 删除文件（调用FileUtil，传入文件路径和附件基础路径）
        BaseResponse fileDelResp = FileUtil.deleteFile(filePath, getAttcPath());
        response.setOther(fileDelResp.getOther()); // 同步文件删除的other信息

        // 6. 数据库删除文件记录（对应C# dataImpl.DelAttcFileInfo）
        DataImpl.DelAttcFileInfo("", "", "", "", fileId);

        // 7. 记录系统日志（对应C# SysLog）
        String logContent = String.format(
                "%s删除附件【%s】,附件id:%s",
                getUser().UserName, fileName, fileId
        );
        sysLog(logContent, "删除附件");

        // 8. 设置成功响应
        response.setSuccess(true);
        return response;
    }

// =========================== 第二个方法：按模块/ID/文件名删除附件 ===========================

    /**
     * 按模块ID、业务ID、文件名等删除附件（对应C# DelAttcFile(string moduleId, ...)）
     *
     * @param moduleId 模块ID
     * @param idValue  业务ID（如表单ID）
     * @param specno   规格编号
     * @param folder   文件夹路径
     * @param fileName 文件名
     * @return 统一响应结果（BaseResponse）
     */
    public BaseResponse DelAttcFile(String moduleId, String idValue, String specno, String folder, String fileName) throws
            SQLException {
        BaseResponse response = new BaseResponse();
        String dirTabId = ""; // 对应C#的dirTabId（文件夹关联表ID）
        String mFolder; // 从数据库获取的文件夹路径

        // 1. 查询附件文件夹路径（out dirTabId：Java用数组接收"输出参数"）
        // C#：dataImpl.GetAcFileFolder(moduleId, idValue, specno, out dirTabId)
        String[] dirTabIdHolder = new String[1]; // Java无out，用数组存输出值
        mFolder = DataImpl.GetAcFileFolder(moduleId, idValue, specno, dirTabIdHolder);
        dirTabId = dirTabIdHolder[0]; // 提取输出的dirTabId

        // 2. 处理文件夹路径（判断是否以"/"开头，对应C# folder.StartsWith("/")）
        String fileFolder = (folder != null && folder.startsWith("/")) ? folder : mFolder;

        // 3. 获取附件操作权限信息（对应C# dataImpl.GetAttcOperInfo）
        response = DataImpl.GetAttcOperInfo(
                3,                  // 操作类型：3（删除）
                fileName,           // 文件名
                dirTabId,           // 文件夹关联表ID
                0,                  // 预留参数：0
                getUser().UserId,   // 用户ID
                getUser().UserName, // 用户名
                "",                 // 预留参数：空字符串
                0,                  // 预留参数：0
                moduleId,           // 模块ID
                idValue,            // 业务ID
                0                   // 预留参数：0
        );

        // 4. 权限校验通过（success=true），执行删除逻辑
        if (response.isSuccess()) {
            // 4.1 拼接文件完整路径（对应C# Path.Combine）
            String fullFilePath = Paths.get(fileFolder, fileName).toString();
            // 4.2 删除物理文件（调用FileUtil）
            FileUtil.deleteFile(fullFilePath, getAttcPath());

            // 4.3 数据库删除文件记录（对应C# dataImpl.DelAttcFileInfo）
            int deleteResult = DataImpl.DelAttcFileInfo(dirTabId, moduleId, specno, fileName);

            // 4.4 记录系统日志
            String logContent = String.format(
                    "%s删除模块%s下%s的附件%s",
                    getUser().UserName, moduleId, idValue, fileName
            );
            sysLog(logContent, "删除附件");

            // 4.5 设置响应信息（判断是否删除成功）
            String respMsg = deleteResult > 0 ? "操作成功" : "操作失败，未找到文件数据！";
            response.setMsg(respMsg);
            response.setSuccess(true); // 确保success为true（权限校验已通过）
        }

        return response;
    }

    /**
     * 获取模块统计数据
     *
     * @param moduleIds 模块 ID 列表，用逗号分隔
     * @param _pams     参数
     * @param _where    查询条件
     * @return 基础响应对象
     * 2026.2.24
     */
//    @Override
    public BaseResponse GetModuleCountData2(String moduleIds, String _pams, String _where) {
        BaseResponse response = new BaseResponse();
        StringBuilder stringBuilder = new StringBuilder();
        List<String> mIds = new ArrayList<>();
        // 分割模块 ID
        String[] moduleIdArray = moduleIds.split(",");
        for (String moduleId : moduleIdArray) {
            ModuleBaseEntity module = GetModule(moduleId);
//            out.println(moduleId + " " + module + " " + module.getNeedCount());
            if (module == null) continue;
            if (!module.getNeedCount()) continue;
            if (module instanceof BaseModule) {
                // 处理基础模块
                String querySql = module.getCountSql();
                querySql = dealQuerySql(querySql, _pams, null, null, null, null, false, false, false);
//                out.println("querySql " + querySql + " " + _pams + " " + _where);
                if (_where != null && !_where.isEmpty()) {
                    Map<String, String> whereDict = new HashMap<>();
                    whereDict.put("$where", _where);
                    querySql = new SqlAnalyzer(querySql).InsertWhere(whereDict, true);
                }
                // 移除末尾分号并添加分号
                stringBuilder.append(querySql.replaceAll(";$", "")).append(";");
            } else {
                // 处理其他类型模块
                List<Map<String, Object>> dtVal = DataImpl.GetBillSource(moduleId, "", "2");
                if (!dtVal.isEmpty()) {
                    BillSourceModule sourceModule = new BillSourceModule(dtVal.get(0));
                    sourceModule.setModuleId(moduleId);
                    String sourceSql = module.getCountSql();
                    // 替换 #...# 格式的内容
                    sourceSql = sourceSql.replaceAll("(?s)#.*?#", "1=1");
                    sourceSql = PublicUtil.ReqSqlPms(null, null, sourceSql, SystemTypeEnums.PmType.sql, getUser());
                    if (_where != null && !_where.isEmpty()) {
                        Map<String, String> whereDict = new HashMap<>();
                        whereDict.put("$where", _where);
                        sourceSql = new SqlAnalyzer(sourceSql).InsertWhere(whereDict, false, false, true);
                    }
                    // 移除末尾分号并添加分号
                    stringBuilder.append(sourceSql.replaceAll(";$", "")).append(";");
                }
            }
            mIds.add(moduleId);
        }
        if (!stringBuilder.isEmpty()) {
            // 执行 SQL 并获取数据集
//            out.println(stringBuilder.toString());

            List<List<Map<String, Object>>> dataSet = dbOperator.executeDataSet(stringBuilder.toString());
            //            out.println("dataSet " + dataSet);
            Map<String, List<Map<String, Object>>> datas = new HashMap<>();
            for (int i = 0; i < dataSet.size(); i++) {
                if (i < mIds.size()) {
                    datas.put(mIds.get(i), dataSet.get(i));
                }
            }
            response.setData(datas);
            response.setSuccess(true);

        }
        return response;
    }

    public BaseResponse GetModuleCountData(String moduleIds, String _pams, String _where) {
        BaseResponse response = new BaseResponse();
        StringBuilder stringBuilder = new StringBuilder();
        ArrayList mIds = new ArrayList();
        for (String moduleId : moduleIds.split(",")) {
            String querysql = "";
            if (moduleId.startsWith("rightmenu_")) {
                List<Map<String, Object>> countDt = DataImpl.GetRightMenuRows("", 0, "", ToInt32(moduleId.split("_")[moduleId.split("_").length - 1]));
                if (countDt != null && countDt.size() > 0) {
                    querysql = (String) countDt.get(0).getOrDefault("countSql", null);
                }
            } else {
                List<Map<String, Object>> countDt = DataImpl.GetCountModuleInfo(moduleId);
                ModuleBaseEntity module = null;
                if (countDt != null && countDt.size() > 0) {
                    module = new ModuleBaseEntity(countDt.get(0));
                }
                if (module == null) continue;
                if (!module.getNeedCount()) continue;
                querysql = module.getCountSql();
            }
            if (isNullOrEmpty(querysql)) continue;
            querysql = DealQuerySql(querysql, _pams, null, null, null, null);
            if (!isNullOrEmpty(_where)) {
                Map<String, String> whereDict = new HashMap<>();
                whereDict.put("$where", _where);
                querysql = new SqlAnalyzer(querysql).InsertWhere(whereDict, true);
            }
            stringBuilder.append(TrimEnd(querysql, ';')).append(';');
            mIds.add(moduleId);
        }
        if (stringBuilder.length() > 0) {
            List<List<Map<String, Object>>> set = dbOperator.executeDataSet(stringBuilder.toString());
            Hashtable datas = new Hashtable();
            for (int i = 0; i < set.size(); i++) {
                datas.put(mIds.get(i), set.get(i));
            }
            response.setData(datas);
            response.setSuccess(true);
        }
        return response;
    }

    public String GetAttcPathByOAUrl(String url) {
        List<Map<String, Object>> dt = getDataImpl().GetSysdbGroup(0);
        for (int i = 0; i < dt.size(); i++) {
            int serverId = ((Number) dt.get(i).get("id")).intValue();
            StringBuilder errMsg = new StringBuilder();
            DbOperator _dbOperator = new SysUserImpl().getServerDbOper(serverId, errMsg);
            if (_dbOperator != null) {
                DataImpl _dImpl = new DataImpl();
                _dImpl.setDbOperator(_dbOperator);  // 对应C#的对象初始化器

                String oaurl = "";
                try {
                    oaurl = _dImpl.GetSystemOAUrl();
                } catch (Exception e) {
                    continue;
                }

                if (oaurl.startsWith(url)) {
                    return _dImpl.GetSystemAttcPath();
                }
            }
        }

        return null;
    }

    /**
     * 完全等价转换C#的 GetModuleCfgs 方法
     *
     * @param moduleId 模块ID字符串(英文逗号分隔)
     * @param menuid   菜单ID
     * @return 组装后的BaseModule对象
     */
    public BaseModule GetModuleCfgs(String moduleId, String menuid) {
        BaseModule module = new BaseModule();
        // 1. 获取基础模块配置（Java 中 DataTable 一般用 List<Map<String, Object>> 替代）
        List<Map<String, Object>> dts = DataImpl.GetBaseModuleCfgs(moduleId, menuid);

        List<Map<String, Object>> tabs = new ArrayList<>();
        List<Map<String, Object>> orderTabs = new ArrayList<>();

        // 2. 遍历数据行，处理权限和参数
        for (Map<String, Object> row : dts) {
            // 权限校验（对应 C#: !string.IsNullOrEmpty(util.CheckPurview(...))）
            String menuIdStr = row.get("MenuId") == null ? "" : row.get("MenuId").toString();
            String purviewCheckResult = createControl.CheckPurview(getUser().PurviewStr, menuIdStr);
            if (purviewCheckResult != null && !purviewCheckResult.isEmpty()) {
                // 行数据转 Hashtable
                Map<String, Object> tab = (row);
                // 处理 xtype（对应 SystemMenu.ConvertToModuleName）
                boolean[] isUrlArr = new boolean[1]; // Java 无 out 参数，用数组模拟
                String dllFileName = tab.get("dllfilename") == null ? "" : tab.get("dllfilename").toString();
                String xtype = SystemMenu.convertToModuleName(dllFileName, 1, isUrlArr);
                boolean isUrl = isUrlArr[0];

                tab.put("xtype", xtype);
                tab.remove("urlparams"); // Java Hashtable 区分大小写，与 C# 保持一致

                // 处理 pubbrower 和 .html 后缀的 URL 参数
                String urlParams = row.get("UrlParams") == null ? "" : row.get("UrlParams").toString();
                if (xtype.toLowerCase().contains("pubbrower") && urlParams.contains(".html")) {
                    Map<String, String> queryPms = new HashMap<>();

                    // 解析 URL 参数（split 处理，与 C# 逻辑一致）
                    String[] urlParts = urlParams.split("\\?");
                    String paramPart = urlParts.length > 1 ? urlParts[1] : "";
                    String[] kvStrs = paramPart.split("&");

                    for (String kvStr : kvStrs) {
                        String[] kvs = kvStr.split("=");
                        // 过滤 username/password，且确保键值对完整
                        if (kvs.length == 2
                                && !kvs[0].equalsIgnoreCase("username")
                                && !kvs[0].equalsIgnoreCase("password")) {
                            queryPms.put(kvs[0].toLowerCase(), kvs[1]);
                        }
                    }

                    // 处理 moduleid/dllcoid 和 xtype
                    if (!queryPms.isEmpty() && queryPms.containsKey("xtype")) {
                        String _moduleId = "";
                        if (queryPms.containsKey("moduleid")) {
                            _moduleId = queryPms.get("moduleid");
                        } else if (queryPms.containsKey("dllcoid")) {
                            _moduleId = queryPms.get("dllcoid");
                        }

                        if (_moduleId != null && !_moduleId.isEmpty()) {
                            tab.put("moduleid", _moduleId);
                            tab.put("xtype", queryPms.get("xtype"));
                        }
                    }
                }

                tab.remove("dllfilename");
                tabs.add(tab);
            }
        }

        // 3. 按 moduleId 拆分筛选，生成有序的 orderTabs
        if (moduleId != null && !moduleId.isEmpty()) {
            String[] midArray = moduleId.split(",");
            for (String mid : midArray) {
                // 替代 C# 的 FirstOrDefault，查找第一个匹配的 tab
                Map<String, Object> matchedTab = null;
                for (Map<String, Object> tb : tabs) {
                    String tbModuleId = tb.get("moduleid") == null ? "" : tb.get("moduleid").toString();
                    if (tbModuleId.equalsIgnoreCase(mid)) {
                        matchedTab = tb;
                        break; // 找到第一个匹配项立即退出
                    }
                }
                if (matchedTab != null) {
                    orderTabs.add(matchedTab);
                }
            }
        }

        // 4. 封装返回
        module.ModuleTabs = (orderTabs);
        return module;
    }

    /**
     * 检查扫码是否重复（完整还原原C#逻辑）
     *
     * @param moduleId  明细模块编号
     * @param scanVal   扫码值
     * @param idValue   主表主键
     * @param repeat    当前扫码是否重复
     * @param dataIndex 数据索引
     * @return 检查结果响应对象
     */
    public BaseResponse CheckScanRepeat(String moduleId, String scanVal, String idValue, boolean repeat,
                                        int dataIndex) throws SQLException {
        // 初始化响应对象，默认成功
        BaseResponse response = new BaseResponse();
        response.setSuccess(true);

        String proName = "p_sysappscancheck";

        // 检查存储过程是否存在
        boolean hasPro = DataImpl.IsExitPro(proName);
        if (!hasPro) {
            response.setData(1);
            return response;
        }

        // 获取存储过程参数（对应原dbOperator.GetStoreParams）
        DbOperator.Parameter[] pmList = dbOperator.getStoreParams(proName);

        // 组装参数值数组：明细模块编号，扫码值，主表主键，用户id，当前扫码是否重复，数据索引
        Object[] vals = new Object[]{
                moduleId,
                scanVal,
                idValue,
                getUser().getUserId(), // 对应user.UserId
                repeat,
                dataIndex
        };

        // 执行存储过程并获取响应
        response = DataImpl.excuteStore(proName, vals);
        // 将other字段值赋值给data字段
        response.setData(response.getOther());

        return response;
    }

    /**
     * 获取扫码数据（完整还原原C#逻辑）
     *
     * @param moduleId 明细模块编号
     * @param scanVal  扫码值
     * @param idValue  主表主键
     * @return 封装后的BaseResponse响应对象
     */
    public BaseResponse GetScanDatas(String moduleId, String scanVal, String idValue) throws SQLException {
        // 初始化响应对象，默认成功
        BaseResponse response = new BaseResponse();
        response.setSuccess(true);

        String proName = "p_sysappscandata";

        // 检查存储过程是否存在
        boolean hasPro = DataImpl.IsExitPro(proName);
        if (!hasPro) {
            return response;
        }

        // 组装参数值数组：明细模块编号，扫码值，主表主键，用户id
        Object[] vals = new Object[]{
                moduleId,
                scanVal,
                idValue,
                getUser().getUserId() // 对应C#的user.UserId
        };

        // 执行存储过程
        response = DataImpl.excuteStore(proName, vals);

        // 将DataSet的第一个DataTable转换为HashMap列表（对应ToHashTable）
        if (response.getData() != null) {
            // Java中DataSet对应List<List<Map<String, Object>>>，DataTable对应List<Map<String, Object>>
            List<List<Map<String, Object>>> dataSet = (List<List<Map<String, Object>>>) response.getData();
            if (dataSet != null && !dataSet.isEmpty()) {
                // 取第一个DataTable转换（对应ds.Tables[0]）
                List<Map<String, Object>> dataTable = dataSet.get(0);
                // 转换为HashMap列表（对应ToHashTable方法）
                response.setData(toHashTable(dataTable));
            }
        }

        return response;
    }

    /**
     * 获取APP打印数据（完整还原原C#逻辑）
     *
     * @param _recs     记录JSON字符串
     * @param moduleId  模块ID
     * @param printType 打印类型（1=命令模式，其他=数据模式）
     * @return 封装后的BaseResponse响应对象
     */
    public BaseResponse GetAppPrintData(String _recs, String moduleId, int printType) {
        // 初始化响应对象，默认成功
        BaseResponse response = new BaseResponse();
        response.setSuccess(true);

        // 1. 处理JSON字符串：确保以[]开头，否则补全
        if (_recs != null && !_recs.trim().startsWith("[")) {
            _recs = String.format("[%s]", _recs);
        }

        // 2. 解析JSON为List（对应C#的JSON.Decode + ArrayList）
        ArrayList<Map<String, Object>> recs = (ArrayList<Map<String, Object>>) JSON.Decode(_recs);

        // 3. 初始化打印数据列表（对应C#的List<Hashtable>）
        List<Map<String, Object>> htList = new ArrayList<>();

        // 4. 获取打印配置表（新版/老版分支）
        List<Map<String, Object>> printTable = DataImpl.getAppPrintRows(moduleId);
        if (printTable != null && !printTable.isEmpty()) {
            // 新版打印逻辑
            for (Map<String, Object> item : printTable) {
                // 获取formKey字段值
                String formKey = Objects.toString(item.get("formKey"), "");

                // 获取单份打印配置
                Map<String, Object> htTable = GetSinglePrint(formKey);

                // 设置打印属性
                htTable.put("pageTitle", Objects.toString(item.get("modname"), ""));
                // printCount：取最大值（至少1）
                int count = toInt32(get(item, "Count", 1).toString());
                htTable.put("printCount", Math.max(1, count));
                // 获取打印项
                htTable.put("items", DataImpl.getAppSinglePrintItems(formKey));

                htList.add(htTable);
            }
        } else {
            // 老版本打印逻辑
            Map<String, Object> htTable = GetOldSinglePrint(moduleId);
            htTable.put("items", DataImpl.getAppOldSinglePrintItems(moduleId));
            htList.add(htTable);
        }

        // 5. 根据打印类型组装响应数据
        if (printType == 1) {
            // 打印类型1：组装命令列表
            List<Object> cmds = new ArrayList<>();
            int printVersion = (printTable != null && !printTable.isEmpty()) ? 2 : 1;
            for (Map<String, Object> rec : recs) {
                cmds.add(GetModulePrintCommand(rec, htList, moduleId, printVersion));
            }
            response.setData(cmds);
        } else {
            // 打印类型非1：组装打印数据列表
            List<Object> retList = new ArrayList<>();
            for (Map<String, Object> printInfo : htList) {
                // 将printInfo["items"]（DataTable）转换为HashMap列表
                List<Map<String, Object>> layoutTab = toHashTable(
                        (List<Map<String, Object>>) printInfo.get("items"),
                        true,
                        false, false
                );

                List<Object> printItems = new ArrayList<>();
                for (Map<String, Object> rec : recs) {
                    List<Map<String, Object>> recInfoList = new ArrayList<>();
                    for (Map<String, Object> item : layoutTab) {
                        // 复制原有字段
                        Map<String, Object> tab = new HashMap<>(item);
                        // 填充打印文本：rec[fieldname]
                        String fieldName = Objects.toString(tab.get("fieldname"), "");
                        tab.put("printtext", Objects.toString(rec.get(fieldName), ""));
                        recInfoList.add(tab);
                    }

                    // 组装单条打印数据
                    Map<String, Object> printItem = new HashMap<>();
                    printItem.put("pageTitle", printInfo.get("pageTitle"));
                    printItem.put("printCount", printInfo.get("printCount"));
                    printItem.put("printItems", recInfoList);
                    printItems.add(printItem);
                }

                // 组装最终打印数据
                Map<String, Object> retItem = new HashMap<>();
                retItem.put("pageTitle", printInfo.get("pageTitle"));
                retItem.put("printItems", printItems);
                retList.add(retItem);
            }
            response.setData(retList);
        }

        return response;
    }

    /**
     * 获取老版单份打印模板配置（对应原C# getOldSinglePrint）
     *
     * @param menuid 菜单ID
     * @return 打印模板配置（HashMap）
     */
    public Map<String, Object> GetOldSinglePrint(String menuid) {
        // 1. 获取老版打印配置数据表
        List<Map<String, Object>> table = DataImpl.getOldAppSinglePrint(menuid);

        // 2. 初始化打印配置，设置默认值
        Map<String, Object> htTable = new HashMap<>();
        htTable.put("pageWidth", 50);    // 默认宽度
        htTable.put("pageHeight", 30);   // 默认高度
        htTable.put("pageTitle", "默认打印模版"); // 老版默认标题

        // 3. 数据表非空且有数据时，覆盖默认值
        if (table != null && !table.isEmpty()) {
            // 获取首行数据（对应C# table.Rows[0]）
            Map<String, Object> firstRow = table.get(0);

            // 赋值页面宽度（空值时保留默认值）
            htTable.put("pageWidth", Objects.requireNonNullElse(firstRow.get("pageWidth"), 50));
            // 赋值页面高度
            htTable.put("pageHeight", Objects.requireNonNullElse(firstRow.get("pageHeight"), 30));
            // 赋值打印方向
            htTable.put("direction", firstRow.get("direction"));
        }

        return htTable;
    }

    /**
     * 获取新版单份打印模板配置（对应原C# getSinglePrint）
     *
     * @param formKey 表单Key
     * @return 打印模板配置（HashMap）
     */
    private Map<String, Object> GetSinglePrint(String formKey) {
        // 1. 获取新版打印配置数据表
        List<Map<String, Object>> table = DataImpl.getAppSinglePrint(formKey);

        // 2. 初始化打印配置，设置默认值
        Map<String, Object> htTable = new HashMap<>();
        htTable.put("pageWidth", 50);    // 默认宽度
        htTable.put("pageHeight", 30);   // 默认高度
        htTable.put("pageTitle", "");    // 新版默认标题为空

        // 3. 数据表非空且有数据时，覆盖默认值
        if (table != null && !table.isEmpty()) {
            // 获取首行数据（对应C# table.Rows[0]）
            Map<String, Object> firstRow = table.get(0);

            // 赋值页面宽度（空值时保留默认值）
            htTable.put("pageWidth", Objects.requireNonNullElse(firstRow.get("pageWidth"), 50));
            // 赋值页面高度
            htTable.put("pageHeight", Objects.requireNonNullElse(firstRow.get("pageHeight"), 30));
            // 赋值打印方向
            htTable.put("direction", firstRow.get("direction"));
            // 新版标题强制设为空（和原C#逻辑一致）
            htTable.put("pageTitle", "");
        }

        return htTable;
    }

    /**
     * 生成打印指令（完整还原原C#逻辑）
     *
     * @param btn 打印按钮对象
     * @return 打印指令Base64字符串 / null（条件不满足）/ 打印数据（条件满足）
     */
    private Object ToPrintCommand(SysPoPupMenuBtn btn) {
        // 核心条件判断：dllname包含print.lsp（忽略大小写） 且 !WindowsDirver
        boolean isPrintLsp = btn.dllname != null
                && btn.dllname.toLowerCase().contains("print.lsp");
        if (!(isPrintLsp && !isWindowsDirver())) {
            return null;
        }

        // 条件满足时，调用GetAppPrintData并返回其data字段（原代码提前返回逻辑）
        String recordJson = JSON.Encode(btn.Record); // 对应JSON.Encode
        int printType = toInt32(btn.dllpar2); // 对应ToInt32
        BaseResponse response = GetAppPrintData(recordJson, btn.getModuleId(), printType);
        return response.getData();
    }

    /**
     * 生成模块打印指令（默认版本2，对应C#可选参数ver=2）
     */
    private String GetModulePrintCommand
    (Map<String, Object> rec, List<Map<String, Object>> layouthashtab, String moduleId) {
        return GetModulePrintCommand(rec, layouthashtab, moduleId, 2);
    }

    /**
     * 生成模块打印指令（完整逻辑，对应原C#方法）
     *
     * @param rec           打印记录数据
     * @param layouthashtab 打印布局哈希表列表
     * @param moduleId      模块ID
     * @param ver           打印版本（2=新版，1=老版）
     * @return 组装后的打印指令字符串
     */
    private String GetModulePrintCommand
    (Map<String, Object> rec, List<Map<String, Object>> layouthashtab, String moduleId, int ver) {
        StringBuilder stringBuilder = new StringBuilder();
        try {
            // 初始化打印布局表和配置表（对应C# DataTable）
            List<Map<String, Object>> layoutTab = null;
            List<Map<String, Object>> printTable = DataImpl.getAppPrintRows(moduleId);

            if (ver == 2) { // 新版打印逻辑
                for (Map<String, Object> item : layouthashtab) {
                    // 获取打印项配置表（DataTable → List<Map>）
                    layoutTab = (List<Map<String, Object>>) item.get("items");

                    // 安全转换页面宽高（默认50/30）
                    int pageWidth = parseIntSafely(item.get("pageWidth"), 50);
                    int pageHeight = parseIntSafely(item.get("pageHeight"), 30);
                    // 获取页面标题（空值转为空字符串）
                    String pageTitle = Objects.toString(item.get("pageTitle"), "");

                    // 添加打印页开始命令
                    PrintHelper.getPrintPageStartCommandStr(stringBuilder, 0, 0, pageWidth, pageHeight, 0);

                    // 遍历布局行，组装字段打印指令
                    if (layoutTab != null) {
                        for (Map<String, Object> layoutrow : layoutTab) {
                            // 安全获取字段值（先判断是否存在，再取值）
                            String fieldname = getTableColumnValue(layoutrow, "fieldname");
                            String text = getTableColumnValue(layoutrow, "text");
                            String fieldType = getTableColumnValue(layoutrow, "fieldType");

                            // 安全转换布局参数（默认0）
                            int left = parseIntSafely(getTableColumnValue(layoutrow, "left"), 0);
                            int top = parseIntSafely(getTableColumnValue(layoutrow, "top"), 0);
                            int width = parseIntSafely(getTableColumnValue(layoutrow, "width"), 0);
                            int height = parseIntSafely(getTableColumnValue(layoutrow, "height"), 0);
                            int fontSize = parseIntSafely(getTableColumnValue(layoutrow, "fontSize"), 0);

                            // 获取打印值（rec中存在fieldname则取值，否则为空）
                            String value = rec.containsKey(fieldname) ? Objects.toString(rec.get(fieldname), "") : "";

                            // 添加字段打印指令
                            PrintHelper.getPrintPageStringCommandStr(stringBuilder, value, left, top, fontSize, 0);
                        }
                    }

                    // 添加打印页结束和打印命令
                    PrintHelper.getPrintPageEndCommandStr(stringBuilder);
                    PrintHelper.getPrintPagePrintCommandStr(stringBuilder);
                }
            } else { // 老版打印逻辑
                if (layouthashtab != null && !layouthashtab.isEmpty()) {
                    Map<String, Object> firstItem = layouthashtab.get(0);
                    // 获取打印项配置表
                    layoutTab = (List<Map<String, Object>>) firstItem.get("items");

                    // 安全转换页面宽高（默认50/30）
                    int pageWidth = parseIntSafely(firstItem.get("pageWidth"), 50);
                    int pageHeight = parseIntSafely(firstItem.get("pageHeight"), 30);
                    // 获取页面标题
                    String pageTitle = Objects.toString(firstItem.get("pageTitle"), "");

                    // 添加打印页开始命令
                    PrintHelper.getPrintPageStartCommandStr(stringBuilder, 0, 0, pageWidth, pageHeight, 0);

                    // 遍历布局行，组装字段打印指令
                    if (layoutTab != null) {
                        for (Map<String, Object> layoutrow : layoutTab) {
                            // 安全获取字段值
                            String fieldname = getTableColumnValue(layoutrow, "fieldname");
                            String text = getTableColumnValue(layoutrow, "text");
                            String fieldType = getTableColumnValue(layoutrow, "fieldType");

                            // 安全转换布局参数
                            int left = parseIntSafely(getTableColumnValue(layoutrow, "left"), 0);
                            int top = parseIntSafely(getTableColumnValue(layoutrow, "top"), 0);
                            int width = parseIntSafely(getTableColumnValue(layoutrow, "width"), 0);
                            int height = parseIntSafely(getTableColumnValue(layoutrow, "height"), 0);
                            int fontSize = parseIntSafely(getTableColumnValue(layoutrow, "fontSize"), 0);

                            // 获取打印值
                            String value = rec.containsKey(fieldname) ? Objects.toString(rec.get(fieldname), "") : "";

                            // 添加字段打印指令
                            PrintHelper.getPrintPageStringCommandStr(stringBuilder, value, left, top, fontSize, 0);
                        }
                    }

                    // 添加打印页结束和打印命令
                    PrintHelper.getPrintPageEndCommandStr(stringBuilder);
                    PrintHelper.getPrintPagePrintCommandStr(stringBuilder);
                }
            }
        } catch (Exception e) {
            // 原代码为空catch，保留该逻辑（如需排查问题可添加日志）
            // e.printStackTrace();
        }
        return stringBuilder.toString();
    }

    /**
     * 获取ESC指令打印命令（完整还原原C#逻辑）
     *
     * @param dataSource 打印数据源（List<Map>对应C# DataTable）
     * @param allHeight  总高度（数组封装实现ref传递）
     * @param nowHeight  当前高度（数组封装实现ref传递）
     * @return ESC指令字节数组列表
     */
    private List<byte[]> GetScanEscCommand(List<Map<String, Object>> dataSource, int[] allHeight,
                                           int[] nowHeight) {
        List<byte[]> bytes = new ArrayList<>();
        try {
            // 列名列表（对应C# dataSource.Columns）
            List<String> columnNames = getColumnNames(dataSource);
            int index = 0;

            // 遍历数据源行（对应C# dataSource.Rows）
            for (Map<String, Object> row : dataSource) {
                // 遍历列（对应C# dataSource.Columns）
                for (String colName : columnNames) {
                    String key = colName;
                    // 获取单元格值（空值转为空字符串）
                    String value = Objects.toString(row.get(colName), "");

                    // 初始化参数数组（7个元素，对应原pramars）
                    String[] pramars = new String[7];
                    // 拆分列名参数（列名_字体大小_加粗_宽度_对齐_换行_高度）
                    String[] sourceParmars = key.split("_");
                    // 填充参数（不足7个则留空）
                    for (int i = 0; i < pramars.length; i++) {
                        if (i < sourceParmars.length) {
                            pramars[i] = sourceParmars[i];
                        } else {
                            pramars[i] = "";
                        }
                    }

                    // 解析参数（安全转换，默认0）
                    int fontSizeModel = parseIntSafely(pramars[1], 0);
                    int boldMode = parseIntSafely(pramars[2], 0);
                    int positionx = parseIntSafely(pramars[3], 0);
                    int alginMode = parseIntSafely(pramars[4], 0);
                    int nextLine = parseIntSafely(pramars[5], 0);
                    int height = parseIntSafely(pramars[6], 0);

                    // 1. 设置字体大小
                    if (!isNullOrEmpty(pramars[1])) {
                        switch (fontSizeModel) {
                            case 1:
                                bytes.add(EscPrintHelper.selectCharacterSize(9));
                                break;
                            case 2:
                                bytes.add(EscPrintHelper.selectCharacterSize(32));
                                break;
                            case 3:
                                bytes.add(EscPrintHelper.selectCharacterSize(18));
                                break;
                            case 0:
                            default:
                                bytes.add(EscPrintHelper.selectCharacterSize(0));
                                break;
                        }
                    }

                    // 2. 设置加粗模式
                    if (!isNullOrEmpty(pramars[2])) {
                        bytes.add(EscPrintHelper.setBold(boldMode));
                    }

                    // 3. 处理字符宽度补空格
                    if (!isNullOrEmpty(pramars[3])) {
                        if (fontSizeModel == 0) {
                            fontSizeModel = 9; // 默认字体大小
                        }
                        // 计算需要补的空格数
                        int charTotalWidth = value.length() * fontSizeModel;
                        if (charTotalWidth < positionx) {
                            int padRightCount = (positionx - charTotalWidth) / fontSizeModel;
                            // 补空格（对应C# PadRight）
                            value = padRight(value, padRightCount + value.length(), ' ');
                        }
                    }

                    // 4. 设置对齐模式
                    if (!isNullOrEmpty(pramars[4])) {
                        bytes.add(EscPrintHelper.setAlign(alginMode));
                    }

                    // 5. 添加打印值
                    if (nowHeight[0] <= allHeight[0] || allHeight[0] == 0) {
                        bytes.add(EscPrintHelper.addValueStr(value));
                    }

                    // 6. 处理换行（原注释逻辑保留）
                    boolean isLastColumn = (index + 1) == (columnNames.size() * dataSource.size());
                    if ((!isNullOrEmpty(pramars[5]) && nextLine > 0 && !isNullOrEmpty(pramars[6]) && height > 0) || isLastColumn) {
                        int line = allHeight[0] - nowHeight[0];
                        if (line >= 0 && nextLine > 0) {
                            bytes.add(EscPrintHelper.printAndFeedLine(line + nextLine));
                        }
                        // 更新高度参数（ref传递）
                        allHeight[0] = height;
                        nowHeight[0] = 1;
                    } else if (!isNullOrEmpty(pramars[5])) {
                        if (nowHeight[0] <= allHeight[0] || allHeight[0] == 0) {
                            bytes.add(EscPrintHelper.printAndFeedLine(nextLine));
                        }
                        if (allHeight[0] > 0) {
                            nowHeight[0] += 1;
                        }
                    }

                    index++;
                }
            }
        } catch (Exception e) {
            // 原代码为空catch，保留该逻辑（如需排查问题可添加日志）
            // e.printStackTrace();
        }
        return bytes;
    }

    /**
     * 获取树形方案（对应原GetTreeScheme）
     *
     * @param moduleId 模块ID
     * @param _record  记录JSON字符串
     * @return 树形方案数据列表（Map列表）
     */
    public List<Map<String, Object>> GetTreeScheme(String moduleId, String _record) {
        // 初始化左侧记录哈希表
        Map<String, Object> leftRecord = new HashMap<>();

        // 解析JSON字符串（非空时）
        if (_record != null && !_record.trim().isEmpty()) {
            leftRecord = (Map<String, Object>) JSON.Decode(_record);
        }

        // 获取树形方案SQL模板并处理
        String sql = DataImpl.getTreeSchemeSql(moduleId);
        // 处理SQL模板 + 格式化替换speciesno参数
        String processedSql = dealQuerySql(sql, leftRecord, leftRecord, null, null, null);
        // 获取speciesno值（空值处理）
        String speciesno = Objects.toString(leftRecord.get("speciesno"), "");
        sql = String.format(processedSql, speciesno);

        // 执行SQL并转换为HashMap列表（对应ToHashTable）
        List<Map<String, Object>> dataTable = jdbcTemplate.queryForList(sql);
        return toHashTable(dataTable);
    }

    /**
     * 获取方案规则（对应原GetSchemeRule）
     *
     * @param groupId 分组ID
     * @return 封装后的BaseResponse响应对象
     */
    public BaseResponse GetSchemeRule(int groupId) {
        BaseResponse response = new BaseResponse();

        // 获取动态方案字段和规则
        List<Map<String, Object>> fields = DataImpl.getDynamicSchemeFields(groupId);
        List<Map<String, Object>> rules = DataImpl.getDynamicSchemeRule(groupId);

        // 生成控件数据并设置响应基础信息
        response.setData(createControl.createControl(fields, null, false));
        response.setSuccess(true);

        // 规则非空时，按fxname分组
        if (rules != null && !rules.isEmpty()) {
            // 按fxname分组（对应C# GroupBy + ToDictionary）
            Map<String, List<Map<String, Object>>> groupRules = rules.stream()
                    .collect(Collectors.groupingBy(
                            // 分组键：fxname字段值（空值转为空字符串）
                            row -> Objects.toString(row.get("fxname"), ""),
                            // 分组值：当前分组的所有行（对应CopyToDataTable）
                            Collectors.toList()
                    ));

            // 设置响应的other字段
            response.setOther(groupRules);
        }

        return response;
    }

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 移动附件文件信息
     *
     * @param fInfos 附件信息哈希表
     * @return 操作响应结果
     */
    public BaseResponse MoveAttcFileInfo(Map<String, Object> fInfos) throws SQLException {
        BaseResponse response = new BaseResponse();
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            // 1. 解析参数
            String moduleId = Objects.toString(fInfos.get("menucode"), "");
            String idValue = Objects.toString(fInfos.get("svalue"), "");
            String tidValue = Objects.toString(fInfos.get("tvalue"), "");
            String filename = Objects.toString(fInfos.get("filename"), "");
            String _filepath = Objects.toString(fInfos.get("filepath"), "");
            String _newdir = Objects.toString(fInfos.get("newdir"), "");
            String newname = isNullOrEmpty(Objects.toString(fInfos.get("newname"), ""))
                    ? filename : Objects.toString(fInfos.get("newname"), "");
            String _ver = Objects.toString(fInfos.get("ver"), "");
            String specno = Objects.toString(fInfos.get("specno"), "");

            // 获取父目录ID
            String dirTabId = DataImpl.GetAttcParentId(moduleId, idValue);
            // 类型ID转换（最小值为1）
            int typeid = Math.max(toInt32(Objects.toString(fInfos.get("typeid"), "0"), 0), 1);

            // 2. 查询文件信息
            List<Map<String, Object>> fileTbVal = DataImpl.GetAttcFileInfo(dirTabId, moduleId, filename, 0);
            if (fileTbVal == null || fileTbVal.isEmpty()) {
                response.setMsg("未找到文件！");
                if (!isNullOrEmpty(_filepath) && !isNullOrEmpty(_newdir)) {
                    response = FileUtil.moveTo(_filepath, _newdir, newname, getAttcPath(), false);
                }
                return response;
            }

            // 3. 提取文件信息
            Map<String, Object> fileRow = fileTbVal.get(0);
            String fileId = Objects.toString(fileRow.get("fileid"), "");
            String filePath = URLDecoder.decode(Objects.toString(fileRow.get("webpath"), ""), "UTF-8");

            // 4. 获取新目录路径
            _newdir = DataImpl.GetAcFileFolder(moduleId, tidValue, specno, new String[]{dirTabId});

            // 处理目录路径格式
            if (!_newdir.trim().replaceFirst("^/", "").toLowerCase()
                    .startsWith(WebConfigUtil_web.fileVPath.toLowerCase())) {
                _newdir = "/" + WebConfigUtil_web.fileVPath + "/" + _newdir.trim().replaceFirst("^/", "");
            }

            // 5. 检查存储过程是否存在并执行
            String proName = "BOM_TDPR";
            if (DataImpl.IsExitPro(proName)) {
                conn = jdbcTemplate.getDataSource().getConnection(); // 获取数据库连接
                pstmt = conn.prepareCall("{call " + proName + "(?,?,?,?)}");

                // 设置存储过程参数
                pstmt.setString(1, tidValue);
                pstmt.setString(2, "." + FileUtil.getFileExtension(filename));
                pstmt.setInt(3, typeid);
                pstmt.setString(4, _ver);

                // 执行存储过程
                rs = pstmt.executeQuery();
                Map<String, Object> retInfos = new HashMap<>();

                if (rs.next()) {
                    // 将ResultSet转换为HashMap
                    ResultSetMetaData metaData = rs.getMetaData();
                    int columnCount = metaData.getColumnCount();
                    for (int i = 1; i <= columnCount; i++) {
                        retInfos.put(metaData.getColumnName(i).toLowerCase(), rs.getObject(i));
                    }

                    // 处理文件名和版本
                    String ver = Objects.toString(retInfos.get("v"), "");
                    String newFilename = Objects.toString(retInfos.get("p"), "")
                            .replace("\\", "/");

                    // 调试日志
                    sysLog(String.format("生成文件版本%s,%s,%s,%s,%s,%s,%s",
                            JSON.Encode(retInfos), filename, typeid, tidValue,
                            FileUtil.getFileExtension(filename), JSON.Encode(fInfos),
                            Request("typeid", "")), "info");

                    // 移动文件
                    FileUtil.moveTo(filePath, _newdir, newFilename, getAttcPath(), false);

                    // 构建新的Web路径
                    String[] newFileWebPaths = (_newdir + "/" + newFilename).split("/");
                    StringBuilder newFileWebPath = new StringBuilder();
                    for (String pathSegment : newFileWebPaths) {
                        if (isNullOrEmpty(pathSegment)) continue;
                        newFileWebPath.append("/")
                                .append(FileUtil.urlEncode(pathSegment, false));
                    }

                    // 构建更新参数
                    Map<String, Object> attcInfos = new HashMap<>();
                    attcInfos.put("fileid", fileId);
                    attcInfos.put("vname", newFilename);
                    attcInfos.put("parentid", dirTabId);
                    attcInfos.put("fileno", ver);
                    attcInfos.put("creator", getUser().getUserId());
                    attcInfos.put("createtime", DATE_FORMAT.format(new Date()));
                    attcInfos.put("webpath", newFileWebPath.toString());

                    // 更新数据库
                    response = AddOrUpdTable(Collections.singletonList(attcInfos),
                            "P_fm_FileTab", "fileid", false, null);

                    // 记录系统日志
                    sysLog(String.format("%s在模块%s记录%s上传附件%s",
                            getUser().getUserName(), moduleId, idValue, newFilename), "上传附件");
                }
            } else {
                // 存储过程不存在时的处理逻辑
                FileUtil.moveTo(filePath, _newdir, newname, getAttcPath(), false);

                // 构建新的Web路径
                String[] newFileWebPaths = (_newdir + "/" + newname).split("/");
                StringBuilder newFileWebPath = new StringBuilder();
                for (String pathSegment : newFileWebPaths) {
                    if (isNullOrEmpty(pathSegment)) continue;
                    newFileWebPath.append("/")
                            .append(FileUtil.urlEncode(pathSegment, false));
                }

                // 构建更新参数
                Map<String, Object> attcInfos = new HashMap<>();
                attcInfos.put("fileid", fileId);
                attcInfos.put("sname", newname);
                attcInfos.put("vname", newname);
                attcInfos.put("parentid", dirTabId);
                attcInfos.put("creator", getUser().getUserId());
                attcInfos.put("createtime", DATE_FORMAT.format(new Date()));
                attcInfos.put("webpath", newFileWebPath.toString());

                // 更新数据库
                response = AddOrUpdTable(Collections.singletonList(attcInfos),
                        "P_fm_FileTab", "fileid", false, null);
            }
        } catch (Exception e) {
            response.setSuccess(false);
            response.setMsg("移动附件失败：" + e.getMessage());
            // 记录异常日志
            log.error("Exception caught", e);
        } finally {
            // 关闭JDBC资源
            if (rs != null) {
                rs.close();
            }
            if (pstmt != null) {
                pstmt.close();
            }
            if (conn != null) {
                conn.close();
            }
        }

        return response;
    }

    public Object GetAuditStepAndHis(ModuleBaseEntity module, String moduleId, String idValue, int atStep,
                                     boolean isBase, Integer billType) {
        // 1. 查询审批历史（对应C#: DataTable his = dataImpl.GetBaseFlowStep(...)）
        List<Map<String, Object>> his = DataImpl.GetBaseFlowStep(idValue, isBase);

        // 2. 查询审批步骤（对应C#: DataTable steps = dataImpl.GetHisAuditStepInfos(...)）
        List<Map<String, Object>> steps = DataImpl.GetHisAuditStepInfos(moduleId, atStep, billType, isBase);

        // 3. 转换steps为HashMap列表（对应C#的ToHashTable）
        List<Map<String, Object>> tabList = toHashTable(steps,
                null, null, null, true, true, false,
                (row, name, val) -> {
                    // 处理zfoper/operatorname字段
                    if ("zfoper".equals(name) || "operatorname".equals(name)) {
                        return DataImpl.GetSqlUser(
                                val == null ? "" : val.toString(),
                                getUser().getUserId(),
                                getUser().getUserName(),
                                module.Updrow
                        );
                    }
                    // 处理autostep字段
                    if ("autostep".equals(name)) {
                        String condtion = row.get("autostepcond") == null ? "" : row.get("autostepcond").toString();
                        if (!condtion.isEmpty() && toBoolean(createControl.evalCond(condtion, module.Updrow, null))) {
                            return 1;
                        }
                    }
                    return val;
                }
        );

        // 4. 初始化新列表（对应C#: IList<Hashtable> newList = new List<Hashtable>();）
        List<Map<String, Object>> newList = new ArrayList<>();

        // 5. 遍历处理每个步骤（对应C#的foreach循环）
        for (Map<String, Object> tab : tabList) {
            // 处理autostep字段：设置opeadvice为"将自动通过"
            if (tab.containsKey("autostep") && toBoolean(tab.get("autostep"))) {
                tab.put("opeadvice", "将自动通过");
            }

            // 获取stepcode并转换为int（对应C#: int tbStepCode = tab["stepcode"].ToInt32();）
            int tbStepCode = toInt32(tab.get("stepcode").toString());

            // 移除不需要的字段（对应C#: tab.Remove(...)）
            tab.remove("autostep");
            tab.remove("autostepcond");

            // 处理抄送逻辑（zfoper）
            String zfoper = tab.get("zfoper") == null ? "" : tab.get("zfoper").toString();
            if (!zfoper.isEmpty()) {
                // 构建抄送条目（对应C#的new Hashtable() { ... }）
                HashMap<String, Object> copyItem = new HashMap<>();
                copyItem.put("stepname", "抄送");
                copyItem.put("opeadvice", "将抄送" + zfoper.length() + "人");
                copyItem.put("operatorname", zfoper);
                // 处理applytime：atStep != 999&&tbStepCode>=atStep?"将抄送":"抄送"
                copyItem.put("applytime", (atStep != 999 && tbStepCode >= atStep) ? "将抄送" : "抄送");
                copyItem.put("sta", 'z');
                copyItem.put("zfopers", DataImpl.GetUserBmps(zfoper));

                tab.put("zfoper", ""); // 清空原zfoper
                newList.add(copyItem); // 添加抄送条目
            }

            // atStep != 999时添加当前步骤到新列表
            if (atStep != 999) {
                newList.add(tab);
            }
        }

        // 6. 拼接newList和his的结果（对应C#: newList.Concat(his.ToHashTable())）
        List<Map<String, Object>> hisList = toHashTable(his);
        List<Map<String, Object>> finalResult = new ArrayList<>();
        finalResult.addAll(newList);
        finalResult.addAll(hisList);

        return finalResult;
    }

    /**
     * 将手机端以前的卡片配置，转换为新的web用的配置
     *
     * @param dtVal 原始配置数据（Java 中 DataTable 常用 List<Map<String, Object>> 替代）
     * @return 转换后的 Web 端配置结构
     */
    private Object ConvertOldMobileCard(List<Map<String, Object>> dtVal) {
        // 1. 初始化卡片列表（对应 C# 的 List<MobileCard>）
        List<MobileCard> cards = new ArrayList<>();

        // 2. 遍历行数据（对应 C# 的 for 循环）
        int rowCount = dtVal.size();
        for (int i = 0; i < rowCount; i++) {
            Map<String, Object> row = dtVal.get(i);

            // 处理 color 字段（对应 C# 的 dtVal.Rows[i].Get("color") + ""）
            String color = row.get("color") == null ? "" : row.get("color").toString();

            // 条件判断：color 为空或等于 "0" 时赋值默认颜色
            if (color.isEmpty() || "0".equals(color)) {
                switch (i) {
                    case 0:
                        color = "#2b2b2b";
                        break;
                    case 1:
                        color = "#efb463";
                        break;
                    default:
                        color = "";
                        break;
                }
            }

            // 3. 创建 MobileCard 对象并赋值（对应 C# 的对象初始化器）
            MobileCard mobileCard = new MobileCard();
            mobileCard.setAlign(1);
            mobileCard.setBgColor("");
            mobileCard.setBold(i == 0); // 第0行加粗
            mobileCard.setContent(row.get("field") == null ? "" : row.get("field").toString());
            mobileCard.setDType(0);
            mobileCard.setDbColor("");
            mobileCard.setDfColor("");
            mobileCard.setFColor(color); // 字体颜色
            mobileCard.setFStrikeLine(false);
            mobileCard.setFitalic(false);
            mobileCard.setFontFamily("宋体");
            // 字体大小：第0行20，第1行12，其他行取size字段的整数值
            mobileCard.setFontSize(i == 0 ? 20 : (i == 1 ? 12 : Integer.parseInt(row.get("size").toString())));
            mobileCard.setRightAlign(false);
            // 行高：第0行30，第1行15，其他行20
            mobileCard.setRowHeight(i == 0 ? 30 : (i == 1 ? 15 : 20));
            mobileCard.setRowId(i);
            mobileCard.setSplitLine(i == 1); // 第1行显示分割线

            // 4. 添加到卡片列表
            cards.add(mobileCard);
        }

        // 5. 无卡片数据时返回 null
        if (cards.isEmpty()) {
            return null;
        }

        // 6. 构建最终返回结构（对应 C# 的 ArrayList + 匿名对象）
        // Java 无匿名对象，用 Map 模拟
        Map<String, Object> groupObj = new java.util.HashMap<>();
        groupObj.put("name", "分组");
        groupObj.put("showText", false);
        groupObj.put("mxId", 0);
        groupObj.put("items", cards);

        ArrayList<Object> result = new ArrayList<>();
        result.add(groupObj);

        return result;
    }
}




