package org.example.Impl.Sql.dmImpl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.example.Enums.CusGridColumnPrefix;
import org.example.Enums.SystemEnums;
import org.example.Impl.Sql.provider.AllInOneSqlProvider;
import org.example.ModuleApi.ModuleAjaxApi.mapper.DMCrmMapper;
import org.example.Utils.DMJdbcMultiResultSetUtil;
import org.example.Utils.DataTableUtil;
import org.example.Utils.PublicUtil;
import org.example.Utils.SqlSafetyGuard;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.CallableStatementCallback;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.StatementCallback;
import org.springframework.stereotype.Service;

import javax.sql.rowset.CachedRowSet;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.example.Utils.NativeExtensionUtils.*;

@Service
public class DmAllInOneSqlProvider implements AllInOneSqlProvider {
    private static final Logger log = LoggerFactory.getLogger(DmAllInOneSqlProvider.class);

    // 成员变量保存Mapper
    private final DMCrmMapper dmCrmMapper;
    private JdbcTemplate jdbcTemplate;

    // 构造器注入（推荐，可控且便于测试）
    public DmAllInOneSqlProvider(DMCrmMapper dmCrmMapper, JdbcTemplate jdbcTemplate) {
        this.dmCrmMapper = dmCrmMapper;
        this.jdbcTemplate = jdbcTemplate;
    }

    //    region SysUserImpl
    @Override
    public String LoginSql() {
        return "select e.EmployeeId, e.EmpLoyeeName, e.LoginAccount, e.password, " + "e.p_emp_clientid, e.p_emp_AttendanceTime, e.p_emp_logintype, " + "e.p_emp_PwdErrNum, e.p_emp_PwdLocked, e.p_emp_PwdLockDate, e.AppIndex " + "from P_EmployeeTab e " + "left join P_customertab b on e.p_emp_clientid = b.id and b.coid = ? " + "where (e.LoginAccount = ? or e.EmployeeName = ? or e.p_emp_phone = ? or isnull(b.id, '') <> '') " + "and isnull(e.sign, 0) = 0 " + "and isnull(e.UseFlag, 0) = 1";
    }
//    endregion

//    region  DataImpl

    @Override
    public String getSysdbGroupByIdSql() {
        return "select dbname as name, isnull(localIp, ip) as ip, mobileurl, showname as text " + "from p_sydbGroupTab where id = ?";
    }

    @Override
    public String getSysdbGroupAllSql() {
        return "select id, showname as text, dbname from p_sydbGroupTab order by orderid";
    }

    @Override
    public String getExitSystemPrivilegeAgentTabSql() {
        return "select 1 from ALL_TABLES where UPPER(OWNER) = UPPER(SYS_CONTEXT('USERENV','CURRENT_SCHEMA')) and UPPER(TABLE_NAME) = UPPER('p_systemPrivilegeAgentTab')";
    }

    @Override
    public String getExitFlowExOperSql() {
        return "select 1 " + "from ALL_TABLES " + "where UPPER(OWNER) = UPPER(SYS_CONTEXT('USERENV','CURRENT_SCHEMA'))" + " and UPPER(TABLE_NAME) = UPPER('wms_billflowOperEx')";
    }


    @Override
    public String GetBaesModuleLeftSql() {
        return "select id detailid,fieldname,ISNULL(userenname,sysname) fieldcaption,fieldkey fromkey,fieldsqlid valuemember,fieldsqlname displaymember from p_systemwordbooktab where tab= ?  and fieldsqltag= ?";
    }

    @Override
    public String GetTableColumnTypeSql() {
        return "Select TYPE$ from syscolumns Where ID=OBJECT_ID('%s') and name='%s'";
    }


    @Override
    public String GetColumnRowsSql(String moduleId, String userId, String userName, int id, boolean windowsDirver) {
        return "";
    }

    @Override
    public String GetBaesModuleLeftSql(String moduleId) {
        return "select id detailid, " + "fieldname, " + "ISNULL(userenname, sysname) fieldcaption, " + "fieldkey fromkey, " + "fieldsqlid valuemember, " + "fieldsqlname displaymember " + "from p_systemwordbooktab " + "where tab = ? and fieldsqltag = ?";
    }

    @Override
    public String getBaesModuleLeftSql(String moduleId) {
        return "";
    }

    @Override
    public String GetBaesModuleBmpFieldsSql(String moduleId, String tbName) {
        String sql = "select fieldname from p_systemwordbooktab w " + "inner join syscolumns col on col.id=OBJECT_ID('%s') and col.TYPE$='BLOB' and col.name=w.fieldname " + "where tab='%s' and fieldsqltag='%d'";
        sql = String.format(sql, tbName, moduleId, SystemEnums.ControlType.LabPic.getValue());

        return sql;
    }

    @Override
    public String GetTableInfoSql(String trimmedTbName) {
        String sql = "SELECT\n" + "    c.NAME AS name,\n" + "    c.TYPE$ AS xtype,\n" + "    CASE \n" + "        WHEN c.TYPE$ IN ('BLOB','TEXT','VARBINARY') THEN 0 \n" + "        ELSE c.LENGTH$ \n" + "    END AS length,\n" + "    CASE WHEN c.NULLABLE$ = 'N' THEN 0 ELSE 1 END AS isnullable,\n" + "    CASE WHEN a.id IS NOT NULL THEN NULL ELSE c.DEFVAL END AS text,\n" + "CASE\n" + " WHEN c.INFO2 = 1 AND a.id IS NULL THEN 1\n" + "WHEN a.id IS NOT NULL AND c.INFO2 = 0 THEN 4\n" + "else 0\n" + "END AS colstat," + "    CASE WHEN c.INFO2 = 1 THEN 1 ELSE 0 END AS isIdentity,\n" + "    CASE WHEN a.id IS NOT NULL THEN 1 ELSE 0 END AS isComputed\n" + "FROM\n" + "    SYSCOLUMNS c\n" + "LEFT JOIN syscolinfos a\n" + "    ON a.id = c.id AND a.colid = c.colid\n" + "WHERE\n" + "    c.ID = OBJECT_ID('%s');";
        sql = String.format(sql, trimmedTbName);
        return sql;
    }


    @Override
    public String GetColorAndBoxColumnsSql() {
        return "select fieldname, fieldsqltag FieldType, fieldsql, " + "fieldsqlid valuemember, fieldsqlname displaymember, " + "TitleColor fontcolor " + "from p_systemwordbooktab zsc " + "where zsc.tab='%s' and " + "(isnull(TitleColor,'')<>'' or " + "(isnull(fieldsql,'')<>'' and isnull(fieldsqlname,'')<>'' and isnull(fieldsqlid,'')<>'' ))";
    }

    @Override
    public String GetBaesModuleDetailsSql() {
        return "select detail.orderid, detail.displayRows, detail.id, detail.detailName, library, " + "detail.detailsql, detail.autorefresh refresh, detail.unionvalue unionfield, unionCond, " + "noGridLine, noRownumber, noColumnHeader hideColumnHeader, detail.isDrag, detail.defaultitem," + "detail.unionparentfield, detail.unionmodule, detail.formkey, detail.detailType, " + "formKey fromkey, addVisible, visibleCond, fieldCond, disableField, fieldCond1, disableField1, " + "case when detail.gridDetailCheck=1 then 1 else detail.gridDetailCheck end multcheck, " + "displaymode, addShowMode%s " + "from p_systemDlltabDetail detail " + "where tabKey=? and isnull(isVisible,0)=0 " + "order by OrderID";
    }

    @Override
    public String GetAttcFilesSql() {
        return "select p.EmployeeName username, f.* from P_fm_FileTab f " + "inner join bmp_ProductSpeciesTab op on f.speciesno = op.speciesno and " + "(INSTR(',' || REPLACE(REPLACE(op.uploadOper, ' ', ''), ' ', '') || ',', ',%s,') > 0 or (NVL(op.uploadOper, '') = '' OR NVL(op.uploadOper, '') IS NULL) " + "or INSTR(',' || REPLACE(REPLACE(op.downloadOper, ' ', ''), ' ', '') || ',', ',%s,') > 0 or (NVL(op.downloadOper, '') = '' OR NVL(op.downloadOper, '') IS NULL) " + "or INSTR(',' || REPLACE(REPLACE(op.deleteOper, ' ', ''), ' ', '') || ',', ',%s,') > 0 or (NVL(op.deleteOper, '') = '' OR NVL(op.deleteOper, '') IS NULL) " + "or INSTR(',' || REPLACE(REPLACE(op.previewOper, ' ', ''), ' ', '') || ',', ',%s,') > 0 or (NVL(op.previewOper, '') = '' OR NVL(op.previewOper, '') IS NULL)) " + "left join p_employeetab p on f.creator = p.employeeid " + "where f.parentid = '%s' %s %s";
    }

    @Override
    public String GetAbsFilePathSql() {
        return "select fun_fm_getAbsolutePath(%s)";
    }

    @Override
    public String GetAcFileFolderSql() {
        return "select fun_fm_getRelativePath(%s)";
    }

    @Override
    public String GetAcFileFolderByCount() {
        return "select fun_fm_getRelativePath(?, ?)";
    }

    @Override
    public String GetAcFileFolderAllCount() {
        return "select fun_fm_getRelativePath(?)";
    }

    @Override
    public String GetPmsCountSql() {
        return "SELECT COUNT(*) FROM ALL_ARGUMENTS WHERE OBJECT_NAME = ?" + "  AND TRIM(NVL(ARGUMENT_NAME, ' ')) <> ' 'AND IN_OUT <> 'OUT'";
    }

    @Override
    public  List<List<Map<String, Object>>> BaseDataSaveSql(String procName, String escapedBaseSql, int execType, String escapedModuleId, String escapedMasterTable, String escapedIdField, String escapedIdValue, String escapedOperatorId, String escapedOperatorName) {
        // 1. 保留原有拼接逻辑
        StringBuilder sql = new StringBuilder();
        sql.append("DECLARE ").append("DM_return INT; ").append("DM_msg VARCHAR(2000); ").append("BEGIN ").append("CALL ").append(procName).append("( ").append("DM_return, ")
                .append("'").append(escapedBaseSql).append("', ")// 参数2：动态SQL（字符串，加单引号）
                .append(execType).append(", ")                  // 参数3：execType（数值，不加单引号）
                .append("'").append(escapedModuleId).append("', ")// 参数4：modid（字符串）
                .append("'").append(escapedMasterTable).append("', ")// 参数5：tablename（字符串）
                .append("'").append(escapedIdField).append("', ")// 参数6：keyfield（字符串）
                .append("'").append(escapedIdValue).append("', ")// 参数7：keyvalue（字符串）
                .append(Integer.parseInt(escapedOperatorId)).append(", ")// 参数8：operatorid（数值）
                .append("'").append(escapedOperatorName).append("', ")// 参数9：operatorname（字符串）
                .append("DM_msg, ")                             // 参数10：OUT消息（固定变量）
                .append("0 ")                                   // 参数11：confirmFlag（固定值0）
                .append("); ").append("SELECT DM_return AS returnValue, DM_msg AS outputValue FROM DUAL;").append("END;");

        // 调用工具类获取所有结果集，解决结果集不匹配问题
        List<List<Map<String, Object>>> AllResultSets = DMJdbcMultiResultSetUtil.executeMultiResultSet(jdbcTemplate, sql.toString());
        // 筛选包含 returnValue 的目标结果集
//        Map<String, Object> ResultMap = new HashMap<>();
//        for (List<Map<String, Object>> resultSet : AllResultSets) {
//            if (!resultSet.isEmpty() && resultSet.get(0).containsKey("returnValue")) {
//                ResultMap = resultSet.get(0);
//                break;
//            }
//        }

        // 兜底处理（按需处理，与公用方法解耦）
//        targetResult.putIfAbsent("returnValue", 0);
//        targetResult.putIfAbsent("outputValue", "");
//
//        // （可选）保留所有结果集，方便后续扩展
//        targetResult.put("allResultSets", AllResultSets);

        return AllResultSets;

    }


