package org.example.Impl.Sql.kingbaseImpl;

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.CRMapper;
import org.example.Utils.PublicUtil;
import org.example.Utils.SqlSafetyGuard;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

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

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

    private final CRMapper crMapper;
    private final JdbcTemplate jdbcTemplate;

    public KingbaseAllInOneSqlProvider(CRMapper crMapper, JdbcTemplate jdbcTemplate) {
        this.crMapper = crMapper;
        this.jdbcTemplate = jdbcTemplate;
    }


    @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 dbo.P_EmployeeTab e " +
                "left join dbo.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";
    }

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

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

    @Override
    public String getExitSystemPrivilegeAgentTabSql() {
        return "select 1 from sysobjects where id = object_id(N'[dbo].[p_systemPrivilegeAgentTab]') and OBJECTPROPERTY(id, N'IsUserTable') = 1";
    }

    @Override
    public String getExitFlowExOperSql() {
        return "select 1 from sysobjects where id = object_id(N'[dbo].[wms_billflowOperex]') and OBJECTPROPERTY(id, N'IsUserTable') = 1";
    }

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

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


    public String GetColumnRowsSql(String moduleId, String userId, String userName, int id, boolean windowsDirver) {

        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("select book.id, book.FieldName, ");
        sqlBuilder.append("coalesce(CAST(gcfg.userName AS varchar(200)), coalesce(coalesce(username1, sysname), book.FieldName)) FieldCaption, ");
        sqlBuilder.append("ControlWidth, ControlHeight, %s Disabled, %s Disabled2, ");
        sqlBuilder.append("fieldsqltag FieldType, addModuleId, %s scanAble, ");
        sqlBuilder.append("ControlTop, ControlLeft, book.tagid nullable, book.cancopy, ");
        sqlBuilder.append("case when coalesce(book.privilegeOper, '') = '' then edit ");
        // 使用position替代charindex，注意参数顺序
        sqlBuilder.append("when position(','||'%s'||',' in ',' || (SELECT string_agg(userList, '') FROM p_systemPrivilege pri ");
        sqlBuilder.append("WHERE book.tab = pri.modid and position(','||cast(pri.privTypeId as varchar(10))||',' in ','||book.privilegeOper||',') > 0)) > 0 then 0 else 1 end Edit, ");
        sqlBuilder.append("isSum \"sum\", sumCond, sumCalc, fieldsql, fieldsqlid valuemember, ");
        sqlBuilder.append("fieldsqlname displaymember, calcExpr CalcExpress, calcOrder CalcOrderId, %s width, ");
        sqlBuilder.append("book.fieldkey fromkey, book.disableType, book.IsAddControl, book.dataAlign textalign, ");
        sqlBuilder.append("unionValue UnionSQL, unionFields UnionField, dataformat, BandTitle, BandFields BandField, ");
        sqlBuilder.append("InputHintText HintText, TitleColor FontColor, limitLen LimitMaxValue, defaultdate defaultvalue, ");
        sqlBuilder.append("TM_HeadString othermember, book.labelWidth, book.labelAlign, book.disableCond, c.xtype fieldDbType, ");
        sqlBuilder.append("frozenFlag locked, (SELECT string_agg(userList, '') FROM p_systemPrivilege pri ");
        sqlBuilder.append("WHERE book.tab = pri.modid and position(','||cast(pri.privTypeId as varchar(10))||',' in ','||book.privilegeView||',') > 0) userList, ");
        sqlBuilder.append("book.FontSize fontSize, book.highlightFColor fcolor, book.highlightBColor bcolor, ");
        sqlBuilder.append("book.highlightBold bold, book.doNotSpelling, book.lookupWidth pickerWidth, ");
        sqlBuilder.append("book.lookupFieldsWidth pickerColsWidth, book.bmptype ftype, book.ifmerge rowspan ");
        sqlBuilder.append("from p_systemwordbooktab book ");
        sqlBuilder.append("left join p_systemdlltab dll on book.tab = dll.dllcoid ");
        // 使用oid获取对象ID，适配Kingbase
        sqlBuilder.append("left join syscolumns c on c.id = (select oid from pg_class where relname = dll.sqldt1) and c.name = book.fieldname ");
        sqlBuilder.append("left join P_systemGridConfigTab gcfg on '%s' || cast(dll.formkey as varchar(100)) = cast(gcfg.formkey as varchar(100)) ");
        sqlBuilder.append("and book.fieldName = gcfg.fieldname and gcfg.operatorid = '%s' ");
        sqlBuilder.append("where 1=1 and (coalesce(book.privilegeView, '') = '' or position(','||'%s'||',' in ',' || ");
        sqlBuilder.append("(SELECT string_agg(userList, '') FROM p_systemPrivilege pri WHERE book.tab = pri.modid ");
        sqlBuilder.append("and position(','||cast(pri.privTypeId as varchar(10))||',' in ','||book.privilegeView||',') > 0)) > 0) ");

        // 处理SQL参数,传递windowsDirver
        String disabled2 = windowsDirver ? "book.addVisible" : "case when coalesce(ShowMobile1,0)=0 then 1 else 0 end";
        String disabled = windowsDirver ? "coalesce(1-gcfg.isvisible,book.vislble)" : "case when coalesce(ShowMobile,0)=0 then 1 else 0 end";
        String scanAble = windowsDirver ? "0" : "book.vislble5";
        String width = windowsDirver ? "coalesce(gcfg.fieldwidth,width)" : "mobilewidth";

        String colsql = String.format(
                sqlBuilder.toString(),
                disabled,         // 对应第一个%s
                disabled2,        // 对应第二个%s
                scanAble,         // 对应第三个%s
                userName,         // 对应第四个%s
                width,            // 对应第五个%s
                CusGridColumnPrefix.BaseMainGridView,  // 对应第六个%s
                userId,           // 对应第七个%s
                userName          // 对应第八个%s
        );
        if (moduleId != null && !moduleId.isEmpty()) {
            colsql = String.format("%s and book.tab = '%s' order by book.orderId limit 1000", colsql, moduleId);
        } else if (id > 0) {
            colsql = String.format("%s and book.id = %d limit 1000", colsql, id);
        }
        return colsql;
    }

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

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

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

    @Override
    public String GetTableInfoSql(String trimmedTbName) {
        String sql = String.format(
                "Select c.name, c.xtype, " +
                        "case when xtype in (35,99,34,173,165) then 0 else c.length end length, " +
                        "c.isnullable, m.text, c.colstat, " +
                        "sc.is_identity isIdentity, sc.is_computed isComputed " +
                        "from syscolumns c " +
                        "left join syscomments m on c.cdefault = m.id " +
                        "left join sys.columns sc on sc.object_id = c.id and c.name = sc.name " +
                        "Where c.ID = OBJECT_ID('%s')",
                trimmedTbName
        );
        return sql;
    }


    @Override
    public String GetColorAndBoxColumnsSql() {
        return "select fieldname, fieldsqltag FieldType, fieldsql, " +
                "fieldsqlid valuemember, fieldsqlname displaymember, " +
                "TitleColor fontcolor " +
                "from dbo.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.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 dbo.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 " +
                "(charindex(',%s,', ',' + replace(op.uploadOper, ' ', '') + ',') > 0 or isnull(op.uploadOper, '') = '' " +
                "or charindex(',%s,', ',' + replace(op.downloadOper, ' ', '') + ',') > 0 or isnull(op.downloadOper, '') = '' " +
                "or charindex(',%s,', ',' + replace(op.deleteOper, ' ', '') + ',') > 0 or isnull(op.deleteOper, '') = '' " +
                "or charindex(',%s,', ',' + replace(op.previewOper, ' ', '') + ',') > 0 or isnull(op.previewOper, '') = '') " +
                "left join p_employeetab p on f.creator = p.employeeid " +
                "where f.parentid = '%s' %s %s";
    }

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

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

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

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

    @Override
    public String GetPmsCountSql() {
        return "SELECT array_length(proargnames,1) FROM sys_proc WHERE proname = ?";
    }

    @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) {
        String sql = "DECLARE @returnValue varchar(max) "
                + "DECLARE @outputValue varchar(max) "
                + "EXEC @returnValue = " + procName + " "
                + "'" + escapedBaseSql + "' ,"  // 末尾无分号，用空格结尾
                + "'" + execType + "' ,"
                + "'" + escapedModuleId + "', "
                + "'" + escapedMasterTable + "' ,"
                + "'" + escapedIdField + "' ,"
                + "'" + escapedIdValue + "' ,"
                + "'" + escapedOperatorId + "' ,"
                + "'" + escapedOperatorName + "' ,"
                + "@outputValue OUTPUT "
                + "SELECT @@ROWCOUNT AS execcount, @returnValue AS returnValue, @outputValue AS outputValue";
        // 步骤4：执行SQL并获取结果
        List<Map<String, Object>> resultSet = new ArrayList<>(jdbcTemplate.queryForList(sql));


        return Collections.singletonList(resultSet);
    }

    @Override
    public String IsExitProSql() {
        return "select 1 from dbo.sysobjects where id = object_id(N'[dbo].[%s]') and OBJECTPROPERTY(id, N'IsProcedure') = 1";
    }

    @Override
    public List<Map<String, Object>> ExecSelectOperStoreSql(List<String> outParamNames, String storeName, Map<String, Object> params) {
        Connection connection = null;
        CallableStatement callableStmt = null;
        ResultSet resultSet = null;
        ResultSet targetResultSet = null;
        try {
            // -------------------- 2. 拼接SQL（保持你的格式） --------------------
            StringBuilder sqlBuilder = new StringBuilder();
            sqlBuilder.append("DECLARE @returnValue int ");
            for (String outParam : outParamNames) {
                if (outParam.equals("@msg")) {
                    sqlBuilder.append("DECLARE ").append(outParam).append(" varchar(2000) ");
                } else {
                    sqlBuilder.append("DECLARE ").append(outParam).append(" varchar(500) ");
                }
            }
            sqlBuilder.append("EXEC @returnValue = [dbo].").append(storeName).append("\n");

            List<String> inputParamParts = new ArrayList<>();
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                String paramName = entry.getKey();
                Object paramValue = entry.getValue();
                String paramPart;
                if (paramValue == null || paramValue.toString().isEmpty()) {
                    paramPart = "\t" + paramName + " = ''";
                } else if (paramValue instanceof String
                        || paramValue instanceof Character
                        || paramValue instanceof java.util.Date) {
                    String escapedValue = paramValue.toString().replace("'", "''");
                    paramPart = "\t" + paramName + " = '" + escapedValue + "'";
                } else {
                    paramPart = "\t" + paramName + " = " + paramValue.toString();
                }
                inputParamParts.add(paramPart);
            }

            List<String> outParamParts = new ArrayList<>();
            for (String outParam : outParamNames) {
                outParamParts.add("\t" + outParam + " = " + outParam + " OUTPUT");
            }

            for (int i = 0; i < inputParamParts.size(); i++) {
                sqlBuilder.append(inputParamParts.get(i));
                if (i != inputParamParts.size() - 1 || !outParamParts.isEmpty()) {
                    sqlBuilder.append(",\n");
                } else {
                    sqlBuilder.append("\n");
                }
            }
            for (int i = 0; i < outParamParts.size(); i++) {
                sqlBuilder.append(outParamParts.get(i));
                if (i != outParamParts.size() - 1) {
                    sqlBuilder.append(",\n");
                }
            }

            sqlBuilder.append("\nSELECT\n");
            sqlBuilder.append("\t@returnValue AS returnValue");
            for (String outParam : outParamNames) {
                sqlBuilder.append(",\n\t").append(outParam).append(" AS ").append(outParam.replace("@", ""));
            }
            sqlBuilder.append(";");

            String finalSql = sqlBuilder.toString();

            // -------------------- 3. 执行SQL并获取正确的结果集（核心简化处理） --------------------
            connection = jdbcTemplate.getDataSource().getConnection();
            callableStmt = connection.prepareCall(finalSql);
            // 执行并处理多结果集（仅保留找到目标结果集的逻辑，去掉冗余判断）
            boolean hasMoreResults = callableStmt.execute();
            while (hasMoreResults) {
                resultSet = callableStmt.getResultSet();
                if (resultSet != null) {
                    // 检查当前结果集是否包含returnValue列（目标结果集）
                    try {
                        if (resultSet.getMetaData().getColumnCount() > 0
                                && resultSet.getMetaData().getColumnName(1).equals("returnValue")) {
                            targetResultSet = resultSet; // 找到目标结果集，跳出循环
                            break;
                        }
                    } catch (SQLException e) {
                        // 忽略无效结果集的错误
                    }
                    // 非目标结果集直接关闭
                    if (resultSet != targetResultSet) {
                        resultSet.close();
                    }
                }
                hasMoreResults = callableStmt.getMoreResults();
            }

            // 解析目标结果集（如果找到）
            Map<String, Object> resultMap = new HashMap<>();
            if (targetResultSet != null) {
                if (targetResultSet.next()) {
                    resultMap.put("RETURN_VALUE", targetResultSet.getInt("returnValue"));
                    for (String outParam : outParamNames) {
                        String colName = outParam.replace("@", "");
                        resultMap.put(outParam, targetResultSet.getString(colName));
                    }
                }
            }
            return (List<Map<String, Object>>) resultMap;
        } catch (Exception e) {
            // 优化1：添加上下文信息，方便排查（明确是人大金仓SQL拼接/创建Stmt失败）
            String errorMsg = String.format("人大金仓存储过程SQL拼接/创建CallableStatement失败，存储过程名：%s", storeName);
            // 优化2：保留原始异常（e），让堆栈包含完整原因
            throw new RuntimeException(errorMsg, e);

        } finally {
            // 优化3：关闭资源（关键！避免连接泄漏）
            // 注意：如果callableStmt创建成功并返回，这里不要关闭（由上层调用方关闭）
            // 只关闭异常时创建的connection（如果callableStmt没返回）
            if (callableStmt == null && connection != null) {
                try {
                    connection.close();
                } catch (SQLException ex) {
                    // 记录资源关闭失败的日志（不抛异常，避免覆盖原始异常）
                    log.error("Exception caught", ex);
                }
            }
        }
    }

    @Override
    public String hasStoreParametersql(String procedureName, String paramName) {
        return String.format("SELECT parameter_name FROM information_schema.parameters " +
                        "WHERE specific_name = '%s' AND parameter_name = '%s'",
                procedureName, paramName);
    }


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

    @Override
    public String GetIdentityFieldSql(String finaltbName) {
        String sql = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = '%s' AND COLUMNPROPERTY(OBJECT_ID('%s'), COLUMN_NAME, 'IsIdentity') = 1";
        String identitySql = String.format(sql, finaltbName, finaltbName);
        return identitySql;
    }

    @Override
    public String CheckIsMulitAuditSql() {
        return "select case when col_length('wms_billflowOperView', 'auditoperators') is null then 0 else 1 end;";
    }

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

    @Override
    public String GetAuditBaseDetailsSql() {
        return "select case when exists (" +
                "select 1 from dbo.sysobjects where id = object_id(N'[dbo].[p_SystemDlltabDetailFlowTab]') " +
                "and OBJECTPROPERTY(id, N'IsUserTable') = 1) then 1 else 0 end";
    }

    @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 dbo.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 dbo.v_systemdlltab " +
                "where dllcoid = ? " +
                "group by bmpspec, dllcoid";
    }

    @Override
    public String GetSqlUserSql() {
        return "SELECT EmployeeName + ',' FROM p_employeetab WHERE (%s) FOR XML PATH('')";
    }

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

    @Override
    public String hasExistsTableSql(String tableName) {
        return "SELECT COUNT(1) FROM sysobjects WHERE id = object_id(N'[dbo].[" + tableName + "]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1";
    }

    @Override
    public String GetUsersSql() {
        return "if not exists (select 1 from dbo.sysobjects where id = object_id(N'[dbo].[P_employeeBaseView]') and xtype='v') " +
                "exec('select employeeid 员工ID, loginaccount 员工工号, employeename 员工姓名, departmentname 所属部门, speciesname as 所属类别 " +
                "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 isnull(a.sign,0)=0 and isnull(useflag,0)=1 " +
                "and (employeename like (''%{0}%'') OR dbo.P_GetPy(employeename) like (''%{0}%'')) " +
                "and loginaccount like (''%{1}%'') " +
                "and ({2}=0 or employeeid=''{2}''))' ) " +
                "else " +
                "exec('select * from P_employeeBaseView where 1=1 " +
                "and (员工姓名 like (''%{0}%'') OR dbo.P_GetPy(员工姓名) like (''%{0}%'')) " +
                "and 员工工号 like (''%{1}%'') " +
                "and ({2}=0 or 员工ID=''{2}'')')";
    }

    @Override
    public String GetAccountInfoByStoreSql() {
        return "select 1 from dbo.sysobjects where id = object_id(N'dbo.p_systemGetAccountTask') and OBJECTPROPERTY(id, N'IsProcedure') = 1";
    }

    @Override
    public String GetProSysTypeSql() {
        return "if not exists (select 1 from dbo.sysobjects where id = object_id(N'[dbo].[p_systemproductseriestab]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) select 0 else select 1";
    }

    @Override
    public String CheckUserAbleSql(Map<String, Object> updRow, String trimmedUsers) {
        return "select employeename + ',' from p_employeetab " +
                "where (" + PublicUtil.ReqSqlPmsByRow(updRow, null, trimmedUsers) + ")" +
                "FOR XML PATH('');";
    }

    @Override
    public String GetNextSelectStepListSql() {
        return "select stepcode, stepname from %s " +
                "where typecode='%s' " +
                "and charindex(';' + cast(stepcode as varchar) + ';', ';%s;') > 0";
    }

    public String GetColumnRows(String moduleId, String userId, String userName, int id, boolean windowsDirver) {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("select book.id, book.FieldName, ");
        sqlBuilder.append("coalesce(CAST(gcfg.userName AS varchar(200)), coalesce(coalesce(username1, sysname), book.FieldName)) FieldCaption, ");
        sqlBuilder.append("ControlWidth, ControlHeight, %s Disabled, %s Disabled2, ");
        sqlBuilder.append("fieldsqltag FieldType, addModuleId, %s scanAble, ");
        sqlBuilder.append("ControlTop, ControlLeft, book.tagid nullable, book.cancopy, ");
        sqlBuilder.append("case when coalesce(book.privilegeOper, '') = '' then edit ");
        // 使用position替代charindex，注意参数顺序
        sqlBuilder.append("when position(','||'%s'||',' in ',' || (SELECT string_agg(userList, '') FROM p_systemPrivilege pri ");
        sqlBuilder.append("WHERE book.tab = pri.modid and position(','||cast(pri.privTypeId as varchar(10))||',' in ','||book.privilegeOper||',') > 0)) > 0 then 0 else 1 end Edit, ");
        sqlBuilder.append("isSum \"sum\", sumCond, sumCalc, fieldsql, fieldsqlid valuemember, ");
        sqlBuilder.append("fieldsqlname displaymember, calcExpr CalcExpress, calcOrder CalcOrderId, %s width, ");
        sqlBuilder.append("book.fieldkey fromkey, book.disableType, book.IsAddControl, book.dataAlign textalign, ");
        sqlBuilder.append("unionValue UnionSQL, unionFields UnionField, dataformat, BandTitle, BandFields BandField, ");
        sqlBuilder.append("InputHintText HintText, TitleColor FontColor, limitLen LimitMaxValue, defaultdate defaultvalue, ");
        sqlBuilder.append("TM_HeadString othermember, book.labelWidth, book.labelAlign, book.disableCond, c.xtype fieldDbType, ");
        sqlBuilder.append("frozenFlag locked, (SELECT string_agg(userList, '') FROM p_systemPrivilege pri ");
        sqlBuilder.append("WHERE book.tab = pri.modid and position(','||cast(pri.privTypeId as varchar(10))||',' in ','||book.privilegeView||',') > 0) userList, ");
        sqlBuilder.append("book.FontSize fontSize, book.highlightFColor fcolor, book.highlightBColor bcolor, ");
        sqlBuilder.append("book.highlightBold bold, book.doNotSpelling, book.lookupWidth pickerWidth, ");
        sqlBuilder.append("book.lookupFieldsWidth pickerColsWidth, book.bmptype ftype, book.ifmerge rowspan ");
        sqlBuilder.append("from p_systemwordbooktab book ");
        sqlBuilder.append("left join p_systemdlltab dll on book.tab = dll.dllcoid ");
        // 使用oid获取对象ID，适配Kingbase
        sqlBuilder.append("left join syscolumns c on c.id = (select oid from pg_class where relname = dll.sqldt1) and c.name = book.fieldname ");
        sqlBuilder.append("left join P_systemGridConfigTab gcfg on '%s' || cast(dll.formkey as varchar(100)) = cast(gcfg.formkey as varchar(100)) ");
        sqlBuilder.append("and book.fieldName = gcfg.fieldname and gcfg.operatorid = '%s' ");
        sqlBuilder.append("where 1=1 and (coalesce(book.privilegeView, '') = '' or position(','||'%s'||',' in ',' || ");
        sqlBuilder.append("(SELECT string_agg(userList, '') FROM p_systemPrivilege pri WHERE book.tab = pri.modid ");
        sqlBuilder.append("and position(','||cast(pri.privTypeId as varchar(10))||',' in ','||book.privilegeView||',') > 0)) > 0) ");

        // 获取Windows驱动标志
        // 处理SQL参数
        String disabled2 = windowsDirver ? "book.addVisible" : "case when coalesce(ShowMobile1,0)=0 then 1 else 0 end";
        String disabled = windowsDirver ? "coalesce(1-gcfg.isvisible,book.vislble)" : "case when coalesce(ShowMobile,0)=0 then 1 else 0 end";
        String scanAble = windowsDirver ? "0" : "book.vislble5";
        String width = windowsDirver ? "coalesce(gcfg.fieldwidth,width)" : "mobilewidth";

        // 格式化SQL（参数顺序与%s占位符对应）
        String colsql = String.format(
                sqlBuilder.toString(),
                disabled,         // 对应第一个%s
                disabled2,        // 对应第二个%s
                scanAble,         // 对应第三个%s
                userName,         // 对应第四个%s
                width,            // 对应第五个%s
                CusGridColumnPrefix.BaseMainGridView,  // 对应第六个%s
                userId,           // 对应第七个%s
                userName          // 对应第八个%s
        );

        // 补充查询条件
        if (moduleId != null && !moduleId.isEmpty()) {
            colsql = String.format("%s and book.tab = '%s' order by book.orderId limit 1000", colsql, moduleId);
        } else if (id > 0) {
            colsql = String.format("%s and book.id = %d limit 1000", colsql, id);
        }
        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 " +
                "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 crMapper.getModuleIdFieldRow(keyName, key, SystemEnums.ControlType.LabTreeType.getValue());
    }

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

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

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

    @Override
    public List<Map<String, Object>> GetBillMasterRows(String moduleId, String userName, Integer id, boolean window) {
        return crMapper.GetBillMasterRows(moduleId, userName, id, window);
    }

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

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

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

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

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

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

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

    @Override
    public List<Map<String, Object>> GetBaseModule(String moduleCode, String menuId) {
        return crMapper.GetBaseModule(moduleCode, menuId);
    }

    @Override
    public String GetAuditMsgTab(String userId) {
        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（使用经过验证的正确语法）
        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
                        LIMIT 100
                    ) t1
                    UNION ALL
                    SELECT * FROM (
                        SELECT 
                            Msg, 
                            NoticeUserID, 
                            CreateDate, 
                            id,
                            1,
                            DllCoid,
                            0,
                            '',
                            Title,
                            MsgDetail,
                            0,
                            0,
                            0,
                            0,
                            2 tablecode,
                            templetename tplname
                        FROM p_systemMessageTab 
                        WHERE DeleteFlag=0 
                          %s  
                        ORDER BY CreateDate DESC
                        LIMIT 100
                    ) t2
                ) cc 
                ORDER BY OperateDate DESC
                LIMIT 100;""", userCond1, userCond2);
        return sql;
    }

    @Override
    public String GetDeskTopCommonUse(int cardId, String userId) {
        String sql = String.format("WITH tmenu(id,MenuCaption,ParentId,menuid,SubSysId,level) " +
                "as " +
                "( " +
                "    SELECT  convert(varchar(50), menu.SubSysId)+'_'+ MenuStruct id,MenuCaption,CONVERT(varchar(50), -CONVERT(int, menu.SubSysId)) 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  LEN(menustruct)=2 and isnull(menu.SeriesId, 1)=1 and ISNULL(menu.UseFlag,1)=1 " +
                "    UNION ALL  " +
                "    SELECT convert(varchar(50), a.SubSysId)+'_'+A.MenuStruct id, A.MenuCaption,CONVERT(varchar(50),convert(varchar(50), a.SubSysId)+'_'+  SUBSTRING(a.MenuStruct,1,case when LEN(a.MenuStruct)>2 then LEN(a.MenuStruct)-2 else 0 end)) ParentId,a.MenuId,a.SubSysId ,b.level+1 FROM p_formmenuconfigtab A,tmenu b   " +
                "    where LEN(a.MenuStruct)>2 and ISNULL(UseFlag,1)=1 and convert(varchar(50), a.SubSysId)+'_'+ SUBSTRING(a.MenuStruct,1,case when  LEN(a.MenuStruct)>2 then LEN(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 CHARINDEX('www',pmt.ObjDll)>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),isnull(cast(psdtp.countsql as varchar),''))='' 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);

        if (cardId < 0) {
            if (cardId == -99) {
                sql = "select pmt.objdll dllfilename,case when CHARINDEX('www',pmt.ObjDll)>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 {
                sql = String.format("select * from (                                " +
                        "select distinct pfmc.serverId,  case when isnull(pfmc.dllfilename1,'')='' then pfmc.dllfilename else pfmc.dllfilename1 end dllfilename,case when CHARINDEX('www',pmt.ObjDll)>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),isnull(cast(psdtp.countsql as varchar),''))='' 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) {
        String sql = "if exists (select 1 from dbo.sysobjects where id = object_id(N'[dbo].[p_systemSearchTextTab]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) " +
                "begin " +
                "   WITH tmenu(id,ParentId,menuid,SubSysId,level) " +
                "   as " +
                "   ( " +
                "       SELECT  convert(varchar(50), menu.SubSysId)+'_'+ MenuStruct id,CONVERT(varchar(50), -CONVERT(int, menu.SubSysId)) 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 LEN(menustruct)=2 " +
                "       UNION ALL  " +
                "       SELECT convert(varchar(50), a.SubSysId)+'_'+A.MenuStruct id,CONVERT(varchar(50),convert(varchar(50), a.SubSysId)+'_'+  SUBSTRING(a.MenuStruct,1,case when LEN(a.MenuStruct)>2 then LEN(a.MenuStruct)-2 else 0 end)) ParentId,a.MenuId,a.SubSysId ,b.level+1 FROM p_formmenuconfigtab A,tmenu b   " +
                "       where LEN(a.MenuStruct)>2 and ISNULL(UseFlag,1)=1 and convert(varchar(50), a.SubSysId)+'_'+ SUBSTRING(a.MenuStruct,1,case when  LEN(a.MenuStruct)>2 then LEN(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,getdate() 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 dbo.P_GetPy(pm.menuCaption) like ('%' + ? +'%')) " +
                "   union all " +
                "   select top 50 * from p_systemSearchTextTab  where keyvalue='SYSTEM_MENU' " +
                "   and   ( key_context like ('%' + ? +'%') OR dbo.P_GetPy(key_context) like ('%' + ? +'%') " +
                "   or target_modid like ('%' + ? +'%')  or src_modid like ('%' + ? +'%') ) " +
                "end " +
                "else " +
                "begin " +
                "   WITH tmenu(id,ParentId,menuid,SubSysId,level) " +
                "   as " +
                "   ( " +
                "       SELECT  convert(varchar(50), menu.SubSysId)+'_'+ MenuStruct id,CONVERT(varchar(50), -CONVERT(int, menu.SubSysId)) 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 LEN(menustruct)=2 " +
                "       UNION ALL  " +
                "       SELECT convert(varchar(50), a.SubSysId)+'_'+A.MenuStruct id,CONVERT(varchar(50),convert(varchar(50), a.SubSysId)+'_'+  SUBSTRING(a.MenuStruct,1,case when LEN(a.MenuStruct)>2 then LEN(a.MenuStruct)-2 else 0 end)) ParentId,a.MenuId,a.SubSysId ,b.level+1 FROM p_formmenuconfigtab A,tmenu b   " +
                "       where LEN(a.MenuStruct)>2 and ISNULL(UseFlag,1)=1 and convert(varchar(50), a.SubSysId)+'_'+ SUBSTRING(a.MenuStruct,1,case when  LEN(a.MenuStruct)>2 then LEN(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,getdate() 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 dbo.P_GetPy(pm.menuCaption) like ('%' + ? +'%')) " +
                "end";
        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 CHARINDEX(',' + ? + ',', ',' + b.itemPrivilege + ',') > 0)";
        return sql;
    }

    @Override
    public String CheckIsAudit() {
        return "select col_length('P_SystemCheckTab', 'stepover')";
    }

    @Override
    public String GetProSysType() {
        return "if not exists (select 1 from dbo.sysobjects where id = object_id(N'[dbo].[p_systemproductseriestab]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) select 0 else select 1";
    }

    @Override
    public String GetPrimaryKeysArray(String tabname) {
        String primaryKeysSql = String.format("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = '%s'", tabname);
        return primaryKeysSql;
    }

    @Override
    public String GetUsers(String name, String code, int userId) {
        String safeName = name == null ? "" : name.replace("'", "");
        String safeCode = code == null ? "" : code.replace("'", "");
        String sql = "if not exists (select 1 from dbo.sysobjects where id = object_id(N'[dbo].[P_employeeBaseView]') and xtype='v') " +
                "exec('select employeeid 员工ID, loginaccount 员工工号, employeename 员工姓名, departmentname 所属部门, speciesname as 所属类别 " +
                "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 isnull(a.sign,0)=0 and isnull(useflag,0)=1 " +
                "and (employeename like (''%{0}%'') OR dbo.P_GetPy(employeename) like (''%{0}%'')) " +
                "and loginaccount like (''%{1}%'') " +
                "and ({2}=0 or employeeid=''{2}''))' ) " +
                "else " +
                "exec('select * from P_employeeBaseView where 1=1 " +
                "and (员工姓名 like (''%{0}%'') OR dbo.P_GetPy(员工姓名) like (''%{0}%'')) " +
                "and 员工工号 like (''%{1}%'') " +
                "and ({2}=0 or 员工ID=''{2}'')')";

        sql = String.format(sql, safeName, safeCode, userId);
        return sql;
    }

    @Override
    public String hasExistsTable(String tableName) {
        return "SELECT COUNT(1) FROM sysobjects WHERE id = object_id(N'[dbo].[" + tableName + "]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1";
    }

    @Override
    public String SeeOneAuditMsg() {
        return """
                declare @type varchar(200)
                select @type = c.name
                from sys.columns a
                inner join sys.tables b on b.object_id = a.object_id
                inner join sys.types c on c.system_type_id = a.system_type_id
                where b.name = 'p_systemNoticeSheet' and a.name = 'Created'
                                
                if @type <> 'datetime'
                begin
                    declare @constraintName varchar(200)
                    set @constraintName = ''
                    select @constraintName = b.name
                    from syscolumns a, sysobjects b
                    where a.id = object_id('p_systemNoticeSheet')
                      and b.id = a.cdefault
                      and a.name = 'Created'
                      and b.name like 'DF%'
                                
                    exec('alter table p_systemNoticeSheet drop constraint ' + @constraintName)
                    alter table p_systemNoticeSheet alter column Created datetime null
                end
                                
                insert into p_systemNoticeSheet (Msg, Created, UserId)
                VALUES ('{0}', '{1}', '{2}')
                """;
    }

//    endregion


//    region UpdateImpl

    @Override
    public String updateSystemOtherTabSql() {

        StringBuilder sqlBuilder = new StringBuilder();

        sqlBuilder.append(String.format(
                "if col_length('%s', 'sumCond') is null exec('alter table %s add sumCond varchar(500);')",
                "dbo.p_systemdlltabdetailgrid", "dbo.p_systemdlltabdetailgrid"
        ));

        sqlBuilder.append(String.format(
                "if col_length('%s', 'sumCond') is null exec('alter table %s add sumCond varchar(500);')",
                "dbo.p_systembilldetail", "dbo.p_systembilldetail"
        ));

        sqlBuilder.append(String.format(
                "if col_length('%s', 'sumCond') is null exec('alter table %s add sumCond varchar(500);')",
                "dbo.p_systembillauditAttachDetail", "dbo.p_systembillauditAttachDetail"
        ));

        sqlBuilder.append(String.format(
                "if col_length('%s', 'dllcoid') is null exec('alter table %s add dllcoid varchar(100);')",
                "dbo.P_fm_DirectoryTab", "dbo.P_fm_DirectoryTab"
        ));

        sqlBuilder.append(String.format(
                "if col_length('%s', 'dllcoid') is null exec('alter table %s add dllcoid varchar(100);')",
                "dbo.p_fm_filetab", "dbo.p_fm_filetab"
        ));
        return sqlBuilder.toString();
    }

    @Override
    public String updateBillTab_210525Sql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append(String.format(
                "if col_length('%s', 'addmoduleid') is null exec('alter table %s add addmoduleid varchar(500);')",
                "dbo.p_systembilldetail", "dbo.p_systembilldetail"
        ));
        return sqlBuilder.toString();
    }

    @Override
    public String updateBillTab_211022Sql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append(String.format(
                "if col_length('%s', 'bandTitle') is null exec('alter table %s add bandTitle varchar(500);') " +
                        "if col_length('%s', 'bandFields') is null exec('alter table %s add bandFields varchar(8000);')",
                "dbo.p_systembilldetail", "dbo.p_systembilldetail",
                "dbo.p_systembilldetail", "dbo.p_systembilldetail"
        ));
        return sqlBuilder.toString();
    }

    @Override
    public String updateSysTabSql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("if col_length('dbo.p_SystemTab', 'clientname') is null " +
                "exec('alter table dbo.p_SystemTab add clientname varchar(100);')");
        sqlBuilder.append("if col_length('dbo.p_SystemTab', 'clientenname') is null " +
                "exec('alter table dbo.p_SystemTab add clientenname varchar(200);')");
        sqlBuilder.append("if col_length('dbo.p_SystemTab', 'copyright') is null " +
                "exec('alter table dbo.p_SystemTab add copyright varchar(100);')");
        sqlBuilder.append("if col_length('dbo.p_SystemTab', 'clientlogname') is null " +
                "exec('alter table dbo.p_SystemTab add clientlogname varchar(200);')");
        sqlBuilder.append("if col_length('dbo.p_SystemTab', 'webclientname') is null " +
                "exec('alter table dbo.p_SystemTab add webclientname varchar(100);')");
        sqlBuilder.append("if col_length('dbo.p_SystemTab', 'webclientenname') is null " +
                "exec('alter table dbo.p_SystemTab add webclientenname varchar(200);')");
        sqlBuilder.append("if col_length('dbo.p_SystemTab', 'serverattachpath') is null " +
                "exec('alter table dbo.p_SystemTab add serverattachpath varchar(2000);')");
        sqlBuilder.append("if col_length('dbo.p_SystemTab', 'localOAUrl') is null " +
                "exec('alter table dbo.p_SystemTab add localOAUrl varchar(2000);')");
        return sqlBuilder.toString();
    }

    @Override
    public String updateSysTab_210630Sql() {
        StringBuilder sqlBuilder = new StringBuilder();

        sqlBuilder.append("if col_length('dbo.p_SystemTab', 'downloadAddress') is null " +
                "exec('alter table dbo.p_SystemTab add downloadAddress varchar(1000);')");
        return sqlBuilder.toString();
    }

    @Override
    public String updateProductSpeciesTabSql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("if col_length('dbo.bmp_ProductSpeciesTab', 'uploadOper') is null " +
                "exec('alter table dbo.bmp_ProductSpeciesTab add uploadOper varchar(2000);')");
        sqlBuilder.append("if col_length('dbo.bmp_ProductSpeciesTab', 'downloadOper') is null " +
                "exec('alter table dbo.bmp_ProductSpeciesTab add downloadOper varchar(2000);')");
        sqlBuilder.append("if col_length('dbo.bmp_ProductSpeciesTab', 'deleteOper') is null " +
                "exec('alter table dbo.bmp_ProductSpeciesTab add deleteOper varchar(2000);')");
        sqlBuilder.append("if col_length('dbo.bmp_ProductSpeciesTab', 'previewOper') is null " +
                "exec('alter table dbo.bmp_ProductSpeciesTab add previewOper varchar(2000);')");
        return sqlBuilder.toString();
    }

    @Override
    public String updateSysMenuTabSql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("if col_length('dbo.p_formmenuconfigtab', 'dllfilename1') is null " +
                "exec('alter table dbo.P_FormMenuConfigTab add DllFileName1 varchar(100);') " +
                "if col_length('dbo.p_formmenuconfigtab', 'TargetMode') is null " +
                "exec('alter table dbo.P_FormMenuConfigTab add TargetMode int default(0);') " +
                "if col_length('dbo.p_formmenuconfigtab', 'GroupCaption') is null " +
                "exec('alter table dbo.P_FormMenuConfigTab add GroupCaption varchar(50);') " +
                "if col_length('dbo.p_formmenuconfigtab', 'SeriesId') is null " +
                "exec('alter table dbo.P_FormMenuConfigTab add SeriesId int');");
        return sqlBuilder.toString();
    }

    @Override
    public String updateSubSysTabSql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("if col_length('dbo.P_SubSystemTab', 'subsystip') is null " +
                "exec('alter table dbo.P_SubSystemTab add subsystip varchar(500);')");
        sqlBuilder.append("if col_length('dbo.P_SubSystemTab', 'visible') is null " +
                "exec('alter table dbo.P_SubSystemTab add visible int default 0;')");
        sqlBuilder.append("if col_length('dbo.P_SubSystemTab', 'webshow') is null " +
                "exec('alter table dbo.P_SubSystemTab add webshow bit default 1;')");
        return sqlBuilder.toString();
    }

    @Override
    public String updateBillTypeTabSql() {
        StringBuilder sqlBuilder = new StringBuilder();

        sqlBuilder.append("if col_length('dbo.p_systembilltype', 'overbacksql') is null " +
                "exec('alter table dbo.p_systembilltype add overbacksql varchar(3000),overbackkey uniqueidentifier;') " +
                "if col_length('dbo.p_systembilltype', 'PopupUnionCode') is null " +
                "exec('alter table dbo.p_systembilltype add PopupUnionCode [varchar](50) NULL') " +
                "if col_length('dbo.p_systembilltype', 'defaultShowSearch') is null " +
                "exec('alter table dbo.p_systembilltype add defaultShowSearch [int] NULL') " +
                "if col_length('dbo.p_systembilltype', 'MuitlAuditFlag') is null " +
                "exec('alter table dbo.p_systembilltype add MuitlAuditFlag [int] NULL') " +
                "if col_length('dbo.p_systembilltype', 'bs_adddllname') is null " +
                "exec('alter table dbo.p_systembilltype add bs_adddllname varchar(100) NULL') " +
                "if col_length('dbo.p_systembilltype', 'BackSelected') is null " +
                "exec('alter table dbo.p_systembilltype add BackSelected bit NULL');");
        return sqlBuilder.toString();
    }

    @Override
    public String updateBillTypeTab_210901Sql() {
        StringBuilder sqlBuilder = new StringBuilder();

        sqlBuilder.append("if col_length('dbo.p_systembilltype', 'countSql') is null " +
                "exec('alter table dbo.p_systembilltype add countSql varchar(max) NULL');");
        return sqlBuilder.toString();
    }

    @Override
    public String updateDllTabSql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("if col_length('dbo.p_systemdlltab', 'LocationImg') is null " +
                "exec('alter table dbo.p_systemdlltab add LocationImg varchar(100);')");
        sqlBuilder.append("if col_length('dbo.p_systemdlltab', 'addcaption') is null " +
                "exec('alter table dbo.p_systemdlltab add addcaption varchar(100);')");
        sqlBuilder.append("if col_length('dbo.p_systemdlltab', 'modifycaption') is null " +
                "exec('alter table dbo.p_systemdlltab add modifycaption varchar(200);')");
        sqlBuilder.append("if col_length('dbo.p_systemdlltab', 'applycaption') is null " +
                "exec('alter table dbo.p_systemdlltab add applycaption varchar(200);')");
        sqlBuilder.append("if col_length('dbo.p_systemdlltab', 'addmodid') is null " +
                "exec('alter table dbo.p_systemdlltab add addmodid varchar(200);')");
        sqlBuilder.append("if col_length('dbo.p_systemdlltab', 'saveapplyenabled') is null " +
                "exec('alter table dbo.p_systemdlltab add saveapplyenabled int;')");
        sqlBuilder.append("if col_length('dbo.p_systemdlltab', 'addcopyenabled') is null " +
                "exec('alter table dbo.p_systemdlltab add addcopyenabled int null;')");

        sqlBuilder.append("if col_length('dbo.p_systemdlltab', 'bs_adddllname') is null " +
                "exec('alter table dbo.p_systemdlltab add bs_adddllname varchar(50)') " +
                "if col_length('dbo.p_systemdlltab', 'displayRows') is null " +
                "exec('alter table dbo.p_systemdlltab add displayRows int default 50;')");

        sqlBuilder.append("if col_length('dbo.p_systemdlltab', 'BSRowHeight') is null " +
                "exec('alter table dbo.p_systemdlltab add BSRowHeight int;')");

        sqlBuilder.append("if col_length('dbo.p_systemdlltab', 'popupWidth') is null " +
                "exec('alter table dbo.p_systemdlltab add popupWidth int,popupHeight int;')");

        sqlBuilder.append("if col_length('dbo.p_systemdlltab', 'BackSelected') is null " +
                "exec('alter table dbo.p_systemdlltab add BackSelected bit;')");
        return sqlBuilder.toString();
    }

    @Override
    public String updateDllTab_210610Sql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("if col_length('dbo.p_systemdlltab', 'delCaption') is null " +
                "exec('alter table dbo.p_systemdlltab add delCaption varchar(100);')");
        return sqlBuilder.toString();
    }

    @Override
    public String updateDllTab_210624Sql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("if col_length('dbo.p_systemdlltab', 'addHintMSG') is null " +
                "exec('alter table dbo.p_systemdlltab add addHintMSG varchar(1000);')");
        return sqlBuilder.toString();
    }

    @Override
    public String updateDllTab_21429Sql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append(String.format("if col_length('%s', 'tasksql') is null " +
                        "exec('alter table %s add tasksql varchar(max);')",
                "dbo.p_systemdlltab", "dbo.p_systemdlltab"));
        sqlBuilder.append(String.format("if col_length('%s', 'tasksql') is null " +
                        "exec('alter table %s add tasksql varchar(max);')",
                "dbo.p_systembilltype", "dbo.p_systembilltype"));
        sqlBuilder.append(String.format("if col_length('%s', 'newver') is null " +
                        "exec('alter table %s add newver int;')",
                "dbo.p_systembilltype", "dbo.p_systembilltype"));
        sqlBuilder.append(String.format("if col_length('%s', 'newWFVer') is null " +
                        "exec('alter table %s add newWFVer int;')",
                "dbo.p_systembilltype", "dbo.p_systembilltype"));
        return sqlBuilder.toString();
    }

    @Override
    public String updatePhoneCodeSql() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("IF NOT EXISTS (\n");
        sqlBuilder.append("    SELECT 1 FROM information_schema.tables \n");
        sqlBuilder.append("    WHERE table_schema = 'dbo'");
        sqlBuilder.append("      AND table_name = 'P_ValidCode' ");
        sqlBuilder.append(")\n");
        sqlBuilder.append("BEGIN\n");
        sqlBuilder.append("    CREATE TABLE dbo.P_ValidCode(\n");
        sqlBuilder.append("        id INT PRIMARY KEY IDENTITY(1,1) NOT NULL,\n");
        sqlBuilder.append("        phone VARCHAR(100) NULL,\n");
        sqlBuilder.append("        sendtime DATETIME NULL,\n");
        sqlBuilder.append("        validcode VARCHAR(10) NULL,\n");
        sqlBuilder.append("        type INT NULL,\n");
        sqlBuilder.append("        ip VARCHAR(20) NULL\n");
        sqlBuilder.append("    );\n");
        sqlBuilder.append("END\n");

        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();
        sqlBuilder.append("DECLARE @returnValue int  ");
        sqlBuilder.append("DECLARE @outputMsg varchar(2000)  ");
        // 4.2 执行存储过程（参数顺序严格匹配）
        sqlBuilder.append("EXEC @returnValue = [dbo].[").append(proName).append("] ");
        sqlBuilder.append("'").append(escapedMasterSql).append("', "); // 参数2：转义后的masterSql
        sqlBuilder.append("'").append(escapedIdValue).append("', ");   // 参数3：转义后的IdValue
        sqlBuilder.append("'").append(escapedDetailGuid).append("', "); // 参数4：转义后的DetailGuid
        sqlBuilder.append(userId).append(", "); // 参数5：userId（int，无单引号）
        sqlBuilder.append(actionTypeValue).append(", "); // 参数6：actionTypeValue（int，无单引号）
        sqlBuilder.append("'").append(escapedBillSeq).append("', "); // 参数7：转义后的billSeq
        sqlBuilder.append("@outputMsg OUTPUT, "); // 参数8：输出参数（标记OUTPUT）
        sqlBuilder.append(auditFlag).append(", "); // 参数9：auditFlag（int，无单引号）
        sqlBuilder.append(comfirmFlag).append("  "); // 参数10：comfirmFlag（int，无单引号）
        // 4.3 查询结果（返回值+输出参数）
        sqlBuilder.append("SELECT @returnValue AS returnValue, @outputMsg AS outputMsg;");
        return sqlBuilder;
    }


//    endregion

//    region
    @Override
    public String BuildBillDetailSqlSql(String getDetailTable) {
        String sql = String.format("Select name,xtype,length,0 isnullable,'' text,colstat from syscolumns Where ID=OBJECT_ID('%s') ",
                getDetailTable + "_temp");
        return sql;
    }
//    endregion

}