    @Override
    public String IsExitProSql() {
        return "select 1 from sysobjects where id = object_id('%s')AND SUBTYPE$ = 'PROC';";
    }

    @Override
    public List<Map<String, Object>> ExecSelectOperStoreSql(List<String> outParamNames, String storeName, Map<String, Object> params) {
        Connection connection = null;
        CallableStatement dmCallableStmt = null;
        try {
            StringBuilder sql = new StringBuilder();
            if (storeName.equalsIgnoreCase("p_billApply")) {
                // 清空原有内容，重新拼接（也可直接append）
                sql.append("DECLARE ")
                        .append("rtn_code INT; ")                  // 对应新增的p_return_code OUT INT
                        .append("p_msg VARCHAR2; ")           // 对应p_msg OUT VARCHAR2
                        .append("BEGIN ")
                        .append("CALL ").append(storeName).append("( ")
                        // 按存储过程参数顺序拼接：先输出参数，后输入参数
                        .append(" rtn_code ,")                      // 1. p_return_code OUT INT
                        .append(getParamValue(params, "@typeCode"))        // 2. p_typeCode IN VARCHAR2
                        .append(", ").append(getParamValue(params, "@billDocumentId")) // 3. p_billDocumentId IN VARCHAR2
                        .append(", ").append(getParamValue(params, "@operatorId"))     // 4. p_operatorId IN INT
                        .append(", ").append(getParamValue(params, "@operatorName"))   // 5. p_operatorName IN VARCHAR2
                        .append(", ").append(getParamValue(params, "@comfirmType"))    // 6. p_comfirmType IN INT
                        .append(", p_msg ")                         // 7. p_msg OUT VARCHAR2
                        .append("); ")
                        // 查询输出参数并返回（仅保留实际的输出参数）
                        .append("SELECT rtn_code AS returnCode, p_msg AS msg FROM DUAL;")
                        .append("END;");
            } else if (storeName.equalsIgnoreCase("p_baseApply")) {
                sql.append("DECLARE ")
                        .append("rtn_code INT; ")                  // 输出：状态返回码
                        .append("p_msg VARCHAR(32000); ")           // 输出：返回结果
//                        .append("p_nextSelectStepCode VARCHAR(100); ") // 输出：下步审核步骤
//                        .append("p_nextSelectStepOper VARCHAR(8000); ") // 输出：下步审核人员
                        .append("BEGIN ")
                        .append("CALL ").append(storeName).append("( ")
                        // 输入参数（从params中获取，按存储过程参数顺序）
                        .append(" rtn_code ,")                      // rtn_code OUT INT
                        .append(getParamValue(params, "@typeCode"))        // p_typeCode IN VARCHAR(10)
//                        .append(", ").append(getParamValue(params, "@stepCode"))       // p_stepCode IN INT
                        .append(", ").append(getParamValue(params, "@billDocumentId")) // p_billDocumentId IN VARCHAR(50)
                        .append(", ").append(getParamValue(params, "@operatorid"))     // p_operatorId IN INT
                        .append(", ").append(getParamValue(params, "@operatorName"))   // p_operatorName IN VARCHAR(20)
                        .append(", ").append(getParamValue(params, "@comfirmType"))    // p_auditAdvice IN VARCHAR(500)
//                        .append(", ").append(getParamValue(params, "@Direction"))      // p_Direction IN CHAR(1)
                        .append(", p_msg ")
//                        .append(", ").append(getParamValue(params, "@backStepCode", "-1")) // p_backStepCode IN INT := -1
//                        .append(", ").append(getParamValue(params, "@hint_opers", "''"))  // p_hint_opers IN VARCHAR(5000) := ''
//                        .append(", ").append(getParamValue(params, "@comfirm_opers", "''")) // p_comfirm_opers IN VARCHAR(5000) := ''
//                        .append(", ").append(getParamValue(params, "p_selectConfirmFlag", "0")) // p_selectConfirmFlag IN INT := 0
                        // 输出参数（绑定声明的变量）
                        // p_msg OUT VARCHAR(3000)
//                        .append(", p_nextSelectStepCode ")          // p_nextSelectStepCode OUT VARCHAR(100)
//                        .append(", p_nextSelectStepOper ")          // p_nextSelectStepOper OUT VARCHAR(8000)
                        .append("); ")
                        // 查询输出参数并返回
                        .append("SELECT rtn_code AS returnCode, p_msg AS msg FROM DUAL;")
//                        .append("p_nextSelectStepCode AS nextStepCode, p_nextSelectStepOper AS nextStepOper FROM DUAL;")
                        .append("END;");
            } else if (storeName.equalsIgnoreCase("p_BaseAudit")) {
                sql.append("DECLARE ")
                        .append("rtn_code INT; ")                  // 输出：状态返回码（存储过程第一个OUT参数）
                        .append("p_msg VARCHAR(3000); ")           // 输出：返回结果（存储过程第8个参数，OUT类型）
                        .append("BEGIN ")
                        // 2. CALL段：严格按存储过程参数顺序拼接，匹配IN/OUT/默认值规则
                        .append("CALL ").append(storeName).append("( ")
                        // 核心修正：参数顺序完全匹配存储过程定义，移除多余的p_nextSelectStepCode/p_nextSelectStepOper
                        // 第1个参数：p_typeCode IN VARCHAR(10)
                        .append(getParamValue(params, "@typeCode"))
                        // 第2个参数：p_stepCode IN INT
                        .append(", ").append(getParamValue(params, "@stepCode"))
                        // 第3个参数：p_billDocumentId IN VARCHAR(200)
                        .append(", ").append(getParamValue(params, "@billDocumentId"))
                        // 第4个参数：p_operatorId IN INT（注意参数名统一为@operatorId，避免拼写错误operatorid）
                        .append(", ").append(getParamValue(params, "@operatorId"))
                        // 第5个参数：p_operatorName IN VARCHAR(40)
                        .append(", ").append(getParamValue(params, "@operatorName"))
                        // 第6个参数：p_auditAdvice IN VARCHAR(500)
                        .append(", ").append(getParamValue(params, "@auditAdvice"))
                        // 第7个参数：p_Direction IN CHAR(1)
                        .append(", ").append(getParamValue(params, "@Direction"))
                        // 第8个参数：p_msg OUT VARCHAR(3000)（绑定声明的p_msg变量）
                        .append(", p_msg ")
                        // 第9个参数：p_backStepCode IN VARCHAR(50) := '-1'（带默认值，无值时用'-1'）
                        .append(", ").append(getParamValue(params, "@backStepCode", "-1"))
                        // 第10个参数：p_hint_opers IN VARCHAR(8000) := ''（带默认值，无值时用空字符串）
                        .append(", ").append(getParamValue(params, "@hint_opers", "''"))
                        // 第11个参数：p_comfirm_opers IN VARCHAR(8000) := ''（带默认值，无值时用空字符串）
                        .append(", ").append(getParamValue(params, "@comfirm_opers", "''"))
                        .append("); ")
                        // 3. SELECT段：仅返回存储过程实际的OUT参数（rtn_code/p_msg），移除多余字段
                        .append("SELECT rtn_code AS returnCode, p_msg AS msg FROM DUAL;")
                        .append("END;");

            } else if (storeName.equalsIgnoreCase("p_billAudit")) {
                sql.append("DECLARE ")
                        .append("rtn_code INT; ")                  // 输出：返回状态码（第一个OUT参数，INT类型）
                        .append("p_msg VARCHAR(3000); ")           // 输出：返回结果（第八个参数，OUT VARCHAR(3000)）
                        .append("BEGIN ")
                        // 2. CALL段：严格按存储过程参数顺序拼接，区分IN/OUT参数
                        .append("CALL ").append(storeName).append("( ")
                        // 第1个参数：rtn_code OUT INT（绑定声明的rtn_code变量）
                        .append("rtn_code ,")
                        // 第2个参数：p_typeCode IN VARCHAR(10)
                        .append(getParamValue(params, "@typeCode"))
                        // 第3个参数：p_stepCode IN INT
                        .append(", ").append(getParamValue(params, "@stepCode"))
                        // 第4个参数：p_billDocumentId IN VARCHAR(50)
                        .append(", ").append(getParamValue(params, "@billDocumentId"))
                        // 第5个参数：p_operatorId IN INT（修正拼写，统一为@operatorId）
                        .append(", ").append(getParamValue(params, "@operatorId"))
                        // 第6个参数：p_operatorName IN VARCHAR(20)
                        .append(", ").append(getParamValue(params, "@operatorName"))
                        // 第7个参数：p_auditAdvice IN VARCHAR(500)
                        .append(", ").append(getParamValue(params, "@auditAdvice"))
                        // 第8个参数：p_Direction IN CHAR(1)
                        .append(", ").append(getParamValue(params, "@Direction"))
                        // 第9个参数：p_msg OUT VARCHAR(3000)（绑定声明的p_msg变量）
                        .append(", p_msg ")
                        // 第10个参数：p_backStepCode IN INT := -1（带默认值-1，INT类型）
                        .append(", ").append(getParamValue(params, "@backStepCode", "-1"))
                        // 第11个参数：p_hint_opers IN VARCHAR(5000) := ''（带默认值空字符串）
                        .append(", ").append(getParamValue(params, "@hint_opers", "''"))
                        // 第12个参数：p_comfirm_opers IN VARCHAR(5000) := ''（带默认值空字符串）
                        .append(", ").append(getParamValue(params, "@comfirm_opers", "''"))
                        .append("); ")
                        // 3. SELECT段：仅返回存储过程的OUT参数（rtn_code/p_msg）
                        .append("SELECT rtn_code AS returnCode, p_msg AS msg FROM DUAL;")
                        .append("END;");
            } else if (storeName.equalsIgnoreCase("p_baseAuditBack")) {
                sql.append("DECLARE ")
                        .append("rtn_code INT; ")                  // 输出：状态返回码（第一个OUT参数，INT类型）
                        .append("p_msg VARCHAR(3000); ")           // 输出：返回结果（第九个参数，OUT VARCHAR(3000)）
                        .append("BEGIN ")
                        // 2. CALL段：严格按存储过程参数顺序拼接，区分IN/OUT参数
                        .append("CALL ").append(storeName).append("( ")
                        // 第1个参数：rtn_code OUT INT（绑定声明的rtn_code变量）
                        .append("rtn_code ,")
                        // 第2个参数：p_typeCode IN VARCHAR(10)
                        .append(getParamValue(params, "@typeCode"))
                        // 第3个参数：p_stepCode IN INT
                        .append(", ").append(getParamValue(params, "@stepCode"))
                        // 第4个参数：p_billDocumentId IN VARCHAR(200)（长度恢复为200）
                        .append(", ").append(getParamValue(params, "@billDocumentId"))
                        // 第5个参数：p_operatorId IN INT（修正拼写，统一为@operatorId）
                        .append(", ").append(getParamValue(params, "@operatorId"))
                        // 第6个参数：p_operatorName IN VARCHAR(20)
                        .append(", ").append(getParamValue(params, "@operatorName"))
                        // 第7个参数：p_auditAdvice IN VARCHAR(500)
                        .append(", ").append(getParamValue(params, "@auditAdvice"))
                        // 第8个参数：p_Direction IN CHAR(1)（仅F/R两种值）
                        .append(", ").append(getParamValue(params, "@Direction"))
                        // 第9个参数：p_msg OUT VARCHAR(3000)（绑定声明的p_msg变量）
                        .append(", p_msg")
                        .append("); ")
                        // 3. SELECT段：仅返回存储过程的OUT参数（rtn_code/p_msg）
                        .append("SELECT rtn_code AS returnCode, p_msg AS msg FROM DUAL;")
                        .append("END;");

            }
            log.debug(String.valueOf(sql.toString() + " ExecSelectOperStoreSql "));
            List<Map<String, Object>> result = jdbcTemplate.queryForList(sql.toString());
            return result;

        } catch (Exception e) {
            String errorMsg = String.format("达梦存储过程调用失败，存储过程名：%s", storeName);
            throw new RuntimeException(errorMsg, e);
        } finally {
            // 修复资源泄漏：finally块统一关闭，无论是否异常都执行
            try {
                if (dmCallableStmt != null) dmCallableStmt.close();
            } catch (SQLException ex) {
                log.error("Exception caught", ex);
            }
            try {
                if (connection != null) connection.close();
            } catch (SQLException ex) {
                log.error("Exception caught", ex);
            }
        }
    }

    private String getParamValue(Map<String, Object> params, String paramKey) {
        return getParamValue(params, paramKey, null);
    }

    private String getParamValue(Map<String, Object> params, String paramKey, String defaultValue) {
        Object value = params != null ? DataTableUtil.get(params, paramKey) : null;
        // 如果参数为空，使用默认值
        if (value == null || value.toString().isEmpty()) {
            return defaultValue == null ? "NULL" : defaultValue;
        }

        // 字符串类型参数加单引号，数值类型直接返回
        if (value instanceof String) {
            return "'" + escapeSql(value.toString()) + "'"; // 防SQL注入转义
        } else {
            return value.toString();
        }
    }

    // 辅助方法：SQL转义（防止单引号导致语法错误）
    private String escapeSql(String str) {
        return str.replace("'", "''");
    }

    @Override
    public String hasStoreParametersql(String procedureName, String paramName) {
        // 处理达梦参数名：去掉开头的@符号（如果有）
        String DMparamName = paramName != null && paramName.startsWith("@") ? paramName.substring(1)  // 去掉开头的@
                : paramName;              // 无@则原样返回

        return String.format("SELECT ARGUMENT_NAME \n" + "FROM ALL_ARGUMENTS \n" + "WHERE  OBJECT_NAME = '%s'\n" + "  AND ARGUMENT_NAME = '%s';", procedureName, DMparamName);
    }

    @Override
    public String GetAttcBmpSpecSql() {
        return "select bmpSpec from v_systemdlltab where DllCoid= '%s'";
    }

    @Override
    public String GetIdentityFieldSql(String finaltbName) {
        String sql = "SELECT sc.NAME AS COLUMN_NAME FROM DBA_OBJECTS do" + "JOIN SYSCOLUMNS sc ON do.OBJECT_ID = sc.ID" + "WHERE do.OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA')" + "AND do.OBJECT_NAME = '%s'" + "AND sc.INFO2 = 1";
        String identitySql = String.format(sql, finaltbName);
        return identitySql;
    }

    @Override
    public String CheckIsMulitAuditSql() {
        return "SELECT CASE WHEN EXISTS (SELECT 1 FROM DBA_OBJECTS do JOIN SYSCOLUMNS sc ON do.OBJECT_ID = sc.ID WHERE do.OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') AND do.OBJECT_NAME = 'wms_billflowOperView' AND sc.NAME = 'auditoperators' ) THEN 1  ELSE 0 END AS col_exists";
    }

    @Override
    public String GetTaskMobileCardColumnSql() {
        return "";
    }

    @Override
    public String GetAuditBaseDetailsSql() {
        return "SELECT CASE WHEN EXISTS " + "(SELECT 1 FROM DBA_OBJECTS do WHERE do.OBJECT_ID = OBJECT_ID" + "(CONCAT(SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA'), '.', 'p_SystemDlltabDetailFlowTab'))" + "AND do.OBJECT_TYPE = 'TABLE' AND do.OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA')" + "AND do.OWNER NOT IN ('SYS', 'SYSAUDITOR', 'SYSJOB')) THEN 1 ELSE 0  END AS is_user_table;";
    }

    @Override
    public String GetAttTreeDataSql() {
        return "select speciesno, speciesname, " + "LEFT(speciesno, LEN(speciesno) - 2) as parentid " +  // 此处已修改
                "from bmp_ProductSpeciesTab spec " + "inner join (" + "    select bmpspec, dllcoid " + "    from v_systemdlltab " + "    where dllcoid = ? " + "    group by bmpspec, dllcoid" + ") vd on (spec.SpeciesNo like CONCAT(COALESCE(vd.bmpSpec, ''), '%'))";
    }

    @Override
    public String GetAttTreeDataSqlisEmpty() {
        return "select bmpspec as speciesno, bmpspec as speciesname, '01' as parentid " + "from v_systemdlltab " + "where dllcoid = ? " + "group by bmpspec, dllcoid";
    }

    @Override
    public String GetSqlUserSql() {
//        return "SELECT LISTAGG(employeename + ',', '') FROM p_employeetab WHERE (%s)";
        return "SELECT WM_CONCAT(EmployeeName) FROM p_employeetab WHERE (%s) AND EmployeeName <> '管理员'";

    }

    @Override
    public String GetSqlUserByroleSql() {
        //return "SELECT  LISTAGG(aa.operatorname + ',', '') FROM p_systemRoleSetTab a  " + "JOIN p_systemRoleOperSetTab aa ON a.id = aa.roleid " + "WHERE a.roleName = '%s' FOR XML PATH('')";
        return "SELECT WM_CONCAT(aa.operatorname) FROM p_systemRoleSetTab a " +
                "JOIN p_systemRoleOperSetTab aa ON a.id = aa.roleid " +
                "WHERE a.roleName = '%s'";
    }

    @Override
    public String hasExistsTableSql(String tableName) {
        return "";
    }

    @Override
    public String GetUsersSql() {
        return "";
    }

    @Override
    public String GetAccountInfoByStoreSql() {
        return "";
    }

    @Override
    public String GetProSysTypeSql() {
        return "";
    }

    @Override
    public String CheckUserAbleSql(Map<String, Object> updRow, String trimmedUsers) {
        return "SELECT LISTAGG(employeename + ',', '') FROM p_employeetab " + "where (" + PublicUtil.ReqSqlPmsByRow(updRow, null, trimmedUsers) + ")";
    }

    @Override
    public String GetNextSelectStepListSql() {
        return "select stepcode, stepname from %s " + "where typecode='%s' " + "and INSTR(';%s;', ';' || CAST(stepcode AS VARCHAR2) || ';') > 0";
    }

    @Override
    public String GetColumnRows(String moduleId, String userId, String userName, int id, boolean windowsDirver) {
        // 构建基础SQL（适配达梦语法，修复作用域问题）
        String disabledPart = windowsDirver ? "case when nvl(vislble, 0) = 1 then 1 else nvl(1-gcfg.isvisible, 0) end" : "case when nvl(ShowMobile,0)=0 then 1 else 0 end";

        String disabled2Part = windowsDirver ? "book.addVisible" : "case when nvl(ShowMobile1,0)=0 then 1 else 0 end";

        String scanAblePart = windowsDirver ? "0" : "book.vislble5";

        String widthPart = windowsDirver ? "nvl(gcfg.fieldwidth,width)" : "mobilewidth";

        // 拼接主SQL（核心修改：将where条件和order by移到子查询内）
        String colsql = String.format("select * from ( " + "select book.id,book.FieldName,nvl(cast(gcfg.userName as varchar(200)), nvl(nvl(username1,sysname),book.FieldName)) FieldCaption," + "ControlWidth ,ControlHeight,%s Disabled ,%s Disabled2, fieldsqltag FieldType,addModuleId,%s scanAble," + "ControlTop,ControlLeft,book.tagid nullable,book.cancopy," + "case when nvl(book.privilegeOper,'')='' then edit when instr(','||(SELECT wm_concat(userList) FROM p_systemPrivilege pri " + "WHERE book.tab=pri.modid and instr(','||cast(pri.privTypeId as varchar(10))||',',','||book.privilegeOper||',')>0)||',',',%s,')>0 then 0 else 1 end Edit," + "isSum [sum],sumCond,sumCalc,fieldsql,fieldsqlid valuemember,fieldsqlname displaymember,calcExpr CalcExpress,calcOrder CalcOrderId,%s width," + "book.fieldkey fromkey,book.disableType,book.IsAddControl,book.dataAlign textalign," + "unionValue UnionSQL,unionFields UnionField,dataformat,BandTitle,BandFields BandField,InputHintText HintText,TitleColor FontColor," + "limitLen LimitMaxValue,defaultdate defaultvalue,TM_HeadString othermember,book.labelWidth,book.labelAlign,book.disableCond,c.TYPE$ fieldDbType," + "frozenFlag locked,(SELECT wm_concat(userList) FROM p_systemPrivilege pri WHERE book.tab=pri.modid and instr(','||cast(pri.privTypeId as varchar(10))||',',','||book.privilegeView||',')>0) userList," + "book.FontSize fontSize,book.highlightFColor fcolor,book.highlightBColor bcolor,book.highlightBold bold,book.doNotSpelling," + "book.lookupWidth pickerWidth,book.lookupFieldsWidth pickerColsWidth,book.bmptype ftype,book.bmpsource,book.ifmerge rowspan,ScanAfterEmpty scanClear "
                        + "from p_systemwordbooktab book " + "left join p_systemdlltab dll on book.tab=dll.dllcoid " + "left join syscolumns c on c.id=OBJECT_ID(dll.sqldt1) and c.name=book.fieldname " + "left join P_systemGridConfigTab gcfg on '%s'||cast(dll.formkey as varchar(100))=cast(gcfg.formkey as varchar(100)) and book.fieldName=gcfg.fieldname and gcfg.operatorid='%s' " + "where 1=1 and (nvl(book.privilegeView,'')='' or instr(','||(SELECT wm_concat(userList) FROM p_systemPrivilege pri " + "WHERE book.tab=pri.modid and instr(','||cast(pri.privTypeId as varchar(10))||',',','||book.privilegeView||',')>0)||',',',%s,')>0) ", // 注意：这里移除了末尾的括号，留空给后续条件
                disabledPart, disabled2Part, scanAblePart, userName, widthPart, CusGridColumnPrefix.BaseMainGridView, userId, userName);

        // 添加模块ID或ID条件（核心修改：将条件和排序移到子查询内）
        if (!isNullOrEmpty(moduleId)) {
            // 把book.tab条件和order by加到子查询的where后
            colsql += String.format("and book.tab='%s' order by book.orderId ", moduleId);
        } else if (id > 0) {
            // 把book.id条件加到子查询的where后
            colsql += String.format("and book.id=%d ", id);
        }

        // 闭合子查询，添加rownum限制（外层只处理rownum）
        colsql += ") where rownum <= 1000";

        return colsql;
    }

    public String GetRightMenuRows(String fromkey, int menutype, String username, int menuid, boolean isWindowsDirver) {
        String baseSql = "select orderid, id, privilegeoper, dllname as library, action, " + "dllpar1 as param1, dllpar2 as param2, dllpar3 as param3, dllpar4 as param4, " + "dllpar5 as param5, dllpar6 as param6, dllpar7 as param7, dllpar8 as param8, " + "dllpar9 as param9, dllpar10 as param10, maxwindow, menuname as menucaption, " + "menucond, actiontype, beforemsg, ifrefresh as refresh, DBClickEvent as dbclick, " + "ifMoreClick as multi, mergeExec as [merge], showtoolbar as toBar, showMode, isCopy, " + "beforeTab, isnull(isStartRun, 0) as notRec, defailtImage as icon, " + "isnull(disabletype, 0) as disabletype " +
//                "--isnull(countSql,'') countSql,isMrpClickBtn \\n "
                ",isnull(hintMsg,'') desp,isnull(countSql,'') countSql,isMrpClickBtn from p_systempopupmenu where 1=1 %s %s " + "and (isnull(privilegeoper, '') = '' or charindex(',%s,', ',' + privilegeoper + ',') > 0) " + "order by orderid asc";

        if (menuid > 0) {
            // 构建查询条件，使用%s作为占位符
            String sql = String.format(baseSql, "and id='" + menuid + "'",  // 第一个%s占位符
                    "",                         // 第二个%s占位符
                    username                    // 第三个%s占位符
            );
            // 执行查询，传入fromkey参数
            return sql;
        } else {
            // 构建where条件
            StringBuilder whereCond = new StringBuilder("and tab=? ");

            if (menutype == 0) { // 右键菜单
                whereCond.append("AND ISNULL([menutype],0)=0 ");
            } else if (menutype == 1) { // 常用功能
                whereCond.append("AND ISNULL([menutype],0)=1 ");
            }

            // 构建驱动条件
            String driverCondition = isWindowsDirver ? "and visible=0" : "and isnull(ShowMobile,0)=1";

            // 格式化SQL，替换%s占位符
            String sql = String.format(baseSql, whereCond.toString(),  // 第一个%s占位符
                    driverCondition,       // 第二个%s占位符
                    username               // 第三个%s占位符
            );
            return sql;
        }
    }

    @Override
    public List<Map<String, Object>> GetModuleIdFieldRow(String keyName, String key) {
        keyName = SqlSafetyGuard.requireAllowedIdentifier(keyName, "tab", "formkey");
        return dmCrmMapper.getModuleIdFieldRow(keyName, key, SystemEnums.ControlType.LabTreeType.getValue());
    }

    @Override
    public int GetAuditStepCount(String moduleId, boolean b) {
        return dmCrmMapper.getAuditStepCount(moduleId, b);
    }

    public List<Map<String, Object>> GetCondition(String fromkey, Integer id, Boolean windowsDirver) {
        return dmCrmMapper.GetCondition(fromkey, id, windowsDirver);
    }

    @Override
    public List<Map<String, Object>> GetBillDetailColumns(String moduleCode, String userId, String username, Integer id, boolean window) {
        return dmCrmMapper.GetBillDetailColumns(moduleCode, userId, username, id, window);
    }

    @Override
    public List<Map<String, Object>> GetBillMasterRows(String moduleId, String userName, Integer id, boolean window) {
        // return dmCrmMapper.GetBillMasterRows(moduleId, userName, id, window);
        // 1. 拼接SQL的动态部分（对应C#的{0}和{1}）
        String disabled2Part = window
                ? "a.isVisible"
                : "case when isnull(a.ShowMobile,0)=0 then 1 else 0 end";
        String controlWidthPart = window
                ? "a.ControlWidth"
                : "case when isnull(a.ControlWidth,0)=0 then 1 else a.ControlWidth end";

        // 2. 拼接基础SQL（对应C#的colsql初始值）
        String colsql = String.format(
                "select a.id,FieldName,ISNULL(userName,fieldname) FieldCaption,%s ControlWidth,ControlHeight,ControlTop,ControlLeft,%s disabled2, " +
                        "fieldtypeid FieldType,nullable,edited Edit,lookupSql fieldsql,lookupKeyField valuemember,lookupResult displaymember,calcExpr CalcExpress, " +
                        "0 CalcOrderId,unionValue UnionSQL,unionFields UnionField,4 FieldDataType,a.addmoduleid,a.doNotSpelling, '' dataformat, " +
                        "InputHintText HintText,a.FontSize fontSize,TitleColor FontColor,limitLen LimitMaxValue,defaultvalue, a.labelWidth, a.labelAlign, " +
                        "a.highlightFColor fcolor,a.highlightBColor bcolor,a.highlightBold bold " +
                        "from p_systembillInfo a left join p_systembilltype b on a.typeCode=b.typeCode",
                controlWidthPart, disabled2Part
        );

        // 3. 拼接WHERE条件和排序（严格对齐C#的if...else if逻辑）
        if (id > 0) {
            colsql = String.format("%s where a.id=%d", colsql, id);
        } else if (moduleId != null && !moduleId.isEmpty()) {
            // 注意：C#中直接拼接moduleId有SQL注入风险，Java中改为参数化（关键优化）
            colsql = String.format("%s where b.typecode=?  order by a.orderId", colsql);
        }

        // 4. 执行SQL并处理结果
        List<Map<String, Object>> resultList;
        if (colsql != null && !colsql.isEmpty()) {
            // 根据是否有moduleId参数，选择不同的执行方式（避免SQL注入）
            if (moduleId != null && !moduleId.isEmpty() && id <= 0) {
                // 有moduleId时，用参数化查询
                resultList = jdbcTemplate.queryForList(colsql, moduleId);
            } else {
                // 无参数或有id时，直接执行
                resultList = jdbcTemplate.queryForList(colsql);
            }
            // 对应C#的ToLowerColumnName()：将Map的key转为小写
            List<Map<String, Object>> lowerKeyList = new ArrayList<>();
            for (Map<String, Object> row : resultList) {
                Map<String, Object> lowerRow = new HashMap<>();
                for (Map.Entry<String, Object> entry : row.entrySet()) {
                    lowerRow.put(entry.getKey().toLowerCase(), entry.getValue());
                }
                lowerKeyList.add(lowerRow);
            }
            return lowerKeyList;
        }
        return null;
    }

    @Override
    public Map<String, Object> GetBillModule(String moduleCode, String menuId) {
        return dmCrmMapper.GetBillModule(moduleCode, menuId);
    }

    @Override
    public List<Map<String, Object>> GetControlRows(Object fromkey, String userName, String moduleId, Integer fieldId) {
        return dmCrmMapper.GetControlRows(fromkey, userName, moduleId, fieldId);
    }

    @Override
    public List<Map<String, Object>> selectSystemPopupMenuById(int menuid, String fromkey, String username) {
        return dmCrmMapper.selectSystemPopupMenuById(menuid, fromkey, username);
    }

    @Override
    public List<Map<String, Object>> selectSystemPopupMenuByType(int menutype, String fromkey, String username, boolean windowsDirver) {
        return dmCrmMapper.selectSystemPopupMenuByType(menutype, fromkey, username, windowsDirver);
    }

    @Override
    public List<Map<String, Object>> GetBillSource(String moduleCode, String stCondition) {
        return dmCrmMapper.getBillSource(moduleCode, SqlSafetyGuard.requireSafeConditionFragment(stCondition));
    }

    @Override
    public List<Map<String, Object>> GetBillSourceColumns(String sourceId, String userId, String billSourceGridView) {
        return dmCrmMapper.getBillSourceColumns(sourceId, userId, billSourceGridView);
    }

    @Override
    public List<Map<String, Object>> GetBillSourceDetailColumns(String sourceId, String userId, String billSourceGridView) {
        return dmCrmMapper.getBillSourceDetailColumns(sourceId, userId, billSourceGridView);
    }

    @Override
    public List<Map<String, Object>> GetBaseModule(String moduleCode, String menuId) {
        // return dmCrmMapper.GetBaseModule(moduleCode, menuId);
        // 1. 动态拼接p_formmenuconfigtab m的关联条件（核心逻辑对齐）
        String menuCond;
        List<Object> params = new ArrayList<>(); // 存储参数，适配达梦参数化查询
        if (menuId == null || menuId.isEmpty() || "0".equals(menuId)) {
            menuCond = "dll.DllCoid=m.UrlParams";
        } else {
            menuCond = "dll.DllCoid=m.UrlParams and m.MenuId=?"; // 达梦支持?占位符
            params.add(menuId); // 菜单ID参数（达梦字符串参数无需额外转换）
        }

        // 2. 拼接完整SQL（适配达梦语法，替换兼容函数/转义符）
        String sql = String.format(
                "select NVL(dll.bs_adddllname,'') adddllname ,NVL(dll.CsHasDefultSearch,0) defaultSearch," +
                        "a.column_prefix MenuPrefix,dll.formkey fromkey,condkey, " +
                        "case when NVL(dll.displayRows,0)=-1 then 0 else 1 end pagerflag,selfedit EditFlag," +
                        "NVL(m.MenuCaption,dll.ToolsName) menuname,dll.ToolsName,SQLDT1 TableName,dll.BSRowHeight rowHeight,bottomHeight, " +
                        "\"SQL\" TableSQL,DllCoid MenuCode,dlltype MenuType,dirId,bmpSpec FileSpeciesNo,dll.bmpType ftype," +
                        "NVL(isReport,'0') ReportFlag,AddCond,modifyCond UpdateCond,DeleteCond,ExportCond, dll.addmodid," +
                        "case when NVL(dll.displayRows,0)=-1 then 100000 else dll.displayRows end pagesize," +
                        "popupWidth winWidth,popupHeight winHeight, NVL(addenable,'0') AddFlag,NVL(modifyEnable,'0') SaveFlag," +
                        "NVL(deleteEnable,'0') DeleteFlag,NVL(importEnable,'0') ImportFlag,NVL(exportEnable,'0') ExportFlag," +
                        "NVL(searchEnable,'0') searchable,BackSelected,dll.tasksql,dll.countSql,dll.selectLeaf," +
                        "case when dll.multcheck=1 then 1 else dll.gridobjcheck end multcheck,dll.DisMobileCard,dll.appAutoSave, " +
                        "addCopyEnabled addCopy,dll.printflag appPrint,dll.printertype appPrintType,PrintFile,PrintType,PrintSQL," +
                        "PrintSQL1 PrintSQL2,PrintSQL2 PrintSQL3, LeftWidth,cp.parentWidth pWidth,cp.parentHeight pHeight," +
                        "cardwidth PopupWidth,m.MenuId menuid,overbacksql,overbackkey,overbackcond,OverBackOper," +
                        "case when NVL(m.dllfilename1,'')='' then m.DllFileName else m.dllfilename1 end dllfilename, " +
                        "dll.saveCaption,dll.addCaption,dll.modifyCaption,dll.delCaption,dll.applyCaption,dll.newver,dll.newWFVer," +
                        "dll.addHintMSG addhint,dta.attachType,dta.attachIMG,dll.detailPageAlign,dll.editType,dll.cellTplflag," +
                        "dll.attatchModifyCond,dll.attatchInfoWidth attInfow ,dll.bmpSpecIsMJ,dll.noGridLine,dll.noRownumber," +
                        "dll.noColumnHeader hideColumnHeader, NVL(dll.closeAfterModify,1) closeAfterModify," +
                        "NVL(dll.closeAfterAdd,1) closeAfterAdd,NVL(dll.scanMode,0) scanMode,dll.scanReadOnly," +
                        "dll.barSplitChar scanChar,dll.barSplitFields scanFields,dll.verifyCancel scanNoRepet,dll.verifyUnique scanUnique," +
                        "NVL(dll.MuitlAuditFlag,0) muitlAudit,NVL(dll.disAppFastAudit,0) disAppFastAudit," +
                        "NVL(dll.disableDetail,0) disableDetail,dll.attcField1,dll.attcField2,dll.attcField3, " +
                        "dll.apiDataNode,dll.apiSuccNode,dll.apiSuccVal,NVL(dll.hintMsg,'') desp," +
                        "NVL(dll.noDataMsg,'') noDataMsg,dll.RefreshTheInterface refreshall " +
                        "from p_systemdlltab dll " +
                        "left join p_formmenuconfigtab m on %s " + // 动态关联条件
                        "left join (select table_name,column_prefix from p_systemtables group by table_name, column_prefix) a on dll.SQLDT1=a.table_name " +
                        "left join p_systemControlParent cp on cp.formkey=dll.formkey " +
                        "left join p_SystemdllTabAttach dta on ?=dta.unionModule " + // moduleCode参数化
                        "where dll.DllCoid=? or CAST(dll.formkey AS VARCHAR(50))=?", // 达梦推荐CAST替代CONVERT
                menuCond // 填充动态关联条件
        );

        // 3. 添加moduleCode参数（达梦参数顺序：menuId → moduleCode → moduleCode）
        params.add(moduleCode); // 对应dta.unionModule=?
        params.add(moduleCode); // 对应dll.DllCoid=?
        params.add(moduleCode); // 对应CAST(dll.formkey AS VARCHAR(50))=?

        // 4. 执行查询（达梦JdbcTemplate兼容）
        List<Map<String, Object>> resultList = jdbcTemplate.queryForList(sql, params.toArray());

        // 5. 字段名转小写（对齐C#的ToLowerColumnName()习惯）
        List<Map<String, Object>> lowerKeyList = new ArrayList<>();
        for (Map<String, Object> row : resultList) {
            Map<String, Object> lowerRow = new HashMap<>();
            for (Map.Entry<String, Object> entry : row.entrySet()) {
                lowerRow.put(entry.getKey().toLowerCase(), entry.getValue());
            }
            lowerKeyList.add(lowerRow);
        }

        return lowerKeyList;
    }

    @Override
    public String GetAuditMsgTab(String userId) {
        // 拼接用户条件（注意达梦字符串拼接用||，但此处是Java拼接SQL，不影响）
        String userCond1 = (userId == null || userId.isEmpty()) ? "" : String.format("AND a.UserId = '%s'", userId);
        String userCond2 = (userId == null || userId.isEmpty()) ? "" : String.format("AND NoticeUserID = '%s'", userId);

// 构建适配达梦的完整SQL（替换LIMIT为达梦支持的FETCH FIRST）
        String sql = String.format("""
                SELECT * FROM (
                    SELECT * FROM (
                        SELECT DISTINCT 
                            '['||b.MenuCaption||']'||'=> '||COALESCE(a.billdocument_id,'')||msg AS AuditMessages,
                            a.UserId, 
                            a.Created AS OperateDate,
                            a.id,
                            a.messid,
                            a.DLLCoid moduleid,
                            Cnt1 num, 
                            CASE WHEN COALESCE(a.DllFileName,'')<>'' THEN a.DllFileName 
                                 WHEN COALESCE(b.dllfilename1,'')='' THEN b.DllFileName  
                                 ELSE b.dllfilename1 
                            END dllname,
                            b.MenuCaption modulename,
                            billdocument_id keyvalue,
                            stepcode,
                            COALESCE(MenuMode_Mobile,0) menumode,
                            b.menuid,
                            CASE WHEN COALESCE(c.DllCoid,'')='' 
                                 THEN COALESCE(d.defaultShowSearch,0)
                                 ELSE COALESCE(c.defaultShowSearch,0) 
                            END hasSearch,
                            1 tablecode,
                            a.templetename tplname
                        FROM p_systemNotification a
                        INNER JOIN P_FormMenuConfigTab b ON COALESCE(a.menuid,0)=b.MenuId  
                        LEFT JOIN p_systemdlltab c ON b.UrlParams = c.DllCoid
                        LEFT JOIN p_systembilltype d ON b.UrlParams = d.typeCode 
                        WHERE COALESCE(a.billdocument_id,'')<>'' 
                          AND COALESCE(a.status,0)=0 
                          AND COALESCE(a.Cnt1,0)<>0 
                          %s 
                        ORDER BY a.Created DESC
                        FETCH FIRST 100 ROWS ONLY  -- 达梦替换LIMIT 100
                    ) t1
                    UNION ALL
                    SELECT * FROM (
                        SELECT 
                            Msg, 
                            NoticeUserID, 
                            CreateDate, 
                            id,
                            1 AS messid,  -- 补充别名，与t1字段对齐
                            DllCoid,
                            0 AS num,     -- 补充别名，与t1字段对齐
                            '' AS dllname,-- 补充别名，与t1字段对齐
                            Title AS modulename,
                            MsgDetail AS keyvalue,
                            0 AS stepcode,
                            0 AS menumode,
                            0 AS menuid,
                            0 AS hasSearch,
                            2 AS tablecode,
                            templetename AS tplname
                        FROM p_systemMessageTab 
                        WHERE DeleteFlag=0 
                          %s  
                        ORDER BY CreateDate DESC
                        FETCH FIRST 100 ROWS ONLY  -- 达梦替换LIMIT 100
                    ) t2
                ) cc 
                ORDER BY OperateDate DESC
                FETCH FIRST 100 ROWS ONLY;  -- 达梦替换LIMIT 100
                """, userCond1, userCond2);
        return sql;
    }

    @Override
    public String GetDeskTopCommonUse(int cardId, String userId) {
        // 适配达梦的SQL拼接逻辑
        String sql;
        if (cardId < 0) {
            if (cardId == -99) {
                // cardId=-99的适配版本
                sql = "SELECT pmt.objdll dllfilename," + "CASE WHEN INSTR(pmt.ObjDll, 'www') > 0 THEN pmt.objdll ELSE '#' END href," + "pmt.id, pmt.DllShowCaption text,pmt.LMenuid menuid,pmt.Lsubsysid subsysid, " + "pmt.dllcoid dllcoid,'' dlltype, 0 needcount ,0 serverId " + "FROM P_MessageToolLinkDllTab pmt " + "WHERE pmt.cardId='-99' AND ISNULL(objdll,'')<>'' AND ISNULL(dllcoid,'')<>'' ";
            } else {
                // cardId<0且≠-99的适配版本
                sql = String.format("SELECT * FROM (                                " + "SELECT DISTINCT pfmc.serverId,  " + "CASE WHEN ISNULL(pfmc.dllfilename1,'')='' THEN pfmc.dllfilename ELSE pfmc.dllfilename1 END dllfilename," + "CASE WHEN INSTR(pmt.ObjDll, 'www') > 0 THEN pmt.objdll ELSE '#' END href, " + "pmt.DllShowCaption text,pmt.LMenuid menuid,pmt.Lsubsysid subsysid, " + "ISNULL(pfmc.UrlParams,ISNULL(psdt.dllcoid,psdtp.typeCode)) dllcoid,psdt.dlltype, " + "CASE WHEN ISNULL(CAST(psdt.countsql AS VARCHAR(1000)),ISNULL(CAST(psdtp.countsql AS VARCHAR(1000)),''))='' THEN 0 ELSE 1 END needcount " + "FROM P_MessageToolLinkDllTab pmt " + "--inner join tmenu lm on lm.menuid=pmt.lmenuid " + "INNER JOIN p_formmenuconfigtab pfmc ON pmt.Lmenuid=pfmc.menuid " + "LEFT JOIN p_systemdlltab psdt ON pfmc.urlparams=psdt.dllcoid AND ISNULL(pfmc.UrlParams,'') !='' " + "LEFT JOIN p_systembilltype psdtp ON pfmc.urlparams=psdtp.typeCode AND ISNULL(pfmc.UrlParams,'') !='' " + "WHERE EmployeeID='%s' AND pmt.cardId='%d' AND (ISNULL(pfmc.targetmode,0)=0 OR pfmc.targetmode=3) " + ") a WHERE ISNULL(dllfilename,'')<>'' AND ISNULL(dllcoid,'')<>'' ", userId, cardId);
            }
        } else {
            // cardId≥0的适配版本（含CTE）
            sql = String.format("WITH tmenu(id,MenuCaption,ParentId,menuid,SubSysId,level) " + "AS " + "( " + "    SELECT CAST(menu.SubSysId AS VARCHAR(50))||'_'|| MenuStruct id," + "    MenuCaption," + "    CAST(-CAST(menu.SubSysId AS INT) AS VARCHAR(50)) ParentId," + "    menu.menuid,menu.SubSysId,1 level " + "    FROM p_formmenuconfigtab menu " + "    INNER JOIN P_SubSystemTab sub ON menu.SubSysId=sub.SubSysId AND ISNULL(UseEd,0)=1 AND ISNULL(visible,0)=0 " + "    WHERE LENGTH(menustruct)=2 AND ISNULL(menu.SeriesId, 1)=1 AND ISNULL(menu.UseFlag,1)=1 " + "    UNION ALL  " + "    SELECT CAST(a.SubSysId AS VARCHAR(50))||'_'||A.MenuStruct id, " + "    A.MenuCaption, " + "    CAST(a.SubSysId AS VARCHAR(50))||'_'|| SUBSTR(a.MenuStruct,1,CASE WHEN LENGTH(a.MenuStruct)>2 THEN LENGTH(a.MenuStruct)-2 ELSE 0 END) ParentId," + "    a.MenuId,a.SubSysId ,b.level+1 " + "    FROM p_formmenuconfigtab A,tmenu b   " + "    WHERE LENGTH(a.MenuStruct)>2 AND ISNULL(UseFlag,1)=1 " + "    AND CAST(a.SubSysId AS VARCHAR(50))||'_'|| SUBSTR(a.MenuStruct,1,CASE WHEN LENGTH(a.MenuStruct)>2 THEN LENGTH(a.MenuStruct)-2 ELSE 0 END) = b.id " + "    AND a.SubSysId=b.SubSysId " + ") " + "SELECT * FROM ( " + "    SELECT pfmc.serverId, " + "    CASE WHEN ISNULL(pfmc.dllfilename1,'')='' THEN pfmc.dllfilename ELSE pfmc.dllfilename1 END dllfilename," + "    CASE WHEN INSTR(pmt.ObjDll, 'www')>0 THEN pmt.objdll ELSE '#' END href,pmt.id, " + "    pmt.DllShowCaption text,pmt.LMenuid menuid,pmt.Lsubsysid subsysid, " + "    ISNULL(pfmc.UrlParams,ISNULL(psdt.dllcoid,psdtp.typeCode)) dllcoid,psdt.dlltype, " + "    CASE WHEN ISNULL(CAST(psdt.countsql AS VARCHAR(1000)),ISNULL(CAST(psdtp.countsql AS VARCHAR(1000)),''))='' THEN 0 ELSE 1 END needcount " + "    FROM P_MessageToolLinkDllTab pmt " + "    INNER JOIN tmenu lm ON lm.menuid=pmt.lmenuid " + "    INNER JOIN p_formmenuconfigtab pfmc ON pmt.Lmenuid=pfmc.menuid " + "    LEFT JOIN p_systemdlltab psdt ON pfmc.urlparams=psdt.dllcoid AND ISNULL(pfmc.UrlParams,'') !='' " + "    LEFT JOIN p_systembilltype psdtp ON pfmc.urlparams=psdtp.typeCode AND ISNULL(pfmc.UrlParams,'') !='' " + "    WHERE EmployeeID='%s' AND pmt.cardId='%d' AND (ISNULL(pfmc.targetmode,0)=0 OR pfmc.targetmode=3) " + ") a WHERE ISNULL(dllfilename,'')<>'' AND ISNULL(dllcoid,'')<>'' ", userId, cardId);
        }
        return sql;
    }

    @Override
    public String GetDeskQueryResult(String queryText, boolean exitTable) {
        // 步骤1：先判断p_systemSearchTextTab表是否存在（达梦语法）
        String checkTableSql = "SELECT COUNT(1) FROM ALL_TABLES WHERE TABLE_NAME = 'P_SYSTEMSEARCHTEXTTAB'";
        // 步骤2：根据表存在性拼接对应SQL
        String sql;
        if (exitTable) {
            // 表存在的适配SQL
            sql = "WITH tmenu(id,ParentId,menuid,SubSysId,level) " + "AS " + "( " + "    SELECT CAST(menu.SubSysId AS VARCHAR(50))||'_'|| MenuStruct id," + "    CAST(-CAST(menu.SubSysId AS INT) AS VARCHAR(50)) ParentId," + "    menu.menuid,menu.SubSysId,1 level " + "    FROM p_formmenuconfigtab menu " + "    INNER JOIN P_SubSystemTab sub ON menu.SubSysId=sub.SubSysId AND ISNULL(UseEd,0)=1 AND ISNULL(visible,0)=0 " + "    WHERE ISNULL(UseFlag,1)=1 AND LENGTH(menustruct)=2 " + "    UNION ALL  " + "    SELECT CAST(a.SubSysId AS VARCHAR(50))||'_'||A.MenuStruct id," + "    CAST(a.SubSysId AS VARCHAR(50))||'_'|| SUBSTR(a.MenuStruct,1,CASE WHEN LENGTH(a.MenuStruct)>2 THEN LENGTH(a.MenuStruct)-2 ELSE 0 END) ParentId," + "    a.MenuId,a.SubSysId ,b.level+1 " + "    FROM p_formmenuconfigtab A,tmenu b   " + "    WHERE LENGTH(a.MenuStruct)>2 AND ISNULL(UseFlag,1)=1 " + "    AND CAST(a.SubSysId AS VARCHAR(50))||'_'|| SUBSTR(a.MenuStruct,1,CASE WHEN LENGTH(a.MenuStruct)>2 THEN LENGTH(a.MenuStruct)-2 ELSE 0 END) = b.id " + "    AND a.SubSysId=b.SubSysId " + ") " + "SELECT pm.menuid id,'SYSTEM_MENU' keyvalue, " + "urlparams src_modid,menuCaption src_modtitle,urlparams target_modid,menuCaption target_modtitle," + "CASE WHEN ISNULL(dllfilename1,'')='' THEN DllFileName ELSE dllfilename1 END target_dllname," + "'MENU' key_fieldname,'功能模块' key_fieldcnname, menuCaption key_context," + "SYSDATE operatedate,'管理员' operatorname " + "FROM p_formmenuconfigtab pm " + "INNER JOIN tmenu tm ON pm.menuid=tm.menuid " + "INNER JOIN P_SubSystemTab s ON pm.subsysid=s.subsysid AND s.useed=1 AND ISNULL(s.visible,0)=0 " + "INNER JOIN v_systemdlltab v ON pm.urlparams=v.dllcoid " + "WHERE ISNULL(pm.urlparams,'')<>'' AND (ISNULL(pm.dllfilename1,'')<>'' OR ISNULL(pm.dllfilename,'')<>'') " + "AND (ISNULL(pm.targetmode,0)=0 OR pm.targetmode=3) AND ISNULL(pm.useFlag,1)=1 " + "AND (pm.urlparams LIKE ('%'||?||'%') OR pm.menuCaption LIKE ('%'||?||'%') OR P_GetPy(pm.menuCaption) LIKE ('%'||?||'%')) " + "UNION ALL " + "SELECT * FROM ( " + "    SELECT * FROM p_systemSearchTextTab  WHERE keyvalue='SYSTEM_MENU' " + "    AND ( key_context LIKE ('%'||?||'%') OR P_GetPy(key_context) LIKE ('%'||?||'%') " + "    OR target_modid LIKE ('%'||?||'%')  OR src_modid LIKE ('%'||?||'%') ) " + "    ORDER BY 1 " + // 需指定排序字段（如id），达梦FETCH FIRST必须配合ORDER BY
                    "    FETCH FIRST 50 ROWS ONLY " + // 替换TOP 50
                    ") t";
        } else {
            // 表不存在的适配SQL
            sql = "WITH tmenu(id,ParentId,menuid,SubSysId,level) " + "AS " + "( " + "    SELECT CAST(menu.SubSysId AS VARCHAR(50))||'_'|| MenuStruct id," + "    CAST(-CAST(menu.SubSysId AS INT) AS VARCHAR(50)) ParentId," + "    menu.menuid,menu.SubSysId,1 level " + "    FROM p_formmenuconfigtab menu " + "    INNER JOIN P_SubSystemTab sub ON menu.SubSysId=sub.SubSysId AND ISNULL(UseEd,0)=1 AND ISNULL(visible,0)=0 " + "    WHERE ISNULL(UseFlag,1)=1 AND LENGTH(menustruct)=2 " + "    UNION ALL  " + "    SELECT CAST(a.SubSysId AS VARCHAR(50))||'_'||A.MenuStruct id," + "    CAST(a.SubSysId AS VARCHAR(50))||'_'|| SUBSTR(a.MenuStruct,1,CASE WHEN LENGTH(a.MenuStruct)>2 THEN LENGTH(a.MenuStruct)-2 ELSE 0 END) ParentId," + "    a.MenuId,a.SubSysId ,b.level+1 " + "    FROM p_formmenuconfigtab A,tmenu b   " + "    WHERE LENGTH(a.MenuStruct)>2 AND ISNULL(UseFlag,1)=1 " + "    AND CAST(a.SubSysId AS VARCHAR(50))||'_'|| SUBSTR(a.MenuStruct,1,CASE WHEN LENGTH(a.MenuStruct)>2 THEN LENGTH(a.MenuStruct)-2 ELSE 0 END) = b.id " + "    AND a.SubSysId=b.SubSysId " + ") " + "SELECT pm.menuid id,'SYSTEM_MENU' keyvalue, " + "urlparams src_modid,menuCaption src_modtitle,urlparams target_modid,menuCaption target_modtitle," + "CASE WHEN ISNULL(dllfilename1,'')='' THEN DllFileName ELSE dllfilename1 END target_dllname," + "'MENU' key_fieldname,'功能模块' key_fieldcnname, menuCaption key_context," + "SYSDATE operatedate,'管理员' operatorname " + "FROM p_formmenuconfigtab pm " + "INNER JOIN tmenu tm ON pm.menuid=tm.menuid " + "INNER JOIN P_SubSystemTab s ON pm.subsysid=s.subsysid AND s.useed=1 AND ISNULL(s.visible,0)=0 " + "WHERE ISNULL(pm.urlparams,'')<>'' AND (ISNULL(pm.dllfilename1,'')<>'' OR ISNULL(pm.dllfilename,'')<>'') " + "AND (ISNULL(pm.targetmode,0)=0 OR pm.targetmode=3) AND ISNULL(pm.useFlag,1)=1 " + "AND (pm.urlparams LIKE ('%'||?||'%') OR pm.menuCaption LIKE ('%'||?||'%') OR P_GetPy(pm.menuCaption) LIKE ('%'||?||'%'))";
        }
        return sql;
    }

    @Override
    public String GetDesktopModuleMain(String userId, String userName, String dllcoid) {
        String sql = "SELECT " + "a.id, " + "a.dllcoid, " + "b.itemCode, " + "ISNULL(a.itemRowNo, b.itemRowNo) itemRowNo, " + "ISNULL(a.itemOrder, b.itemOrder) itemOrder, " + "ISNULL(a.itemWidth, b.itemWidth) itemWidth, " + "ISNULL(a.itemHeight, b.itemHeight) itemHeight, " + "a.itemLeft, " + "a.itemTop, " + "CASE WHEN ISNULL(a.deleted, 0) = 1 THEN 1 ELSE ISNULL(a.enableFlag, 0) END enableFlag, " + "b.itemTitle, " + "b.itemTypeFull, " + "a.queryField, " + "a.condition " + "FROM P_SystemFirstPageSetTab b " + "LEFT JOIN P_SystemDllFirstPageTab a ON a.itemCode = b.itemCode AND a.dllcoid = ? " + "WHERE b.enableType IN (-1, 2) " + "AND (ISNULL(b.itemPrivilege, '') = '' OR INSTR(',' || b.itemPrivilege || ',', ',' || ? || ',') > 0)";
        return sql;
    }

    @Override
    public String CheckIsAudit() {
        return "SELECT DATA_LENGTH AS col_length" + "FROM ALL_TAB_COLUMNS" + "WHERE " + "    TABLE_NAME = 'P_SYSTEMCHECKTAB' " + "    AND COLUMN_NAME = 'STEOVER';";
    }

    @Override
    public String GetProSysType() {
        return "SELECT CASE WHEN EXISTS (SELECT 1 FROM SYSOBJECTS WHERE ID = OBJECT_ID('P_SYSTEMPRODUCTSERIESTAB') AND TYPE$ = 'U') THEN 1 ELSE 0 END AS TABLE_EXISTS";
    }

    @Override
    public String GetPrimaryKeysArray(String tabname) {
        String primaryKeysSql = String.format("SELECT C.COLUMN_NAME " + "FROM ALL_CONSTRAINTS T " + "JOIN ALL_CONS_COLUMNS C  ON T.CONSTRAINT_NAME = C.CONSTRAINT_NAME " + "WHERE " + "    T.TABLE_NAME = UPPER('%s') " + "    --AND T.CONSTRAINT_TYPE = 'U' " + "ORDER BY C.POSITION; ",       // P = PRIMARY KEY
                tabname);
        return primaryKeysSql;
    }

    @Override
    public String GetUsers(String name, String code, int userId) {
        // 替换原 sysobjects 判断逻辑为达梦兼容版本
        name = name.replace("'", "''");
        code = code.replace("'", "''");
        String sql = "DECLARE " + "    v_name    VARCHAR(100) := '" + name + "';  -- 替换为实际值：管理员 " + "    v_code    VARCHAR(100) := '" + code + "';       -- 替换为实际值：0 " + "    v_userId  VARCHAR(10)  := '" + userId + "';     -- 原userId=001为字符串型，调整类型避免隐式转换 " + "    v_sql     VARCHAR(4000); " + "BEGIN " + "    -- 检查视图是否存在（当前用户下） " + "    IF  NOT EXISTS ( " + "        SELECT 1  " + "        FROM ALL_VIEWS  " + "        WHERE OWNER = SYS_CONTEXT('USERENV','CURRENT_SCHEMA')  " + "          AND VIEW_NAME = 'P_EMPLOYEEBASEVIEW' " + "    ) THEN " + "        -- 1. 视图不存在：创建静态视图（仅保留固定筛选条件） " + "        v_sql := 'CREATE VIEW P_employeeBaseView AS  " + "                  SELECT employeeid \"员工ID\",  " + "                         loginaccount \"员工工号\",  " + "                         employeename \"员工姓名\",  " + "                         departmentname \"所属部门\",  " + "                         speciesname \"所属类别\"  " + "                  FROM p_employeetab a  " + "                  LEFT JOIN P_DepartmentTab b ON a.departmentid = b.departmentid  " + "                  LEFT JOIN P_EmployeeSpecTab c ON a.speciesno = c.speciesno  " + "                  WHERE NVL(a.sign,0)=0 AND NVL(useflag,0)=1'; " + "        EXECUTE IMMEDIATE v_sql; " + "         " + "        -- 创建后拼接动态查询条件 " + "        v_sql := 'SELECT * FROM P_employeeBaseView WHERE 1=1  " + "                  AND (\"员工姓名\" LIKE ''%' || v_name || '%'' OR P_GetPy(\"员工姓名\") LIKE ''%' || v_name || '%'')  " + "                  AND \"员工工号\" LIKE ''%' || v_code || '%''  " + "                  AND (''' || v_userId || ''' = ''0'' OR \"员工ID\" = ''' || v_userId || ''')'; " + "    ELSE " + "        -- 2. 视图已存在：直接拼接动态查询条件 " + "        v_sql := 'SELECT * FROM P_employeeBaseView WHERE 1=1  " + "                  AND (\"员工姓名\" LIKE ''%' || v_name || '%'' OR P_GetPy(\"员工姓名\") LIKE ''%' || v_name || '%'')  " + "                  AND \"员工工号\" LIKE ''%' || v_code || '%''  " + "                  AND (''' || v_userId || ''' = ''0'' OR \"员工ID\" = ''' || v_userId || ''')'; " + "    END IF; " + "     " + "    -- 执行最终查询（注：若需返回结果，需结合游标/存储过程输出参数） " + "    EXECUTE IMMEDIATE v_sql; " + "END;";
        return sql;
    }

    @Override
    public String hasExistsTable(String tableName) {
        return "SELECT COUNT(1) FROM DBA_TABLES " + "WHERE TABLE_NAME = '" + tableName + "'";
    }

    @Override
    public String SeeOneAuditMsg() {
        return "-- 声明变量（达梦PL/SQL规范） " + "DECLARE " + "    v_type VARCHAR(200);          -- 存储列类型 " + "    v_constraintName VARCHAR(200);-- 存储默认约束名 " + "    v_dropSql VARCHAR(500);       -- 存储删除约束的动态SQL " + "    v_owner VARCHAR(100);         -- 表所属用户（模式名） " + "    v_search_condition CLOB;      -- 存储约束条件（适配低版本达梦的CLOB类型） " + "    v_insert_sql VARCHAR(1000);   -- 动态插入SQL（解决模式名拼接问题） " + "BEGIN " + "    -- 获取当前用户（模式名） " + "    SELECT SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') INTO v_owner FROM DUAL; " + " " + "    -- 1. 校验表和列是否存在（避免后续查询无意义） " + "    DECLARE " + "        v_col_exists INT := 0; " + "    BEGIN " + "        SELECT 1 INTO v_col_exists " + "        FROM USER_TAB_COLUMNS " + "        WHERE TABLE_NAME = 'P_SYSTEMNOTICESHEET' " + "          AND COLUMN_NAME = 'CREATED'; " + "    EXCEPTION " + "        WHEN NO_DATA_FOUND THEN " + "            RAISE_APPLICATION_ERROR(-20001, '表P_SYSTEMNOTICESHEET或列CREATED不存在！'); " + "    END; " + " " + "    -- 2. 查询Created列的类型 " + "    SELECT DATA_TYPE INTO v_type " + "    FROM USER_TAB_COLUMNS " + "    WHERE TABLE_NAME = 'P_SYSTEMNOTICESHEET' " + "      AND COLUMN_NAME = 'CREATED'; " + " " + "    -- 3. 若类型不是datetime，修改列类型（先删默认约束） " + "    IF v_type <> 'DATETIME' THEN " + "        -- 3.1 查询Created列的默认约束名（适配所有达梦版本） " + "        BEGIN " + "            SELECT a.CONSTRAINT_NAME, a.SEARCH_CONDITION " + "            INTO v_constraintName, v_search_condition " + "            FROM USER_CONSTRAINTS a " + "            INNER JOIN USER_CONS_COLUMNS b " + "                ON a.OWNER = b.OWNER  " + "                AND a.CONSTRAINT_NAME = b.CONSTRAINT_NAME " + "            WHERE a.OWNER = v_owner " + "              AND a.TABLE_NAME = 'P_SYSTEMNOTICESHEET' " + "              AND b.COLUMN_NAME = 'CREATED' " + "              AND a.CONSTRAINT_TYPE = 'C'; -- 达梦默认约束归类为CHECK约束 " + "             " + "            -- 筛选包含DEFAULT的默认约束（适配CLOB类型的模糊查询） " + "            IF v_search_condition NOT LIKE '%DEFAULT%' THEN " + "                v_constraintName := NULL; " + "            END IF; " + "        EXCEPTION " + "            WHEN NO_DATA_FOUND THEN " + "                v_constraintName := NULL; -- 无默认约束时置空 " + "        END; " + " " + "        -- 3.2 删除默认约束（动态SQL） " + "        IF v_constraintName IS NOT NULL AND TRIM(v_constraintName) <> '' THEN " + "            v_dropSql := 'ALTER TABLE \"' || v_owner || '\".\"P_SYSTEMNOTICESHEET\" DROP CONSTRAINT \"' || v_constraintName || '\"'; " + "            EXECUTE IMMEDIATE v_dropSql; " + "        END IF; " + " " + "        -- 3.3 修改列类型为DATETIME（动态SQL避免模式名问题） " + "        v_dropSql := 'ALTER TABLE \"' || v_owner || '\".\"P_SYSTEMNOTICESHEET\" ALTER COLUMN \"CREATED\" DATETIME NULL'; " + "        EXECUTE IMMEDIATE v_dropSql; " + "    END IF; " + " " + "    -- 4. 插入数据：留出参数占位符（外部格式化时替换{0}/{1}/{2}） " + "    v_insert_sql := 'INSERT INTO \"' || v_owner || '\".\"P_SYSTEMNOTICESHEET\" (MSG, CREATED, USERID) " + "                     VALUES (''{0}'', ''{1}'', ''{2}'')'; -- 占位符标记 " + "    EXECUTE IMMEDIATE v_insert_sql; " + " " + "    COMMIT; -- 提交事务 " + "EXCEPTION " + "    WHEN OTHERS THEN " + "        DBMS_OUTPUT.PUT_LINE('执行失败：' || SQLERRM || '（错误码：' || SQLCODE || '）'); " + "        ROLLBACK; -- 异常回滚 " + "END; " + "/";
    }

    //    endregion

//    region  UpdateImpl

    @Override
    public String updateSystemOtherTabSql() {

        StringBuilder sqlBuilder = new StringBuilder();

        sqlBuilder.append(String.format("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = '%s' AND COLUMN_NAME = 'SUMCOND' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');" + "IF v_col_exists = 0 THEN EXECUTE IMMEDIATE 'ALTER TABLE %s ADD sumCond VARCHAR(500)';END IF;END;", "p_systemdlltabdetailgrid", "p_systemdlltabdetailgrid"));

        sqlBuilder.append(String.format("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = '%s' AND COLUMN_NAME = 'SUMCOND' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');" + "IF v_col_exists = 0 THEN EXECUTE IMMEDIATE 'ALTER TABLE %s ADD sumCond VARCHAR(500)';END IF;END;", "p_systembilldetail", "p_systembilldetail"));

        sqlBuilder.append(String.format("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = '%s' AND COLUMN_NAME = 'SUMCOND' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');" + "IF v_col_exists = 0 THEN EXECUTE IMMEDIATE 'ALTER TABLE %s ADD sumCond VARCHAR(500)';END IF;END;", "p_systembillauditAttachDetail", "p_systembillauditAttachDetail"));

        sqlBuilder.append(String.format("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS" + " WHERE TABLE_NAME = '%s' AND COLUMN_NAME = 'dllcoid'" + " AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');" + "IF v_col_exists = 0 THEN EXECUTE IMMEDIATE 'ALTER TABLE %s ADD  dllcoid VARCHAR(100)';END IF;END;", "P_fm_DirectoryTab", "P_fm_DirectoryTab"));

        sqlBuilder.append(String.format("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS" + " WHERE TABLE_NAME = '%s' AND COLUMN_NAME = 'dllcoid'" + " AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');" + "IF v_col_exists = 0 THEN EXECUTE IMMEDIATE 'ALTER TABLE %s ADD  dllcoid VARCHAR(100)';END IF;END;", "p_fm_filetab", "p_fm_filetab"));

        return sqlBuilder.toString();
    }

    @Override
    public String updateBillTab_210525Sql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append(String.format("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS" + " WHERE TABLE_NAME = '%s' AND COLUMN_NAME = 'dllcoid'" + " AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');" + "IF v_col_exists = 0 THEN EXECUTE IMMEDIATE 'ALTER TABLE %s ADD  dllcoid VARCHAR(100)';END IF;END;", "p_fm_filetab", "p_fm_filetab"));
        return sqlBuilder.toString();
    }

    @Override
    public String updateBillTab_211022Sql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append(String.format("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = '%s' AND COLUMN_NAME = 'bandTitle' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');" + "IF v_col_exists = 0 THEN EXECUTE IMMEDIATE 'ALTER TABLE %s ADD  bandTitle VARCHAR(500)';" + "END IF;" + "END;DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = '%s' " + "AND COLUMN_NAME = 'bandFields' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');" + "IF v_col_exists = 0 THEN EXECUTE IMMEDIATE 'ALTER TABLE %s ADD  bandFields VARCHAR(8000)';END IF;END;", "p_systembilldetail", "p_systembilldetail", "p_systembilldetail", "p_systembilldetail"));
        return sqlBuilder.toString();
    }

    @Override
    public String updateSysTabSql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_SystemTab' AND COLUMN_NAME = 'clientname' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_SystemTab ADD  clientname VARCHAR(100);';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_SystemTab' AND COLUMN_NAME = 'clientenname' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_SystemTab ADD  clientenname VARCHAR(200);';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_SystemTab' AND COLUMN_NAME = 'copyright' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_SystemTab ADD  copyright VARCHAR(100);';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_SystemTab' AND COLUMN_NAME = 'clientlogname' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_SystemTab ADD  clientlogname VARCHAR(200);';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_SystemTab' AND COLUMN_NAME = 'webclientname' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_SystemTab ADD  webclientname VARCHAR(100);';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_SystemTab' AND COLUMN_NAME = 'webclientenname' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_SystemTab ADD  webclientenname VARCHAR(200);';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_SystemTab' AND COLUMN_NAME = 'serverattachpath' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_SystemTab ADD  serverattachpath VARCHAR(2000);';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_SystemTab' AND COLUMN_NAME = 'localOAUrl' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_SystemTab ADD  localOAUrl VARCHAR(2000);';END IF;END;");
        return "";
    }

    @Override
    public String updateSysTab_210630Sql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_SystemTab' AND COLUMN_NAME = 'downloadAddress' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_SystemTab ADD  downloadAddress VARCHAR(1000);';END IF;END;");
        return sqlBuilder.toString();
    }

    @Override
    public String updateProductSpeciesTabSql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'bmp_ProductSpeciesTab' AND COLUMN_NAME = 'uploadOper' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE bmp_ProductSpeciesTab ADD uploadOper VARCHAR(2000);';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'bmp_ProductSpeciesTab' AND COLUMN_NAME = 'downloadOper' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE bmp_ProductSpeciesTab ADD downloadOper VARCHAR(2000);';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'bmp_ProductSpeciesTab' AND COLUMN_NAME = 'deleteOper' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE bmp_ProductSpeciesTab ADD deleteOper VARCHAR(2000);';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'bmp_ProductSpeciesTab' AND COLUMN_NAME = 'previewOper' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE bmp_ProductSpeciesTab ADD previewOper VARCHAR(2000);';END IF;END;");
        return sqlBuilder.toString();
    }

    @Override
    public String updateSysMenuTabSql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'p_formmenuconfigtab' AND COLUMN_NAME = 'dllfilename1' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE P_FormMenuConfigTab ADD DllFileName1 VARCHAR(100);" + "';END IF;END;DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_formmenuconfigtab' AND COLUMN_NAME = 'TargetMode' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE P_FormMenuConfigTab ADD TargetMode INT DEFAULT(0);" + "';END IF;END;DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_formmenuconfigtab' AND COLUMN_NAME = 'GroupCaption' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE P_FormMenuConfigTab ADD GroupCaption VARCHAR(50);" + "';END IF;END;DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_formmenuconfigtab' AND COLUMN_NAME = 'SeriesId' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE P_FormMenuConfigTab ADD SeriesId INT;';END IF;END;");
        return sqlBuilder.toString();
    }

    @Override
    public String updateSubSysTabSql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'P_SubSystemTab' AND COLUMN_NAME = 'subsystip' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE P_SubSystemTab ADD subsystip VARCHAR(500);';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'P_SubSystemTab' AND COLUMN_NAME = 'visible' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE P_SubSystemTab ADD visible INT DEFAULT 0;';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'P_SubSystemTab' AND COLUMN_NAME = 'webshow' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE P_SubSystemTab ADD webshow BIGINT DEFAULT 1;';END IF;END;");
        return sqlBuilder.toString();
    }

    @Override
    public String updateBillTypeTabSql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_systembilltype' AND COLUMN_NAME = 'overbacksql' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systembilltype" + " ADD overbacksql VARCHAR(3000),overbackkey CHAR(36);" + "';END IF;END;DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_systembilltype' AND COLUMN_NAME = 'PopupUnionCode' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systembilltype ADD PopupUnionCode VARCHAR(50) NULL;" + "';END IF;END;DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_systembilltype' AND COLUMN_NAME = 'defaultShowSearch' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systembilltype ADD defaultShowSearch INT NULL;" + "';END IF;END;DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_systembilltype' AND COLUMN_NAME = 'MuitlAuditFlag' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systembilltype ADD MuitlAuditFlag INT NULL;" + "';END IF;END;DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_systembilltype' AND COLUMN_NAME = 'bs_adddllname' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systembilltype ADD bs_adddllname VARCHAR(100) NULL;" + "';END IF;END;DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_systembilltype' AND COLUMN_NAME = 'BackSelected' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systembilltype ADD BackSelected BIGINT NULL;';END IF;END;");
        return "";
    }

    @Override
    public String updateBillTypeTab_210901Sql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_systembilltype' AND COLUMN_NAME = 'countSql ' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systembilltype ADD countSql varchar(max) NULL;" + "';END IF;END;");
        return "";
    }

    @Override
    public String updateDllTabSql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'p_systemdlltab' AND COLUMN_NAME = 'LocationImg' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systemdlltab ADD LocationImg VARCHAR(100);';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'p_systemdlltab' AND COLUMN_NAME = 'addcaption' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systemdlltab ADD addcaption VARCHAR(100);" + "';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'p_systemdlltab' AND COLUMN_NAME = 'modifycaption' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systemdlltab ADD modifycaption VARCHAR(200);" + "';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'p_systemdlltab' AND COLUMN_NAME = 'applycaption' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systemdlltab ADD applycaption VARCHAR(200);" + "';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'p_systemdlltab' AND COLUMN_NAME = 'addmodid' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systemdlltab ADD addmodid VARCHAR(200);';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'p_systemdlltab' AND COLUMN_NAME = 'saveapplyenabled' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systemdlltab ADD saveapplyenabled INT;';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'p_systemdlltab' AND COLUMN_NAME = 'addcopyenabled' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systemdlltab ADD addcopyenabled INT NULL;';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'p_systemdlltab' AND COLUMN_NAME = 'bs_adddllname' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systemdlltab ADD bs_adddllname VARCHAR(50);" + "';END IF;END;DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'p_systemdlltab' AND COLUMN_NAME = 'displayRows' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systemdlltab ADD displayRows INT DEFAULT 50;" + "';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'p_systemdlltab' AND COLUMN_NAME = 'BSRowHeight' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systemdlltab ADD BSRowHeight INT;';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'p_systemdlltab' AND COLUMN_NAME = 'popupWidth' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systemdlltab ADD popupWidth INT,popupHeight INT;" + "';END IF;END;");
        sqlBuilder.append("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'p_systemdlltab' AND COLUMN_NAME = 'BackSelected' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systemdlltab ADD BackSelected BIGINT;';END IF;END;");
        return sqlBuilder.toString();
    }

    @Override
    public String updateDllTab_210610Sql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = 'p_systemdlltab' AND COLUMN_NAME = 'delCaption' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systemdlltab ADD delCaption VARCHAR(100);" + "';END IF;END;");
        return sqlBuilder.toString();
    }

    @Override
    public String updateDllTab_210624Sql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = 'p_systemdlltab' AND COLUMN_NAME = 'addHintMSG' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE p_systemdlltab ADD addHintMSG VARCHAR(1000);" + "';END IF;END;");
        return sqlBuilder.toString();
    }

    @Override
    public String updateDllTab_21429Sql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append(String.format("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = '%s' AND COLUMN_NAME = 'tasksql' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE %s ADD tasksql VARCHAR(max);';END IF;END;", "p_systemdlltab", "p_systemdlltab"));
        sqlBuilder.append(String.format("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = '%s' AND COLUMN_NAME = 'tasksql' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE %s ADD tasksql VARCHAR(max);" + "';END IF;END;", "p_systembilltype", "p_systembilltype"));
        sqlBuilder.append(String.format("DECLARE v_col_exists NUMBER;" + "BEGIN SELECT COUNT(1)INTO v_col_exists FROM ALL_TAB_COLUMNS " + "WHERE TABLE_NAME = '%s' AND COLUMN_NAME = 'newver' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE %s ADD newver INT;';END IF;END;", "p_systembilltype", "p_systembilltype"));
        sqlBuilder.append(String.format("DECLARE v_col_exists NUMBER;BEGIN SELECT COUNT(1)INTO v_col_exists " + "FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = '%s' AND COLUMN_NAME = 'newWFVer' " + "AND OWNER = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');IF v_col_exists = 0 " + "THEN EXECUTE IMMEDIATE 'ALTER TABLE %s ADD newWFVer INT;" + "';END IF;END;", "p_systembilltype", "p_systembilltype"));
        return sqlBuilder.toString();
    }

    @Override
    public String updatePhoneCodeSql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("DECLARE v_table_exists NUMBER;v_owner VARCHAR2(100) := SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');" + "BEGIN SELECT COUNT(1) INTO v_table_exists FROM ALL_TABLES " + "WHERE OWNER = v_owner AND TABLE_NAME = 'P_VALIDCODE'; IF v_table_exists = 0 " + "THEN EXECUTE IMMEDIATE ' CREATE TABLE ' || v_owner || '.P_VALIDCODE( id INT IDENTITY(1,1) " + "NOT NULL PRIMARY KEY,phone VARCHAR(100) NULL, sendtime DATETIME NULL, validcode VARCHAR(10) NULL," + "type INT NULL, ip VARCHAR(20) NULL)';END IF;END;");
        return sqlBuilder.toString();
    }

    @Override
    public StringBuilder BillDataSave(String proName, String escapedMasterSql, String escapedIdValue, String escapedDetailGuid, String escapedBillSeq, int userId, int actionTypeValue, int auditFlag, int comfirmFlag) {
        StringBuilder sqlBuilder = new StringBuilder();
// 修正Oracle变量声明语法（分号分隔、指定长度），保留DM_return/DM_msg变量
        sqlBuilder.append("DECLARE DM_return INT; DM_msg VARCHAR2(2000);  ");
        sqlBuilder.append("BEGIN  ");
// 调用存储过程，严格按存储过程参数顺序传参，OUT参数直接传变量名
        sqlBuilder.append(proName).append("(  ");
// 1: p_returnValue    OUT INT        --> 对应DM_return（OUT）
        sqlBuilder.append("DM_return,  ");
// 2: p_Sql            IN  VARCHAR2   --> 对应传入的escapedMasterSql（IN）
        sqlBuilder.append("'").append(escapedMasterSql).append("',  ");
// 3: p_billdocument_id IN  VARCHAR2 --> 对应传入的escapedIdValue（IN）
        sqlBuilder.append("'").append(escapedIdValue).append("',  ");
// 4: p_tmpstr         IN  VARCHAR2   --> 对应传入的escapedDetailGuid（IN）
        sqlBuilder.append("'").append(escapedDetailGuid).append("',  ");
// 5: p_Operatorid     IN  INT        --> 对应传入的userId（IN）
        sqlBuilder.append(userId).append(",  ");
// 6: p_Fbilltagid     IN  INT        --> 对应传入的actionTypeValue（IN）
        sqlBuilder.append(actionTypeValue).append(",  ");
// 7: p_operateway     IN  VARCHAR2   --> 对应传入的escapedBillSeq（IN）
        sqlBuilder.append("'").append(escapedBillSeq).append("',  ");
// 8: p_msg            OUT VARCHAR2   --> 对应DM_msg（OUT）
        sqlBuilder.append("DM_msg,  ");
// 9: p_auditFlag      IN  INT DEFAULT 0 --> 对应传入的auditFlag（IN，可选）
        sqlBuilder.append(auditFlag).append(",  ");
// 10: p_comfirmFlag   IN  INT DEFAULT 0 --> 对应传入的comfirmFlag（IN，可选）
        sqlBuilder.append(comfirmFlag).append("  ");
        sqlBuilder.append(");  ");
// 查询OUT变量结果，通过DUAL表返回
        sqlBuilder.append("SELECT DM_return AS returnValue, DM_msg AS outputMsg FROM DUAL;  ");
        sqlBuilder.append("END;  ");
        return sqlBuilder;
    }


//    endregion

    //    region
    @Override
    public String BuildBillDetailSqlSql(String getDetailTable) {
//        INFO2是自增列,不是计算列,这里需要考虑colstat(目前用INFO2来表示)
        String sql = String.format("Select name，TYPE$,LENGTH$ as length，0 isnullable,'' text,INFO2 from syscolumns Where ID=OBJECT_ID('%s') ",
                getDetailTable + "_temp");
        return sql;
    }
//endregion

}
