liuling 3 月之前
父節點
當前提交
1f9b705947

+ 84 - 0
websrc/com/kingdee/eas/custom/dataconfig/utils/NativeSqlJoinOptimizer.java

@@ -0,0 +1,84 @@
+package com.kingdee.eas.custom.dataconfig.utils;
+
+import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import net.sf.jsqlparser.schema.Column;
+import net.sf.jsqlparser.schema.Table;
+import net.sf.jsqlparser.statement.Statement;
+import net.sf.jsqlparser.statement.select.*;
+import net.sf.jsqlparser.util.TablesNamesFinder;
+
+import java.util.*;
+
+
+public class NativeSqlJoinOptimizer {
+
+    public  String cleanRedundantJoins(String originalSql)   throws JSQLParserException {
+        // 1. 解析SQL
+        Statement statement = CCJSqlParserUtil.parse(originalSql);
+        Select select = (Select) statement;
+        PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
+
+        // 2. 提取所有被使用的表名(用内置工具类,更稳定)
+        Set<String> usedTables = getUsedTables(plainSelect);
+        System.out.println("被使用的表:" + usedTables); // 输出 [t1, t2]
+
+        // 3. 过滤冗余JOIN
+        List<Join> filteredJoins = new ArrayList<>();
+        if (plainSelect.getJoins() != null) {
+            for (Join join : plainSelect.getJoins()) {
+                if (join.getRightItem() instanceof Table) {
+                    Table joinTable = (Table) join.getRightItem();
+                    String tableName = joinTable.getName();
+                    if (usedTables.contains(tableName)) {
+                        filteredJoins.add(join);
+                    }
+                }
+            }
+        }
+
+        // 4. 重构SQL
+        plainSelect.setJoins(filteredJoins);
+        String optimizedSql = plainSelect.toString();
+        System.out.println("优化后的SQL:" + optimizedSql);
+        return optimizedSql;
+    }
+
+    /**
+     * 提取使用的表名(不依赖SelectExpressionItem)
+     */
+    private static Set<String> getUsedTables(PlainSelect plainSelect) {
+        Set<String> usedTables = new HashSet<>();
+        TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
+
+        // 1. 提取主表(FROM)
+        if (plainSelect.getFromItem() instanceof Table) {
+            Table fromTable = (Table) plainSelect.getFromItem();
+            usedTables.add(fromTable.getName());
+        }
+
+        // 2. 提取SELECT字段中的表(遍历Column,直接获取表名)
+        for (SelectItem item : plainSelect.getSelectItems()) {
+            // 直接遍历Item中的Column,绕过SelectExpressionItem
+            item.accept(new SelectItemVisitorAdapter() {
+                public void visit(Column column) {
+                    if (column.getTable() != null) {
+                        usedTables.add(column.getTable().getName());
+                    }
+                }
+            });
+        }
+
+        // 3. 提取WHERE条件中的表(用内置工具类)
+        if (plainSelect.getWhere() != null) {
+            List<String> whereTables = tablesNamesFinder.getTableList(plainSelect.getWhere());
+            usedTables.addAll(whereTables);
+        }
+
+        return usedTables;
+    }
+
+
+}

+ 116 - 74
websrc/com/kingdee/eas/custom/dataconfig/utils/SqlUtils.java

@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.kingdee.bos.Context;
 import com.kingdee.bos.metadata.data.ColumnInfo;
 import com.kingdee.bos.metadata.entity.*;
+import com.kingdee.bos.metadata.resource.BizEnumInfo;
 import com.kingdee.eas.custom.entryconfig.DataConfigSelectFieldEntryCollection;
 import com.kingdee.eas.custom.entryconfig.DataConfigSelectFieldEntryInfo;
 import com.kingdee.shr.base.syssetting.util.MetaDataUtil;
@@ -85,30 +86,31 @@ public class SqlUtils {
                 PropertyCollection propertiesChildren = entryInfo.getInheritedNoDuplicatedProperties();
 
                 // 遍历目标实体的属性集合(处理二级关联)
-                for (int j = 0; j < propertiesChildren.size(); j++) {
-                    PropertyInfo propertyChildren = propertiesChildren.get(j);
-                    // 判断二级属性是否为关联属性
-                    if (propertyChildren instanceof LinkPropertyInfo) {
-                        // 生成二级关联表的别名(一级关联表别名.二级属性名)
-                        String asTableChildren = name1 + "." + propertyChildren.getName();
-                        // 获取二级关联属性的映射字段
-                        ColumnInfo mappingFieldChildren = propertyChildren.getMappingField();
-                        // 如果映射字段为空,则跳过当前二级属性
-                        if (mappingFieldChildren == null) {
-                            continue;
-                        }
-                        // 获取二级关联属性指向的目标表名
-                        String tableChildren = ((LinkPropertyInfo) propertyChildren).getRelationship().getSupplierObject().getTable().getName();
-                        // 获取二级映射字段的名称(数据库列名)
-                        String mappingFieldNameChildren = mappingFieldChildren.getName();
-                        // 拼接二级LEFT JOIN语句(如:left join child_table as "asTableChildren")
-                        sql.append("left join ").append(tableChildren).append(" as ").append("\"" + asTableChildren + "\"").append("\n");
-                        // 拼接二级关联条件(一级关联表别名.二级映射列名 = 二级关联表别名.FID)
-                        sql.append("on \"" + name1 + "\".\"" + mappingFieldNameChildren + "\" = " + " \"" + asTableChildren + "\".\"FID" + "\" ").append("\n");
-
-                    }
-
-                }
+//                for (int j = 0; j < propertiesChildren.size(); j++) {
+//                    PropertyInfo propertyChildren = propertiesChildren.get(j);
+//                    // 判断二级属性是否为关联属性
+//                    if (propertyChildren instanceof LinkPropertyInfo) {
+//                        // 生成二级关联表的别名(一级关联表别名.二级属性名)
+//                        String asTableChildren = name1 + "." + propertyChildren.getName();
+//                        // 获取二级关联属性的映射字段
+//                        ColumnInfo mappingFieldChildren = propertyChildren.getMappingField();
+//                        // 如果映射字段为空,则跳过当前二级属性
+//                        if (mappingFieldChildren == null) {
+//                            continue;
+//                        }
+//                        // 获取二级关联属性指向的目标表名
+//                        String tableChildren = ((LinkPropertyInfo) propertyChildren).getRelationship().getSupplierObject().getTable().getName();
+//                        // 获取二级映射字段的名称(数据库列名)
+//                        String mappingFieldNameChildren = mappingFieldChildren.getName();
+//                        // 拼接二级LEFT JOIN语句(如:left join child_table as "asTableChildren")
+//                        sql.append("left join ").append(tableChildren).append(" as ").append("\"" + asTableChildren + "\"").append("\n");
+//                        // 拼接二级关联条件(一级关联表别名.二级映射列名 = 二级关联表别名.FID)
+//                        sql.append("on \"" + name1 + "\".\"" + mappingFieldNameChildren + "\" = " + " \"" + asTableChildren + "\".\"FID" + "\" ").append("\n");
+//
+//                    }
+//
+//                }
+//
             }
 
         }
@@ -148,6 +150,15 @@ public class SqlUtils {
                 currentJson.put("field", alias+"."+fieldName);
                 currentJson.put("name", fieldAlias);
                 currentJson.put("dataType", DataType.OBJECTVALUE.getName());
+                // 字段 linkProperty.getMappingField()
+                if (linkProperty.getMappingField()==null){
+                    continue;
+                }
+                String name2 = linkProperty.getMappingField().getName();
+                currentJson.put("mappingField",   "\"" + alias + "\"." +name2);
+                currentJson.put("asMappingField", "\"" + alias + "." + name2 + "\"");
+
+
 
                 // 获取关联属性的映射字段(数据库列)
                 ColumnInfo mappingField = linkProperty.getMappingField();
@@ -175,7 +186,6 @@ public class SqlUtils {
                     childJson.put("field", alias+"."+realName+"."+childProperty.getName());
                     childJson.put("name", childProperty.getAlias());
                     childJson.put("dataType", DataType.OBJECTVALUE.getName());
-
                     // 判断子属性是否为关联属性(二级关联)
                     if (childProperty instanceof LinkPropertyInfo) {
                         // 强制转换为LinkPropertyInfo
@@ -185,6 +195,12 @@ public class SqlUtils {
                         if (childMappingField == null) {
                             continue; // 若映射字段为空,则跳过当前子属性
                         }
+                        if (childLink.getMappingField()==null){
+                            continue;
+                        }
+                        String name3 = childLink.getMappingField().getName();
+                        childJson.put("mappingField",   "\"" + alias+childProperty.getAlias() + "\"." +name3);
+                        childJson.put("asMappingField", "\"" + alias+childProperty.getAlias() + "." + name3 + "\"");
 
                         // 获取二级关联关系信息
                         RelationshipInfo childRelationship = childLink.getRelationship();
@@ -200,55 +216,65 @@ public class SqlUtils {
                         EntityObjectInfo grandChildEntity = this.getEntryInfo(childEnumSource, context);
                         // 获取二级目标实体的所有非重复继承属性集合(孙属性)
                         PropertyCollection grandChildProperties = grandChildEntity.getInheritedNoDuplicatedProperties();
-                        JSONArray grandChildArray = new JSONArray(); // 存储孙属性信息的JSON数组
+                        //JSONArray grandChildArray = new JSONArray(); // 存储孙属性信息的JSON数组
 
                         // 遍历孙属性
-                        for (int k = 0; k < grandChildProperties.size(); k++) {
-                            PropertyInfo grandChildProperty = grandChildProperties.get(k);
-                            // 只处理自有属性(OwnPropertyInfo),跳过关联属性
-                            if (!(grandChildProperty instanceof OwnPropertyInfo)) {
-                                continue;
-                            }
-                            // 强制转换为OwnPropertyInfo
-                            OwnPropertyInfo ownGrandChild = (OwnPropertyInfo) grandChildProperty;
-                            JSONObject grandChildJson = new JSONObject();
-
-                            // 设置孙属性的基础信息
-                            grandChildJson.put("field",  alias+"."+realName+"."+childProperty.getName()+"."+ownGrandChild.getName());
-                            grandChildJson.put("name", ownGrandChild.getAlias());
-                            grandChildJson.put("dataType", ownGrandChild.getDataType().getName());
-
-                            // 若孙属性为枚举类型,设置枚举来源
-                            if (ownGrandChild.getDataType().getName().equals("Enum")) {
-                                grandChildJson.put("enumSource", ownGrandChild.getEnumType().getFullName());
-                                grandChildJson.put("enumData",  ownGrandChild.getEnumType().getEnumValue());
-                            }
 
-                            // 处理孙属性的数据库字段映射(考虑多语言)
-                            boolean isMultilingual = ownGrandChild.getMappingField().isMultilingual();
-                            // 构建父字段路径(一级关联别名.二级属性名)
-                            String parentFieldPath = fieldName + alias + "." + childProperty.getName();
-                            // 获取孙属性对应的数据库列名
-                            String dbFieldName = ownGrandChild.getMappingField().getName();
-                            String mappingFieldVal; // 数据库字段引用(如:"alias.field"."dbColumn")
-                            String asMappingFieldVal; // 字段别名(如:"alias.field.dbColumn")
-
-                            // 多语言字段拼接_l2后缀(可能为特定语言标识)
-                            if (isMultilingual) {
-                                mappingFieldVal = "\"" + parentFieldPath + "\"." + dbFieldName + "_l2";
-                                asMappingFieldVal = "\"" + parentFieldPath + "." + dbFieldName + "_l2\"";
-                            } else {
-                                mappingFieldVal = "\"" + parentFieldPath + "\"." + dbFieldName;
-                                asMappingFieldVal = "\"" + parentFieldPath + "." + dbFieldName + "\"";
-                            }
-                            grandChildJson.put("mappingField", mappingFieldVal);
-                            grandChildJson.put("asMappingField", asMappingFieldVal);
-
-                            // 将孙属性信息添加到孙属性数组
-                            grandChildArray.add(grandChildJson);
-                        }
+//                        for (int k = 0; k < grandChildProperties.size(); k++) {
+//                            PropertyInfo grandChildProperty = grandChildProperties.get(k);
+//                            // 只处理自有属性(OwnPropertyInfo),跳过关联属性
+//                            if (!(grandChildProperty instanceof OwnPropertyInfo)) {
+//                                continue;
+//                            }
+//                            // 强制转换为OwnPropertyInfo
+//                            OwnPropertyInfo ownGrandChild = (OwnPropertyInfo) grandChildProperty;
+//                            JSONObject grandChildJson = new JSONObject();
+//
+//                            // 设置孙属性的基础信息
+//                            grandChildJson.put("field",  alias+"."+realName+"."+childProperty.getName()+"."+ownGrandChild.getName());
+//                            grandChildJson.put("name", ownGrandChild.getAlias());
+//                            grandChildJson.put("dataType", ownGrandChild.getDataType().getName());
+//
+//                            // 若孙属性为枚举类型,设置枚举来源
+//                            if (ownGrandChild.getDataType().getName().equals("Enum")) {
+//                                BizEnumInfo enumType = ownGrandChild.getEnumType();
+//                                if (enumType==null){
+//                                    grandChildJson.put("enumSource",  ownGrandChild.getMetaDataRef());
+//                                }else{
+//                                    grandChildJson.put("enumSource", ownGrandChild.getEnumType().getFullName());
+//                                    grandChildJson.put("enumData",  ownGrandChild.getEnumType().getEnumValue());
+//                                }
+//                            }
+//
+//                            // 处理孙属性的数据库字段映射(考虑多语言)
+//                            boolean isMultilingual = ownGrandChild.getMappingField().isMultilingual();
+//                            // 构建父字段路径(一级关联别名.二级属性名)
+//                            String parentFieldPath = fieldName + alias + "." + childProperty.getName();
+//                            // 获取孙属性对应的数据库列名
+//                            if (ownGrandChild.getMappingField()==null){
+//                                continue;
+//                            }
+//                            String dbFieldName = ownGrandChild.getMappingField().getName();
+//                            String mappingFieldVal; // 数据库字段引用(如:"alias.field"."dbColumn")
+//                            String asMappingFieldVal; // 字段别名(如:"alias.field.dbColumn")
+//
+//                            // 多语言字段拼接_l2后缀(可能为特定语言标识)
+//                            if (isMultilingual) {
+//                                mappingFieldVal = "\"" + parentFieldPath + "\"." + dbFieldName + "_l2";
+//                                asMappingFieldVal = "\"" + parentFieldPath + "." + dbFieldName + "_l2\"";
+//                            } else {
+//                                mappingFieldVal = "\"" + parentFieldPath + "\"." + dbFieldName;
+//                                asMappingFieldVal = "\"" + parentFieldPath + "." + dbFieldName + "\"";
+//                            }
+//                            grandChildJson.put("mappingField", mappingFieldVal);
+//                            grandChildJson.put("asMappingField", asMappingFieldVal);
+//
+//                            // 将孙属性信息添加到孙属性数组
+//                            grandChildArray.add(grandChildJson);
+//                        }
+//
                         // 将孙属性数组添加到当前子属性的JSON中
-                        childJson.put("children", grandChildArray);
+                        //childJson.put("children", grandChildArray);
 
                     } else if (childProperty instanceof OwnPropertyInfo) {
                         // 子属性为自有属性(OwnPropertyInfo)
@@ -258,13 +284,21 @@ public class SqlUtils {
 
                         // 若子属性为枚举类型,设置枚举来源
                         if (ownChild.getDataType().getName().equals("Enum")) {
-                            childJson.put("enumSource", ownChild.getEnumType().getFullName());
-                            childJson.put("enumData",  ownChild.getEnumType().getEnumValue());
+                            BizEnumInfo enumType = ownChild.getEnumType();
+                            if (enumType==null){
+                                childJson.put("enumSource",  ownChild.getMetaDataRef());
+                            }else{
+                                childJson.put("enumSource", ownChild.getEnumType().getFullName());
+                                childJson.put("enumData",  ownChild.getEnumType().getEnumValue());
+                            }
                         }
 
                         // 处理子属性的数据库字段映射(考虑多语言)
                         boolean isMultilingual = ownChild.getMappingField().isMultilingual();
                         // 获取子属性对应的数据库列名
+                        if (ownChild.getMappingField()==null){
+                            continue;
+                        }
                         String dbFieldName = ownChild.getMappingField().getName();
                         String mappingFieldVal; // 数据库字段引用
                         String asMappingFieldVal; // 字段别名
@@ -294,6 +328,9 @@ public class SqlUtils {
                 // 当前属性为自有属性(OwnPropertyInfo)
                 OwnPropertyInfo ownProperty = (OwnPropertyInfo) propertyInfo;
                 String fieldName = ownProperty.getName(); // 属性名称
+                if (ownProperty.getMappingField()==null){
+                    continue;
+                }
                 String dbFieldName = ownProperty.getMappingField().getName(); // 对应的数据库列名
 
                 // 设置自有属性的基础信息
@@ -303,8 +340,13 @@ public class SqlUtils {
 
                 // 若自有属性为枚举类型,设置枚举来源
                 if (ownProperty.getDataType().getName().equals("Enum")) {
-                    currentJson.put("enumSource", ownProperty.getEnumType().getFullName());
-                    currentJson.put("enumData",  ownProperty.getEnumType().getEnumValue());
+                    BizEnumInfo enumType = ownProperty.getEnumType();
+                    if (enumType==null){
+                        currentJson.put("enumSource",  ownProperty.getMetaDataRef());
+                    }else{
+                        currentJson.put("enumSource", ownProperty.getEnumType().getFullName());
+                        currentJson.put("enumData",  ownProperty.getEnumType().getEnumValue());
+                    }
                 }
 
                 // 处理自有属性的数据库字段映射(考虑多语言)

+ 2 - 1
websrc/com/kingdee/eas/custom/dataconfig/utils/service/GetCompleteSqlService.java

@@ -4,11 +4,13 @@ import com.kingdee.bos.BOSException;
 import com.kingdee.bos.Context;
 import com.kingdee.bos.bsf.service.app.IHRMsfService;
 import com.kingdee.eas.common.EASBizException;
+import com.kingdee.eas.custom.dataconfig.utils.NativeSqlJoinOptimizer;
 import com.kingdee.eas.custom.dataconfig.utils.SqlUtils;
 import com.kingdee.eas.custom.entryconfig.DataConfigFactory;
 import com.kingdee.eas.custom.entryconfig.DataConfigInfo;
 import com.kingdee.eas.custom.entryconfig.DataConfigSelectFieldEntryCollection;
 import com.kingdee.shr.base.syssetting.MSFServiceFacadeFactory;
+import net.sf.jsqlparser.JSQLParserException;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -40,7 +42,6 @@ public class GetCompleteSqlService implements IHRMsfService {
             field.append("top 1").append("\n");
         }
         StringBuilder selectField = sqlUtils.getSelectField(selectFieldEntry, field);
-
         Map<String,Object> getSqlServiceMap = new HashMap<String,Object>();
         getSqlServiceMap.put("number",number);
         Object o1 = MSFServiceFacadeFactory.getLocalInstance(context).processService("getSqlService", getSqlServiceMap);

+ 1 - 0
websrc/com/kingdee/eas/custom/dataconfig/utils/service/GetSqlService.java

@@ -7,6 +7,7 @@ import com.kingdee.bos.metadata.entity.EntityObjectInfo;
 import com.kingdee.bos.metadata.entity.FilterInfo;
 import com.kingdee.bos.sql.ParserException;
 import com.kingdee.eas.common.EASBizException;
+import com.kingdee.eas.custom.dataconfig.utils.NativeSqlJoinOptimizer;
 import com.kingdee.eas.custom.dataconfig.utils.SqlUtils;
 import com.kingdee.eas.custom.entryconfig.*;
 import com.kingdee.eas.custom.entryconfig.enumlist.RelationshipEnum;

+ 44 - 0
websrc/com/kingdee/eas/custom/esign/contract/ContractCompositionQueryHandlerEx.java

@@ -0,0 +1,44 @@
+package com.kingdee.eas.custom.esign.contract;
+
+import com.alibaba.fastjson.JSONObject;
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.Context;
+import com.kingdee.eas.common.EASBizException;
+import com.kingdee.shr.base.syssetting.MSFServiceFacadeFactory;
+import com.kingdee.shr.base.syssetting.context.SHRContext;
+import com.kingdee.shr.base.syssetting.exception.SHRWebException;
+import com.kingdee.shr.base.syssetting.web.json.JSONUtils;
+import com.kingdee.shr.batchContract.web.handler.ContractCompositionQueryHandler;
+import org.springframework.ui.ModelMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ContractCompositionQueryHandlerEx extends ContractCompositionQueryHandler {
+    public String getTableDataAction(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) throws SHRWebException {
+        Context ctx = SHRContext.getInstance().getContext();
+        String id = request.getParameter("id");
+        String number = request.getParameter("number");
+        try {
+            Map<String,Object> getSqlServiceMap = new HashMap<String,Object>();
+            getSqlServiceMap.put("number",number);
+            getSqlServiceMap.put("filter","  where \"employeeContract\".FID = '"+id+"'");
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("id",id);
+            getSqlServiceMap.put("otherParameters",jsonObject);
+            Object o2 = MSFServiceFacadeFactory.getLocalInstance(ctx)
+                    .processService("getESignConfigTableService", getSqlServiceMap);
+            Map<String,Object> map = new HashMap<String,Object>();
+            map.put("code",200);
+            map.put("data",o2);
+            JSONUtils.SUCCESS(map);
+        } catch (BOSException e) {
+            throw new RuntimeException(e);
+        } catch (EASBizException e) {
+            throw new RuntimeException(e);
+        }
+        return null;
+    }
+}

+ 44 - 0
websrc/com/kingdee/eas/custom/esign/handler/EmployeeListHandlerESignEx.java

@@ -0,0 +1,44 @@
+package com.kingdee.eas.custom.esign.handler;
+
+import com.alibaba.fastjson.JSONObject;
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.Context;
+import com.kingdee.eas.common.EASBizException;
+import com.kingdee.eas.custom.beisen.syncperson.handler.EmployeeListHandlerEx;
+import com.kingdee.shr.base.syssetting.MSFServiceFacadeFactory;
+import com.kingdee.shr.base.syssetting.context.SHRContext;
+import com.kingdee.shr.base.syssetting.exception.SHRWebException;
+import com.kingdee.shr.base.syssetting.web.json.JSONUtils;
+import org.springframework.ui.ModelMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
+import java.util.Map;
+
+public class EmployeeListHandlerESignEx extends EmployeeListHandlerEx {
+    public String getTableDataAction(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) throws SHRWebException {
+        Context ctx = SHRContext.getInstance().getContext();
+        String id = request.getParameter("id");
+        String number = request.getParameter("number");
+        try {
+            Map<String,Object> getSqlServiceMap = new HashMap<String,Object>();
+            getSqlServiceMap.put("number",number);
+            getSqlServiceMap.put("filter","  where \"employeeemployeeContract\".FID = '"+id+"'");
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("id",id);
+            getSqlServiceMap.put("otherParameters",jsonObject);
+            Object o2 = MSFServiceFacadeFactory.getLocalInstance(ctx)
+                    .processService("getESignConfigTableService", getSqlServiceMap);
+            Map<String,Object> map = new HashMap<String,Object>();
+            map.put("code",200);
+            map.put("data",o2);
+            JSONUtils.SUCCESS(map);
+        } catch (BOSException e) {
+            throw new RuntimeException(e);
+        } catch (EASBizException e) {
+            throw new RuntimeException(e);
+        }
+        return null;
+    }
+}

+ 44 - 0
websrc/com/kingdee/eas/custom/esign/handler/GeneralESignBillHandler.java

@@ -0,0 +1,44 @@
+package com.kingdee.eas.custom.esign.handler;
+
+import com.alibaba.fastjson.JSONObject;
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.Context;
+import com.kingdee.eas.common.EASBizException;
+import com.kingdee.shr.base.syssetting.MSFServiceFacadeFactory;
+import com.kingdee.shr.base.syssetting.context.SHRContext;
+import com.kingdee.shr.base.syssetting.exception.SHRWebException;
+import com.kingdee.shr.base.syssetting.web.handler.ListHandler;
+import com.kingdee.shr.base.syssetting.web.json.JSONUtils;
+import org.springframework.ui.ModelMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
+import java.util.Map;
+
+public class GeneralESignBillHandler extends ListHandler {
+    public String getTableDataAction(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) throws SHRWebException {
+        Context ctx = SHRContext.getInstance().getContext();
+        String id = request.getParameter("id");
+        String number = request.getParameter("number");
+        try {
+            Map<String,Object> getSqlServiceMap = new HashMap<String,Object>();
+            getSqlServiceMap.put("number",number);
+            getSqlServiceMap.put("filter","  where \"employeeemployeeContract\".FID = '"+id+"'");
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("id",id);
+            getSqlServiceMap.put("otherParameters",jsonObject);
+            Object o2 = MSFServiceFacadeFactory.getLocalInstance(ctx)
+                    .processService("getESignConfigTableService", getSqlServiceMap);
+            Map<String,Object> map = new HashMap<String,Object>();
+            map.put("code",200);
+            map.put("data",o2);
+            JSONUtils.SUCCESS(map);
+        } catch (BOSException e) {
+            throw new RuntimeException(e);
+        } catch (EASBizException e) {
+            throw new RuntimeException(e);
+        }
+        return null;
+    }
+}

+ 338 - 0
websrc/com/kingdee/eas/custom/esign/service/GetESignConfigDataService.java

@@ -0,0 +1,338 @@
+package com.kingdee.eas.custom.esign.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.Context;
+import com.kingdee.bos.bsf.service.app.IHRMsfService;
+import com.kingdee.bos.metadata.access.Simple;
+import com.kingdee.bos.metadata.entity.EntityViewInfo;
+import com.kingdee.bos.metadata.entity.FilterInfo;
+import com.kingdee.bos.metadata.entity.FilterItemInfo;
+import com.kingdee.bos.metadata.entity.SelectorItemCollection;
+import com.kingdee.bos.metadata.query.util.CompareType;
+import com.kingdee.bos.sql.ParserException;
+import com.kingdee.bos.util.BOSUuid;
+import com.kingdee.eas.common.EASBizException;
+import com.kingdee.eas.custom.dataconfig.utils.SqlUtils;
+import com.kingdee.eas.custom.entryconfig.DataConfigInfo;
+import com.kingdee.eas.custom.entryconfig.DataConfigSelectFieldEntryCollection;
+import com.kingdee.eas.custom.entryconfig.DataConfigSelectFieldEntryInfo;
+import com.kingdee.eas.custom.esign.*;
+import com.kingdee.eas.custom.esign.bizEnum.ComponentTypeEnum;
+import com.kingdee.eas.custom.esign.utils.ESignFieldMappingUtils;
+import com.kingdee.eas.fi.arap.util.DBUtil;
+import com.kingdee.jdbc.rowset.IRowSet;
+
+import java.sql.SQLException;
+import java.text.SimpleDateFormat;
+import java.util.Map;
+import java.util.logging.SimpleFormatter;
+
+/**
+ * 获取电子签配置数据的服务类
+ * 实现IHRMsfService接口,用于处理电子签配置数据的查询与封装
+ */
+public class GetESignConfigDataService implements IHRMsfService {
+    /**
+     * 处理请求的核心方法
+     * @param context 上下文对象,包含环境信息
+     * @param map 参数映射,包含请求的参数
+     * @return 处理后的电子签配置数据,以JSONObject形式返回
+     * @throws EASBizException EAS业务异常
+     * @throws BOSException BOS框架异常
+     */
+    @Override
+    public Object process(Context context, Map map) throws EASBizException, BOSException {
+        // 从参数映射中获取"number"参数值,用于定位配置信息
+        Object o = map.get("number");
+        // 从参数映射中获取"filter"参数值,用于构建查询过滤条件
+        Object o1 = map.get("filter");
+        // 若"number"参数为null,返回空字符串(无效请求)
+        if(o==null){
+            return "";
+        }
+        // 将"number"参数转换为字符串类型
+        String number = o.toString();
+        // 将"filter"参数转换为字符串类型(若为null则转换为"null"字符串)
+        String filter = o1.toString();
+        // 声明实体视图信息对象,用于查询时的字段映射配置
+        EntityViewInfo entityViewInfo= null;
+        try {
+            // 调用方法获取实体视图信息,该视图包含查询所需的字段映射配置
+            entityViewInfo = this.getEntityViewInfo(number);
+        } catch (ParserException e) {
+            // 解析异常时,封装为运行时异常抛出
+            throw new RuntimeException(e);
+        }
+        // 通过字段映射工厂,根据实体视图信息查询对应的字段映射集合
+        FieldMappingCollection fieldMappingCollection = FieldMappingFactory.
+                getLocalInstance(context).getFieldMappingCollection(entityViewInfo);
+        // 创建SQL工具类实例,用于SQL语句的构建辅助
+        SqlUtils sqlUtils = new SqlUtils();
+        // 创建电子签字段映射工具类实例,用于字段映射的核心处理
+        ESignFieldMappingUtils utils = new ESignFieldMappingUtils();
+        // 获取字段映射集合中的第一个映射信息(默认取第一个配置项)
+        FieldMappingInfo fieldMappingInfo = fieldMappingCollection.get(0);
+        // 获取当前字段映射信息中的明细项集合(包含具体的字段映射关系)
+        FieldMappingEntryCollection entrys = fieldMappingInfo.getEntrys();
+        // 获取当前字段映射信息对应的数据源配置
+        DataConfigInfo dataSource = fieldMappingInfo.getDataSource();
+
+        // 验证数据源配置是否有效(非空且编号存在)
+        if (dataSource!=null&&dataSource.getNumber()!=null&&!dataSource.getNumber().equals("")){
+            // 通过工具类从字段映射明细中获取电子签SQL所需的字段集合
+            DataConfigSelectFieldEntryCollection eSignSqlField = utils.getESignSqlField(entrys);
+            // 创建字符串构建器,用于拼接SQL查询的字段部分
+            StringBuilder sqlField = new StringBuilder();
+            // 调用SQL工具类,将电子签字段集合拼接为SQL的SELECT子句
+            sqlUtils.getSelectField(eSignSqlField, sqlField);
+
+            // 获取数据源配置的编号
+            String number1 = dataSource.getNumber();
+            // 获取当前字段映射信息对应的电子签模板信息
+            ESignTemplateInfo eSignFile = fieldMappingInfo.getESignFile();
+            if(sqlField.toString().equals("")){
+                JSONObject jsonObject = this.reassembleData(eSignFile, context);
+                return jsonObject;
+            }
+            // 通过工具类根据数据源编号和上下文获取电子签基础查询SQL
+            String eSignSql = utils.getESignSql(number1, context);
+            // 替换SQL中的{{filed}}占位符为实际拼接的查询字段
+            eSignSql = eSignSql.replace("{{filed}}",sqlField.toString());
+            // 在SQL末尾添加过滤条件(换行分隔,便于阅读)
+            eSignSql += "\n"+filter;
+            // 执行SQL查询并将结果封装为JSON对象(包含字段映射关系)
+            JSONObject data = this.getData(eSignSql, context, entrys);
+            // 重新组装电子签模板数据为JSON对象
+            JSONObject jsonObject = this.reassembleData(eSignFile, context);
+            // 合并查询数据与模板数据,返回合并后的JSON对象
+            JSONObject newJSONObject = utils.mergeAgreements(data, jsonObject);
+            return newJSONObject;
+
+        }else {
+            // 若数据源无效,直接获取电子签模板信息
+            ESignTemplateInfo eSignFile = fieldMappingInfo.getESignFile();
+            // 重新组装模板数据并返回
+            JSONObject jsonObject = this.reassembleData(eSignFile, context);
+            return jsonObject;
+        }
+
+
+
+    }
+
+    /**
+     * 执行SQL查询并将结果封装为JSON对象
+     * @param sql 要执行的查询SQL
+     * @param context 上下文对象
+     * @param entrys 字段映射明细集合
+     * @return 封装后的JSON对象,包含电子签模板各字段信息
+     */
+    public JSONObject getData(String sql,Context context,FieldMappingEntryCollection entrys){
+        // 创建JSON对象用于存储最终封装的数据
+        JSONObject jsonObject = new JSONObject();
+        try {
+            // 执行SQL查询,获取结果集
+            IRowSet iRowSet1 = DBUtil.executeQuery(context, sql);
+            // 遍历结果集中的每一行数据
+            while (iRowSet1.next()){
+                // 遍历每个字段映射明细项
+                for (int i = 0; i <entrys.size(); i++) {
+                    // 获取当前索引对应的字段映射明细信息
+                    FieldMappingEntryInfo fieldMappingEntryInfo = entrys.get(i);
+                    // 获取明细项中关联的电子签模板字段信息
+                    ESignTemplateFileEntryFieldInfo eField = fieldMappingEntryInfo.getEField();
+                    // 判断该字段是否无效(已废弃)
+                    boolean invalid = eField.isInvalid();
+                    // 若字段无效,跳过当前循环(不处理)
+                    if (invalid){
+                        continue;
+                    }
+                    // 获取明细项中关联的电子签模板明细信息
+                    ESignTemplateFileEntryInfo template = fieldMappingEntryInfo.getTemplate();
+                    // 获取电子签模板的ID
+                    String eSignTemplateId = template.getESignTemplateId();
+                    // 从JSON对象中获取该模板ID对应的子JSON对象
+                    JSONObject  templateJSON = jsonObject.getJSONObject(eSignTemplateId);
+                    // 若模板对应的子JSON对象不存在,则创建并初始化(设置ID和名称)
+                    if (templateJSON==null){
+                        templateJSON = new JSONObject();
+                        templateJSON.put("id",eSignTemplateId); // 设置模板ID
+                        templateJSON.put("name",template.getName()); // 设置模板名称
+                        jsonObject.put(eSignTemplateId,templateJSON); // 将模板信息存入主JSON对象
+                    }
+                    // 从模板子JSON对象中获取字段信息对应的子JSON对象
+                    JSONObject fields = templateJSON.getJSONObject("fields");
+                    // 若字段信息子JSON对象不存在,则创建并添加到模板子JSON中
+                    if (fields == null){
+                        fields = new JSONObject();
+                        templateJSON.put("fields",fields);
+                    }
+                    // 获取电子签模板字段的ID
+                    String templateFieldId = eField.getTemplateFieldId();
+                    // 创建用于存储字段值信息的JSON对象
+                    JSONObject value = new JSONObject();
+                    value.put("name",eField.getTemplateFieldName()); // 设置字段名称
+                    // 获取明细项中关联的数据源字段信息
+                    DataConfigSelectFieldEntryInfo dataSourceField = fieldMappingEntryInfo.getDataSourceField();
+                    if (dataSourceField==null){
+                        value.put("value","");
+                        value.put("dataFormat", eField.getDataFormat());
+                    }else {
+                        // 获取数据源字段的映射键(对应数据库列名)
+                        String name = dataSourceField.getMappingKey();
+                        // 去除映射键中的双引号(处理可能的格式问题)
+                        name = name.replace("\"","");
+                        // 从结果集中获取该字段对应的值,并存入字段值JSON对象
+                        value.put("value",iRowSet1.getObject(name));
+                        // 设置字段的数据类型(取自数据源字段的类型别名)
+                        value.put("dataType",dataSourceField.getDataType().getAlias());
+                        if (dataSourceField.getDataType().getAlias().equals("Date")){
+                            Object object = iRowSet1.getObject(name);
+                            if(object!=null){
+                                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
+                                value.put("value",simpleDateFormat.format(iRowSet1.getDate(name)));
+                            }
+                            value.put("dataFormat", eField.getDataFormat());
+                        }
+                    }
+                    if (eField.getComponentType().getValue()==6||eField.getComponentType().getValue()==106){
+                        value.put("positionX",eField.getPositionX());
+                        value.put("positionY",eField.getPositionY());
+                        value.put("pageNum",eField.getPageNum());
+                    }
+                    // 将字段值信息存入字段信息子JSON对象(以模板字段ID为键)
+                    fields.put(templateFieldId,value);
+                }
+            }
+
+        } catch (BOSException e) {
+            // BOS异常时,封装为运行时异常抛出
+            throw new RuntimeException(e);
+        } catch (SQLException e) {
+            // SQL异常时,封装为运行时异常抛出
+            throw new RuntimeException(e);
+        }
+        // 返回封装好的JSON对象
+        return jsonObject;
+    }
+
+    /**
+     * 构建查询字段映射信息的实体视图
+     * @param number 字段映射的编号,用于过滤
+     * @return 构建好的实体视图信息
+     * @throws ParserException 解析异常
+     */
+    public EntityViewInfo  getEntityViewInfo(String number) throws ParserException {
+        // 创建实体视图信息对象
+        EntityViewInfo entityViewInfo = new EntityViewInfo();
+        // 创建选择项集合,用于指定查询返回的字段
+        SelectorItemCollection selectorItemCollection = new SelectorItemCollection();
+        selectorItemCollection.add("*"); // 选择所有基础字段
+        selectorItemCollection.add("eSignFile.*"); // 选择关联的电子签文件的所有字段
+        selectorItemCollection.add("dataSource.*"); // 选择关联的数据源的所有字段
+        selectorItemCollection.add("entrys.*"); // 选择关联的明细项的所有字段
+        selectorItemCollection.add("entrys.eField.*"); // 选择明细项中电子签字段的所有字段
+        selectorItemCollection.add("entrys.dataSourceField.*"); // 选择明细项中数据源字段的所有字段
+        selectorItemCollection.add("entrys.template.*"); // 选择明细项中模板的所有字段
+        // 为实体视图设置选择项(指定查询返回的字段集)
+        entityViewInfo.setSelector(selectorItemCollection);
+        // 创建过滤信息对象,用于设置查询条件
+        FilterInfo filterInfo = new FilterInfo();
+        // 添加过滤条件:字段映射的编号等于传入的number
+        filterInfo.getFilterItems().add(new FilterItemInfo("number",number));
+        // 为实体视图设置过滤条件
+        entityViewInfo.setFilter(filterInfo);
+        // 返回构建好的实体视图信息
+        return entityViewInfo;
+    }
+
+    /**
+     * 重新组装数据(当前未实现完整逻辑,仅基础框架)
+     * @param eSignFile 电子签模板信息
+     * @param context 上下文对象
+     * @return 重新组装后的JSON对象(数据可能为null)
+     */
+    public JSONObject reassembleData(ESignTemplateInfo eSignFile,Context context){
+        // 获取电子签模板的ID
+        BOSUuid id = eSignFile.getId();
+        // 声明用于存储组装后数据的JSON对象
+        JSONObject dataJSON ;
+        try {
+            // 初始化数据JSON对象
+            dataJSON = new JSONObject();
+            // 创建实体视图信息对象
+            EntityViewInfo entityViewInfo = new EntityViewInfo();
+            // 创建选择项集合,指定查询返回的字段
+            SelectorItemCollection selectorItemCollection = new SelectorItemCollection();
+            selectorItemCollection.add("*"); // 选择所有基础字段
+            selectorItemCollection.add("Parent1.*"); // 选择父级对象的所有字段
+            // 为实体视图设置选择项
+            entityViewInfo.setSelector(selectorItemCollection);
+            // 创建过滤信息对象
+            FilterInfo filterInfo = new FilterInfo();
+            // 添加过滤条件:字段未失效(Invalid为false)
+            filterInfo.getFilterItems().add(new FilterItemInfo("Invalid",1, CompareType.NOTEQUALS));
+            // 添加过滤条件:父级的父级ID等于电子签模板ID
+            filterInfo.getFilterItems().add(new FilterItemInfo("Parent1.Parent.id",id));
+            // 为实体视图设置过滤条件
+            entityViewInfo.setFilter(filterInfo);
+            // 根据实体视图查询电子签模板字段明细集合
+            ESignTemplateFileEntryFieldCollection eFields = ESignTemplateFileEntryFieldFactory.
+                    getLocalInstance(context).getESignTemplateFileEntryFieldCollection(entityViewInfo);
+            // 遍历字段明细集合
+            for (int i = 0; i < eFields.size(); i++) {
+
+                // 获取当前索引对应的电子签模板字段明细
+                ESignTemplateFileEntryFieldInfo eFile = eFields.get(i);
+                // 获取该字段明细的父级对象(模板明细)
+                ESignTemplateFileEntryInfo parent1 = eFile.getParent1();
+                // 获取电子签模板ID
+                String eSignTemplateId = parent1.getESignTemplateId();
+                // 获取电子签模板名称
+                String name = parent1.getName();
+                // 从数据JSON中获取模板ID对应的子JSON对象
+                JSONObject signTemplate = dataJSON.getJSONObject(eSignTemplateId);
+                // 若模板子JSON对象不存在,则创建并初始化
+                if (signTemplate == null){
+                    signTemplate = new JSONObject();
+                    signTemplate.put("name",name); // 设置模板名称
+                    signTemplate.put("id",eSignTemplateId); // 设置模板ID
+                    JSONObject fields = new JSONObject(); // 创建字段信息JSON对象
+                    signTemplate.put("fields",fields); // 将字段信息存入模板JSON
+                    dataJSON.put(eSignTemplateId,signTemplate);
+                }
+                // 从模板子JSON中获取字段信息JSON对象
+                JSONObject fields = signTemplate.getJSONObject("fields");
+                // 获取模板字段ID
+                String templateFieldId = eFile.getTemplateFieldId();
+                // 从字段信息JSON中获取该字段ID对应的子JSON对象
+                JSONObject field = fields.getJSONObject(templateFieldId);
+                // 若字段子JSON对象不存在,则创建并添加到字段信息JSON
+                if (field==null){
+                    field = new JSONObject();
+                    fields.put(templateFieldId,field);
+                }
+                // 获取模板字段名称
+                String templateFieldName = eFile.getTemplateFieldName();
+                // 设置字段名称到字段子JSON
+                field.put("name",templateFieldName);
+                // 获取字段组件类型
+                ComponentTypeEnum componentType = eFile.getComponentType();
+                // 设置字段数据类型(取自组件类型的别名)
+                field.put("dataType",componentType.getAlias());
+                field.put("dataFormat",eFile.getDataFormat());
+                field.put("isRequired",eFile.isRequired());
+                field.put("seq",eFile.getSeq());
+                // 字段值暂设为null
+                field.put("value",null);
+            }
+
+        } catch (BOSException e) {
+            // BOS异常时,封装为运行时异常抛出
+            throw new RuntimeException(e);
+        }
+        // 返回重新组装后的JSON对象
+        return dataJSON;
+    }
+}

+ 55 - 0
websrc/com/kingdee/eas/custom/esign/service/GetESignConfigTableService.java

@@ -0,0 +1,55 @@
+package com.kingdee.eas.custom.esign.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.Context;
+import com.kingdee.bos.bsf.service.app.IHRMsfService;
+import com.kingdee.eas.common.EASBizException;
+import com.kingdee.eas.custom.esign.FieldMappingFactory;
+import com.kingdee.eas.custom.esign.FieldMappingInfo;
+import com.kingdee.shr.base.syssetting.MSFServiceFacadeFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class GetESignConfigTableService implements IHRMsfService {
+    @Override
+    public Object process(Context context, Map map) throws EASBizException, BOSException {
+        // 从参数映射中获取"number"参数值,用于定位配置信息
+        Object o = map.get("number");
+        // 从参数映射中获取"filter"参数值,用于构建查询过滤条件
+        Object o1 = map.get("filter");
+
+        Object otherParameters = map.get("otherParameters");
+        // 若"number"参数为null,返回空字符串(无效请求)
+        if(o==null){
+            return "";
+        }
+        // 若"number"参数为null,返回空字符串(无效请求)
+        if(o1==null){
+            return "";
+        }
+        FieldMappingInfo fieldMappingInfo = FieldMappingFactory.getLocalInstance(context)
+                .getFieldMappingInfo("where number = '" + o.toString() + "' ");
+
+        String dataInterface = fieldMappingInfo.getDataInterface();
+        if (dataInterface==null||dataInterface.equals("")){
+            Map<String,Object> getSqlServiceMap = new HashMap<String,Object>();
+            getSqlServiceMap.put("number",fieldMappingInfo.getNumber());
+            getSqlServiceMap.put("filter",o1.toString());
+            Object o2 = MSFServiceFacadeFactory.getLocalInstance(context)
+                    .processService("getESignConfigDataService", getSqlServiceMap);
+            return o2;
+        }else {
+            JSONObject jsonObject = JSONObject.parseObject(otherParameters.toString());
+            Map<String,Object> osfMap = new HashMap<String, Object>();
+            for (String key : jsonObject.keySet()) {
+                osfMap.put(key,jsonObject.getString(key));
+            }
+            osfMap.put("number",fieldMappingInfo.getNumber());
+            Object o2 = MSFServiceFacadeFactory.getLocalInstance(context)
+                    .processService(dataInterface, osfMap);
+            return o2;
+        }
+    }
+}

+ 364 - 0
websrc/com/kingdee/eas/custom/esign/service/OtherESignConfigDataService.java

@@ -0,0 +1,364 @@
+package com.kingdee.eas.custom.esign.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.Context;
+import com.kingdee.bos.bsf.service.app.IHRMsfService;
+import com.kingdee.bos.metadata.entity.EntityViewInfo;
+import com.kingdee.bos.metadata.entity.FilterInfo;
+import com.kingdee.bos.metadata.entity.FilterItemInfo;
+import com.kingdee.bos.metadata.entity.SelectorItemCollection;
+import com.kingdee.bos.metadata.query.util.CompareType;
+import com.kingdee.bos.sql.ParserException;
+import com.kingdee.bos.util.BOSUuid;
+import com.kingdee.eas.common.EASBizException;
+import com.kingdee.eas.custom.dataconfig.utils.SqlUtils;
+import com.kingdee.eas.custom.entryconfig.DataConfigInfo;
+import com.kingdee.eas.custom.entryconfig.DataConfigSelectFieldEntryCollection;
+import com.kingdee.eas.custom.entryconfig.DataConfigSelectFieldEntryInfo;
+import com.kingdee.eas.custom.esign.*;
+import com.kingdee.eas.custom.esign.bizEnum.ComponentTypeEnum;
+import com.kingdee.eas.custom.esign.utils.ESignFieldMappingUtils;
+import com.kingdee.eas.fi.arap.util.DBUtil;
+import com.kingdee.jdbc.rowset.IRowSet;
+
+import java.sql.SQLException;
+import java.text.SimpleDateFormat;
+import java.util.Map;
+
+public class OtherESignConfigDataService implements IHRMsfService {
+    @Override
+    public Object process(Context context, Map map) throws EASBizException, BOSException {
+        // 从参数映射中获取"number"参数值,用于定位配置信息
+        Object o = map.get("number");
+        // 从参数映射中获取"filter"参数值,用于构建查询过滤条件
+        Object id = map.get("id");
+        // 若"number"参数为null,返回空字符串(无效请求)
+        if (o == null) {
+            return "";
+        }
+        // 将"number"参数转换为字符串类型
+        String number = o.toString();
+        // 将"filter"参数转换为字符串类型(若为null则转换为"null"字符串)
+        String filter = id.toString();
+        // 声明实体视图信息对象,用于查询时的字段映射配置
+        EntityViewInfo entityViewInfo = null;
+        try {
+            // 调用方法获取实体视图信息,该视图包含查询所需的字段映射配置
+            entityViewInfo = this.getEntityViewInfo(number);
+        } catch (ParserException e) {
+            // 解析异常时,封装为运行时异常抛出
+            throw new RuntimeException(e);
+        }
+        // 通过字段映射工厂,根据实体视图信息查询对应的字段映射集合
+        FieldMappingCollection fieldMappingCollection = FieldMappingFactory.
+                getLocalInstance(context).getFieldMappingCollection(entityViewInfo);
+        // 创建SQL工具类实例,用于SQL语句的构建辅助
+        //SqlUtils sqlUtils = new SqlUtils();
+        // 创建电子签字段映射工具类实例,用于字段映射的核心处理
+        ESignFieldMappingUtils utils = new ESignFieldMappingUtils();
+        // 获取字段映射集合中的第一个映射信息(默认取第一个配置项)
+        FieldMappingInfo fieldMappingInfo = fieldMappingCollection.get(0);
+        // 获取当前字段映射信息中的明细项集合(包含具体的字段映射关系)
+        FieldMappingEntryCollection entrys = fieldMappingInfo.getEntrys();
+        // 获取当前字段映射信息对应的数据源配置
+        //DataConfigInfo dataSource = fieldMappingInfo.getDataSource();
+
+        // 验证数据源配置是否有效(非空且编号存在)
+        //if (dataSource != null && dataSource.getNumber() != null && !dataSource.getNumber().equals("")) {
+        // 通过工具类从字段映射明细中获取电子签SQL所需的字段集合
+        //DataConfigSelectFieldEntryCollection eSignSqlField = utils.getESignSqlField(entrys);
+        // 创建字符串构建器,用于拼接SQL查询的字段部分
+        //StringBuilder sqlField = new StringBuilder();
+        // 调用SQL工具类,将电子签字段集合拼接为SQL的SELECT子句
+        //sqlUtils.getSelectField(eSignSqlField, sqlField);
+
+        // 获取数据源配置的编号
+        //String number1 = dataSource.getNumber();
+        // 获取当前字段映射信息对应的电子签模板信息
+        ESignTemplateInfo eSignFile = fieldMappingInfo.getESignFile();
+//        if (sqlField.toString().equals("")) {
+//            JSONObject jsonObject = this.reassembleData(eSignFile, context);
+//            return jsonObject;
+//        }
+        // 通过工具类根据数据源编号和上下文获取电子签基础查询SQL
+        //String eSignSql = utils.getESignSql(number1, context);
+        // 替换SQL中的{{filed}}占位符为实际拼接的查询字段
+        //eSignSql = eSignSql.replace("{{filed}}",sqlField.toString());
+        // 在SQL末尾添加过滤条件(换行分隔,便于阅读)
+        //eSignSql += "\n"+filter;
+        // 执行SQL查询并将结果封装为JSON对象(包含字段映射关系)
+        JSONObject data = this.getData(getSql(filter), context, entrys);
+        // 重新组装电子签模板数据为JSON对象
+        JSONObject jsonObject = this.reassembleData(eSignFile, context);
+        // 合并查询数据与模板数据,返回合并后的JSON对象
+        JSONObject newJSONObject = utils.mergeAgreements(data, jsonObject);
+        return newJSONObject;
+
+        // }
+//        else {
+//            // 若数据源无效,直接获取电子签模板信息
+//            ESignTemplateInfo eSignFile = fieldMappingInfo.getESignFile();
+//            // 重新组装模板数据并返回
+//            JSONObject jsonObject = this.reassembleData(eSignFile, context);
+//            return jsonObject;
+//        }
+
+    }
+
+    public String getSql(String id) {
+        StringBuilder sql = new StringBuilder();
+        sql.append("SELECT").append("\n");
+        sql.append("person.FNAME_L2 AS person_name ,  -- 人员姓名").append("\n");
+        sql.append("elation.FEnterDate  eDate ,  --入职日期").append("\n");
+        sql.append("-- 1. 固定工资(税前)= 标准基本工资+综合工资+其他工资+加班工资").append("\n");
+        sql.append("ISNULL(SUM(CASE WHEN sitem.FNUMBER IN ('TD01', 'T012', 'T013', 'TD02') THEN fas.FMoney ELSE 0 END), 0) AS fixed_salary ,").append("\n");
+        sql.append("-- 2. 试用期固定工资(税前)= 标准基本工资+综合工资+其他工资+加班工资").append("\n");
+        sql.append(" ISNULL(SUM(CASE WHEN sitem.FNUMBER IN ('TD01', 'T012', 'T013', 'TD02') THEN fas.FMoney ELSE 0 END), 0) AS prob_fixed_salary ,").append("\n");
+        sql.append("  -- 3. 绩效奖金(税前)= 标准绩效奖金").append("\n");
+        sql.append("ISNULL(SUM(CASE WHEN sitem.FNUMBER = 'TD04' THEN fas.FMoney ELSE 0 END), 0) AS perf_bonus ,").append("\n");
+        sql.append("-- 4. 试用期绩效奖金(税前)= 试用期标准绩效奖金").append("\n");
+        sql.append(" ISNULL(SUM(CASE WHEN sitem.FNUMBER = 'TD74' THEN fas.FMoney ELSE 0 END), 0) AS prob_perf_bonus ,").append("\n");
+        sql.append("-- 5. 国内餐补 = 餐补补贴").append("\n");
+        sql.append("ISNULL(SUM(CASE WHEN sitem.FNUMBER = 'T025' THEN fas.FMoney ELSE 0 END), 0) AS domestic_meal_subsidy ,").append("\n");
+        sql.append(" -- 6. 艰苦补贴 = 艰苦补贴").append("\n");
+        sql.append("ISNULL(SUM(CASE WHEN sitem.FNUMBER = 'T038' THEN fas.FMoney ELSE 0 END), 0) AS hardship_subsidy ,").append("\n");
+        sql.append("-- 7. 社保公积金补贴 = 社保公积金补贴").append("\n");
+        sql.append("ISNULL(SUM(CASE WHEN sitem.FNUMBER = 'TD101' THEN fas.FMoney ELSE 0 END), 0) AS social_fund").append("\n");
+        sql.append("FROM").append("\n");
+        sql.append("T_bd_Person person").append("\n");
+        sql.append(" LEFT JOIN T_HR_EmpLaborRelation   elation 	on person.fid =  elation.FPersonID").append("\n");
+        sql.append("LEFT JOIN").append("\n");
+        sql.append("T_HR_SFixAdjustSalary fas ON person.FID = fas.FPersonID").append("\n");
+        sql.append(" LEFT JOIN").append("\n");
+        sql.append("T_HR_SCmpItem sitem ON fas.FCmpItemID = sitem.FID").append("\n");
+        sql.append("WHERE").append("\n");
+        sql.append("sitem.FNUMBER IN ('TD01', 'T012', 'T013', 'TD02', 'TD04', 'TD74', 'T025', 'T038', 'TD101')").append("\n");
+        sql.append("and  person.fid  in (select FEmployeeID from T_HR_EmployeeContract where fid = '"+id+"')").append("\n");
+        sql.append(" GROUP BY").append("\n");
+        sql.append("person.FID, person.FNAME_L2  , elation.FEnterDate").append("\n");
+        sql.append("ORDER BY").append("\n");
+        sql.append("person.FNAME_L2").append("\n");
+        return sql.toString();
+    }
+
+    /**
+     * 执行SQL查询并将结果封装为JSON对象
+     *
+     * @param sql     要执行的查询SQL
+     * @param context 上下文对象
+     * @param entrys  字段映射明细集合
+     * @return 封装后的JSON对象,包含电子签模板各字段信息
+     */
+    public JSONObject getData(String sql, Context context, FieldMappingEntryCollection entrys) {
+        // 创建JSON对象用于存储最终封装的数据
+        JSONObject jsonObject = new JSONObject();
+        try {
+            // 执行SQL查询,获取结果集
+            IRowSet iRowSet1 = DBUtil.executeQuery(context, sql);
+            // 遍历结果集中的每一行数据
+            while (iRowSet1.next()) {
+                // 遍历每个字段映射明细项
+                for (int i = 0; i < entrys.size(); i++) {
+                    // 获取当前索引对应的字段映射明细信息
+                    FieldMappingEntryInfo fieldMappingEntryInfo = entrys.get(i);
+                    // 获取明细项中关联的电子签模板字段信息
+                    ESignTemplateFileEntryFieldInfo eField = fieldMappingEntryInfo.getEField();
+                    // 判断该字段是否无效(已废弃)
+                    boolean invalid = eField.isInvalid();
+                    // 若字段无效,跳过当前循环(不处理)
+                    if (invalid) {
+                        continue;
+                    }
+                    // 获取明细项中关联的电子签模板明细信息
+                    ESignTemplateFileEntryInfo template = fieldMappingEntryInfo.getTemplate();
+                    // 获取电子签模板的ID
+                    String eSignTemplateId = template.getESignTemplateId();
+                    // 从JSON对象中获取该模板ID对应的子JSON对象
+                    JSONObject templateJSON = jsonObject.getJSONObject(eSignTemplateId);
+                    // 若模板对应的子JSON对象不存在,则创建并初始化(设置ID和名称)
+                    if (templateJSON == null) {
+                        templateJSON = new JSONObject();
+                        templateJSON.put("id", eSignTemplateId); // 设置模板ID
+                        templateJSON.put("name", template.getName()); // 设置模板名称
+                        jsonObject.put(eSignTemplateId, templateJSON); // 将模板信息存入主JSON对象
+                    }
+                    // 从模板子JSON对象中获取字段信息对应的子JSON对象
+                    JSONObject fields = templateJSON.getJSONObject("fields");
+                    // 若字段信息子JSON对象不存在,则创建并添加到模板子JSON中
+                    if (fields == null) {
+                        fields = new JSONObject();
+                        templateJSON.put("fields", fields);
+                    }
+                    // 获取电子签模板字段的ID
+                    String templateFieldId = eField.getTemplateFieldId();
+                    // 创建用于存储字段值信息的JSON对象
+                    JSONObject value = new JSONObject();
+                    value.put("name", eField.getTemplateFieldName()); // 设置字段名称
+                    // 获取明细项中关联的数据源字段信息
+                    //DataConfigSelectFieldEntryInfo dataSourceField = fieldMappingEntryInfo.getDataSourceField();
+//                    if (dataSourceField==null){
+//                        value.put("value","");
+//                        value.put("dataFormat", eField.getDataFormat());
+//                    }else {
+                    // 获取数据源字段的映射键(对应数据库列名)
+                    String name = fieldMappingEntryInfo.getEFieldMark();
+                    // 去除映射键中的双引号(处理可能的格式问题)
+                    //name = name.replace("\"", "");
+                    // 从结果集中获取该字段对应的值,并存入字段值JSON对象
+                    if (name != null && !name.equals("")) {
+                        value.put("value", iRowSet1.getObject(name));
+                    }
+                    // 设置字段的数据类型(取自数据源字段的类型别名)
+                    //value.put("dataType", dataSourceField.getDataType().getAlias());
+//                    if (dataSourceField.getDataType().getAlias().equals("Date")) {
+//                        Object object = iRowSet1.getObject(name);
+//                        if (object != null) {
+//                            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
+//                            value.put("value", simpleDateFormat.format(iRowSet1.getDate(name)));
+//                        }
+//                        value.put("dataFormat", eField.getDataFormat());
+//                    }
+                    //}
+                    if (eField.getComponentType().getValue() == 6 || eField.getComponentType().getValue() == 106) {
+                        value.put("positionX", eField.getPositionX());
+                        value.put("positionY", eField.getPositionY());
+                        value.put("pageNum", eField.getPageNum());
+                    }
+                    // 将字段值信息存入字段信息子JSON对象(以模板字段ID为键)
+                    fields.put(templateFieldId, value);
+                }
+            }
+
+        } catch (BOSException e) {
+            // BOS异常时,封装为运行时异常抛出
+            throw new RuntimeException(e);
+        } catch (SQLException e) {
+            // SQL异常时,封装为运行时异常抛出
+            throw new RuntimeException(e);
+        }
+        // 返回封装好的JSON对象
+        return jsonObject;
+    }
+
+    /**
+     * 构建查询字段映射信息的实体视图
+     *
+     * @param number 字段映射的编号,用于过滤
+     * @return 构建好的实体视图信息
+     * @throws ParserException 解析异常
+     */
+    public EntityViewInfo getEntityViewInfo(String number) throws ParserException {
+        // 创建实体视图信息对象
+        EntityViewInfo entityViewInfo = new EntityViewInfo();
+        // 创建选择项集合,用于指定查询返回的字段
+        SelectorItemCollection selectorItemCollection = new SelectorItemCollection();
+        selectorItemCollection.add("*"); // 选择所有基础字段
+        selectorItemCollection.add("eSignFile.*"); // 选择关联的电子签文件的所有字段
+        selectorItemCollection.add("dataSource.*"); // 选择关联的数据源的所有字段
+        selectorItemCollection.add("entrys.*"); // 选择关联的明细项的所有字段
+        selectorItemCollection.add("entrys.eField.*"); // 选择明细项中电子签字段的所有字段
+        selectorItemCollection.add("entrys.dataSourceField.*"); // 选择明细项中数据源字段的所有字段
+        selectorItemCollection.add("entrys.template.*"); // 选择明细项中模板的所有字段
+        // 为实体视图设置选择项(指定查询返回的字段集)
+        entityViewInfo.setSelector(selectorItemCollection);
+        // 创建过滤信息对象,用于设置查询条件
+        FilterInfo filterInfo = new FilterInfo();
+        // 添加过滤条件:字段映射的编号等于传入的number
+        filterInfo.getFilterItems().add(new FilterItemInfo("number", number));
+        // 为实体视图设置过滤条件
+        entityViewInfo.setFilter(filterInfo);
+        // 返回构建好的实体视图信息
+        return entityViewInfo;
+    }
+
+    /**
+     * 重新组装数据(当前未实现完整逻辑,仅基础框架)
+     *
+     * @param eSignFile 电子签模板信息
+     * @param context   上下文对象
+     * @return 重新组装后的JSON对象(数据可能为null)
+     */
+    public JSONObject reassembleData(ESignTemplateInfo eSignFile, Context context) {
+        // 获取电子签模板的ID
+        BOSUuid id = eSignFile.getId();
+        // 声明用于存储组装后数据的JSON对象
+        JSONObject dataJSON;
+        try {
+            // 初始化数据JSON对象
+            dataJSON = new JSONObject();
+            // 创建实体视图信息对象
+            EntityViewInfo entityViewInfo = new EntityViewInfo();
+            // 创建选择项集合,指定查询返回的字段
+            SelectorItemCollection selectorItemCollection = new SelectorItemCollection();
+            selectorItemCollection.add("*"); // 选择所有基础字段
+            selectorItemCollection.add("Parent1.*"); // 选择父级对象的所有字段
+            // 为实体视图设置选择项
+            entityViewInfo.setSelector(selectorItemCollection);
+            // 创建过滤信息对象
+            FilterInfo filterInfo = new FilterInfo();
+            // 添加过滤条件:字段未失效(Invalid为false)
+            filterInfo.getFilterItems().add(new FilterItemInfo("Invalid", 1, CompareType.NOTEQUALS));
+            // 添加过滤条件:父级的父级ID等于电子签模板ID
+            filterInfo.getFilterItems().add(new FilterItemInfo("Parent1.Parent.id", id));
+            // 为实体视图设置过滤条件
+            entityViewInfo.setFilter(filterInfo);
+            // 根据实体视图查询电子签模板字段明细集合
+            ESignTemplateFileEntryFieldCollection eFields = ESignTemplateFileEntryFieldFactory.
+                    getLocalInstance(context).getESignTemplateFileEntryFieldCollection(entityViewInfo);
+            // 遍历字段明细集合
+            for (int i = 0; i < eFields.size(); i++) {
+
+                // 获取当前索引对应的电子签模板字段明细
+                ESignTemplateFileEntryFieldInfo eFile = eFields.get(i);
+                // 获取该字段明细的父级对象(模板明细)
+                ESignTemplateFileEntryInfo parent1 = eFile.getParent1();
+                // 获取电子签模板ID
+                String eSignTemplateId = parent1.getESignTemplateId();
+                // 获取电子签模板名称
+                String name = parent1.getName();
+                // 从数据JSON中获取模板ID对应的子JSON对象
+                JSONObject signTemplate = dataJSON.getJSONObject(eSignTemplateId);
+                // 若模板子JSON对象不存在,则创建并初始化
+                if (signTemplate == null) {
+                    signTemplate = new JSONObject();
+                    signTemplate.put("name", name); // 设置模板名称
+                    signTemplate.put("id", eSignTemplateId); // 设置模板ID
+                    JSONObject fields = new JSONObject(); // 创建字段信息JSON对象
+                    signTemplate.put("fields", fields); // 将字段信息存入模板JSON
+                    dataJSON.put(eSignTemplateId, signTemplate);
+                }
+                // 从模板子JSON中获取字段信息JSON对象
+                JSONObject fields = signTemplate.getJSONObject("fields");
+                // 获取模板字段ID
+                String templateFieldId = eFile.getTemplateFieldId();
+                // 从字段信息JSON中获取该字段ID对应的子JSON对象
+                JSONObject field = fields.getJSONObject(templateFieldId);
+                // 若字段子JSON对象不存在,则创建并添加到字段信息JSON
+                if (field == null) {
+                    field = new JSONObject();
+                    fields.put(templateFieldId, field);
+                }
+                // 获取模板字段名称
+                String templateFieldName = eFile.getTemplateFieldName();
+                // 设置字段名称到字段子JSON
+                field.put("name", templateFieldName);
+                // 获取字段组件类型
+                ComponentTypeEnum componentType = eFile.getComponentType();
+                // 设置字段数据类型(取自组件类型的别名)
+                field.put("dataType", componentType.getAlias());
+                field.put("dataFormat", eFile.getDataFormat());
+                field.put("isRequired", eFile.isRequired());
+                field.put("seq", eFile.getSeq());
+                // 字段值暂设为null
+                field.put("value", null);
+            }
+
+        } catch (BOSException e) {
+            // BOS异常时,封装为运行时异常抛出
+            throw new RuntimeException(e);
+        }
+        // 返回重新组装后的JSON对象
+        return dataJSON;
+    }
+}

+ 493 - 0
websrc/com/kingdee/eas/custom/esign/utils/ESignFieldMappingUtils.java

@@ -0,0 +1,493 @@
+package com.kingdee.eas.custom.esign.utils;
+
+import com.alibaba.fastjson.JSONException;
+import com.alibaba.fastjson.JSONObject;
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.metadata.entity.EntityViewInfo;
+import com.kingdee.bos.metadata.entity.FilterInfo;
+import com.kingdee.bos.metadata.entity.FilterItemInfo;
+import com.kingdee.eas.custom.entryconfig.DataConfigSelectFieldEntryCollection;
+import com.kingdee.eas.custom.entryconfig.DataConfigSelectFieldEntryInfo;
+import com.kingdee.bos.Context;
+import com.kingdee.eas.custom.esign.*;
+import com.kingdee.shr.base.syssetting.MSFServiceFacadeFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 电子签名字段映射工具类
+ * 提供电子签名相关的字段处理、SQL获取、模板信息获取、JSON合并等工具方法
+ */
+public class ESignFieldMappingUtils {
+//    /**
+//     * 生成电子签名SQL字段字符串(已注释,备用逻辑)
+//     * @param fields 字段映射集合
+//     * @return 拼接后的SQL字段字符串(格式:数据库字段 as 映射键, ...)
+//     */
+//    public String getESignSqlField(FieldMappingEntryCollection fields){
+//        String sqlField = "";
+//        // 遍历字段映射集合
+//        for (int i = 0; i < fields.size(); i++) {
+//            // 获取当前字段映射信息
+//            FieldMappingEntryInfo field = fields.get(i);
+//            // 获取数据源字段信息
+//            DataConfigSelectFieldEntryInfo dataSourceField = field.getDataSourceField();
+//            // 获取映射键
+//            String mappingKey = dataSourceField.getMappingKey();
+//            // 获取数据库字段名
+//            String databaseName = dataSourceField.getDatabaseName();
+//            // 拼接SQL字段字符串(数据库字段 as 映射键)
+//            sqlField+=databaseName+" as "+mappingKey;
+//            // 判断是否为最后一个字段,最后一个字段后加换行,否则加逗号
+//            if (i==fields.size()-1){
+//                sqlField+="\n";
+//            }else {
+//                sqlField+=",";
+//            }
+//        }
+//        // 返回拼接好的SQL字段字符串
+//        return sqlField;
+//    }
+
+    /**
+     * 从字段映射集合中提取数据源字段集合
+     * @param fields 字段映射集合(包含字段映射关系)
+     * @return 数据源字段集合(仅包含数据源字段信息)
+     */
+    public DataConfigSelectFieldEntryCollection getESignSqlField(FieldMappingEntryCollection fields){
+        // 初始化数据源字段集合
+        DataConfigSelectFieldEntryCollection dataConfigSelectFieldEntryCollection = new DataConfigSelectFieldEntryCollection();
+        // 遍历字段映射集合
+        for (int i = 0; i < fields.size(); i++) {
+            // 获取当前字段映射信息
+            FieldMappingEntryInfo field = fields.get(i);
+            // 从字段映射中提取数据源字段
+            DataConfigSelectFieldEntryInfo dataSourceField = field.getDataSourceField();
+            // 将数据源字段添加到结果集合中
+            if (dataSourceField!=null){
+                dataConfigSelectFieldEntryCollection.add(dataSourceField);
+            }
+        }
+        // 返回提取后的数据源字段集合
+        return dataConfigSelectFieldEntryCollection;
+    }
+
+    /**
+     * 根据数据配置编号获取电子签名相关SQL
+     * @param dataConfigNumber 数据配置编号(用于标识特定的数据配置)
+     * @param context 上下文对象(金蝶BOS框架上下文,包含环境信息)
+     * @return 获取到的SQL字符串
+     */
+    public String getESignSql(String dataConfigNumber,Context context){
+        // 初始化服务调用参数映射
+        Map<String,Object> getSqlServiceMap = new HashMap<String,Object>();
+        // 设置参数:数据配置编号
+        getSqlServiceMap.put("number",dataConfigNumber);
+        // 声明SQL结果变量
+        Object sql = null;
+        try {
+            // 调用本地服务获取SQL,服务名为"getSqlService",传入参数映射
+            sql = MSFServiceFacadeFactory.getLocalInstance(context).processService("getSqlService", getSqlServiceMap);
+        } catch (Exception e) {
+            // 捕获异常并包装为运行时异常抛出
+            throw new RuntimeException(e);
+        }
+        // 将SQL结果转为字符串返回
+        return sql.toString();
+    }
+
+    /**
+     * 根据ID获取电子签名模板信息
+     * @param id 模板ID(用于唯一标识模板)
+     * @param context 上下文对象(金蝶BOS框架上下文)
+     * @return 电子签名模板信息(当前逻辑未实现,返回null)
+     * @throws BOSException 金蝶BOS框架异常
+     */
+    public ESignTemplateInfo getTemplateInfo(String id,Context context) throws BOSException {
+        // 创建实体视图信息(用于查询时指定视图条件)
+        EntityViewInfo entityViewInfo = new EntityViewInfo();
+        // 创建过滤信息对象(用于设置查询过滤条件)
+        FilterInfo filterInfo = new FilterInfo();
+        // 向过滤信息中添加过滤项:根据ID过滤(字段名为"id",值为传入的id)
+        filterInfo.getFilterItems().add(new FilterItemInfo("id",id));
+        // 将过滤信息设置到实体视图中
+        entityViewInfo.setFilter(filterInfo);
+        // 调用电子签名模板工厂获取符合条件的模板集合(当前未使用返回的集合)
+        ESignTemplateFactory.getLocalInstance(context).getESignTemplateCollection(entityViewInfo);
+
+        // 方法暂未实现,返回null
+        return null;
+    }
+
+    /**
+     * 合并两个协议JSONObject对象的方法
+     * @param baseJson 基础JSON对象(被合并的原始数据,优先级较低)
+     * @param newJson 新JSON对象(待合并的数据,优先级较高,用于覆盖/补充原始数据)
+     * @return 合并后的JSONObject
+     * @throws IllegalArgumentException 当基础JSON为null时抛出
+     */
+    public  JSONObject mergeAgreements(JSONObject baseJson, JSONObject newJson) {
+        // 基础JSON非空校验
+        if (baseJson == null) {
+            throw new IllegalArgumentException("基础JSON数据不能为null");
+        }
+        // 若新JSON为null,直接返回基础JSON
+        if (newJson == null) {
+            return baseJson;
+        }
+
+        // 遍历新JSON中的所有协议Key
+        for (String agreementKey : newJson.keySet()) {
+            try {
+                // 从新JSON中获取当前Key对应的协议对象
+                JSONObject newAgreement = newJson.getJSONObject(agreementKey);
+                // 若当前协议不是JSONObject类型,跳过处理
+                if (newAgreement == null) {
+                    continue;
+                }
+
+                // 若基础JSON中已包含当前协议Key
+                if (baseJson.containsKey(agreementKey)) {
+                    // 从基础JSON中获取对应协议对象
+                    JSONObject baseAgreement = baseJson.getJSONObject(agreementKey);
+                    // 合并两个协议对象的内部字段
+                    mergeSingleAgreement(baseAgreement, newAgreement);
+                } else {
+                    // 若基础JSON中不包含当前协议Key,直接添加新协议到基础JSON
+                    baseJson.put(agreementKey, newAgreement);
+                }
+            } catch (JSONException e) {
+                // 捕获JSON解析异常,打印错误信息后继续处理其他协议
+                System.err.println("处理协议[" + agreementKey + "]时出错:" + e.getMessage());
+                continue;
+            }
+        }
+        // 返回合并后的基础JSON
+        return baseJson;
+    }
+
+//    /**
+//     * 合并两个单个协议对象的内部字段
+//     * @param baseAgreement 基础协议对象(被合并的原始协议)
+//     * @param newAgreement 新协议对象(待合并的协议,用于覆盖/补充原始协议)
+//     */
+//    private  void mergeSingleAgreement(JSONObject baseAgreement, JSONObject newAgreement) {
+//        // 合并协议的基础属性:name(若新协议有name则覆盖)
+//        if (newAgreement.getString("name") != null) {
+//            baseAgreement.put("name", newAgreement.getString("name"));
+//        }
+//        // 合并协议的基础属性:id(若新协议有id则覆盖)
+//        if (newAgreement.getString("id") != null) {
+//            baseAgreement.put("id", newAgreement.getString("id"));
+//        }
+//        // 合并协议的基础属性:dataType(dataType则覆盖)
+//        if (newAgreement.getString("dataType") != null) {
+//            baseAgreement.put("dataType", newAgreement.getString("dataType"));
+//        }
+//        // 合并协议的基础属性:dataFormat(dataFormat则覆盖)
+//        if (newAgreement.getString("dataFormat") != null) {
+//            baseAgreement.put("dataFormat", newAgreement.getString("dataFormat"));
+//        }
+//
+//        // 从新协议中获取fields字段(字段集合)
+//        JSONObject newFields = newAgreement.getJSONObject("fields");
+//        // 若新协议的fields为空或不存在,无需合并字段,直接返回
+//        if (newFields == null || newFields.isEmpty()) {
+//            return;
+//        }
+//
+//        // 从基础协议中获取fields字段(字段集合)
+//        JSONObject baseFields = baseAgreement.getJSONObject("fields");
+//        // 若基础协议中没有fields字段,直接将新协议的fields设置到基础协议
+//        if (baseFields == null) {
+//            baseAgreement.put("fields", newFields);
+//            return;
+//        }
+//
+//        // 遍历新协议fields中的所有字段Key
+//        for (String fieldKey : newFields.keySet()) {
+//            // 从新协议fields中获取当前字段对象
+//            JSONObject newField = newFields.getJSONObject(fieldKey);
+//            if (newField != null) {
+//                // 获取新字段的value值
+//                Object value = newField.get("value");
+//                // 若新字段value为null且基础fields中已存在该字段,则跳过(不覆盖现有有效数据)
+//                if (value == null && baseFields.containsKey(fieldKey)) {
+//                    JSONObject jsonObject1 = baseFields.getJSONObject(fieldKey);
+//                    String dataType = jsonObject1.getString("dataType");
+//                    String dataFormat = jsonObject1.getString("dataFormat");
+//                    newField.put("dataType",dataType);
+//                    newField.put("dataFormat",dataFormat);
+//                    //continue;
+//                }
+////                else {
+////                    String dataType = baseFields.getString("dataType");
+////                    String dataFormat = baseFields.getString("dataFormat");0
+////                    newField.put("dataType",dataType);
+////                    newField.put("dataFormat",dataFormat);
+////                }
+//                // 将新字段添加/覆盖到基础fields中
+//                baseFields.put(fieldKey, newField);
+//            }
+//        }
+//    }
+    /**
+     * 合并单个协议对象内部的字段
+     * @param baseAgreement 基础协议对象(被合并的原始协议)
+     * @param newAgreement 新协议对象(要合并的协议,用于覆盖/补充原始协议)
+     */
+    private void mergeSingleAgreement(JSONObject baseAgreement, JSONObject newAgreement) {
+        // 协议级字段:优先使用newAgreement的name、id、dataType、dataFormat
+        if (newAgreement.getString("name") != null) {
+            baseAgreement.put("name", newAgreement.getString("name"));
+        }
+        if (newAgreement.getString("id") != null) {
+            baseAgreement.put("id", newAgreement.getString("id"));
+        }
+        if (newAgreement.getString("dataType") != null) {
+            baseAgreement.put("dataType", newAgreement.getString("dataType"));
+        }
+        if (newAgreement.getString("dataFormat") != null) {
+            baseAgreement.put("dataFormat", newAgreement.getString("dataFormat"));
+        }
+
+        // 从新协议中获取fields
+        JSONObject newFields = newAgreement.getJSONObject("fields");
+        if (newFields == null || newFields.isEmpty()) {
+            return; // 新协议无fields,无需处理
+        }
+
+        // 从基础协议中获取fields(若不存在则直接使用新协议的fields)
+        JSONObject baseFields = baseAgreement.getJSONObject("fields");
+        if (baseFields == null) {
+            baseAgreement.put("fields", newFields); // base无fields,直接用new的fields
+            return;
+        }
+
+        // 基础协议存在fields,遍历新协议的fields进行合并
+        for (String fieldKey : newFields.keySet()) {
+            JSONObject newField = newFields.getJSONObject(fieldKey);
+            if (newField == null) {
+                continue;
+            }
+
+            // 若基础协议的fields中没有当前字段,直接添加新字段(以new为主)
+            if (!baseFields.containsKey(fieldKey)) {
+                baseFields.put(fieldKey, newField);
+                continue;
+            }
+
+            // 基础协议存在当前字段,进行字段级合并
+            JSONObject baseField = baseFields.getJSONObject(fieldKey);
+            // 1. new的dataType和dataFormat优先覆盖base
+            String newDataType = newField.getString("dataType");
+            if (newDataType != null) {
+                baseField.put("dataType", newDataType);
+            }
+            String newDataFormat = newField.getString("dataFormat");
+            if (newDataFormat != null) {
+                baseField.put("dataFormat", newDataFormat);
+            }
+
+            // 2. 其他字段(如name、value等):以base为主,new有非空值时覆盖
+            for (String key : newField.keySet()) {
+                if ("dataType".equals(key) || "dataFormat".equals(key)) {
+                    continue; // 已处理过的字段跳过
+                }
+                Object newValue = newField.get(key);
+                if (newValue != null) {
+                    baseField.put(key, newValue); // 仅new有值时覆盖base
+                }
+            }
+
+            // 将合并后的字段放回baseFields
+            baseFields.put(fieldKey, baseField);
+        }
+    }
+    // ---------- 以下为重载方法,支持JSON字符串的合并 ----------
+
+    /**
+     * 合并两个JSON字符串(重载方法)
+     * @param baseJsonStr 基础JSON字符串
+     * @param newJsonStr 新JSON字符串
+     * @return 合并后的JSONObject
+     * @throws IllegalArgumentException 当JSON字符串解析失败时抛出
+     */
+    public  JSONObject mergeAgreements(String baseJsonStr, String newJsonStr) {
+        try {
+            // 将基础JSON字符串解析为JSONObject
+            JSONObject baseJson = JSONObject.parseObject(baseJsonStr);
+            // 将新JSON字符串解析为JSONObject
+            JSONObject newJson = JSONObject.parseObject(newJsonStr);
+            // 调用JSON对象合并方法
+            return mergeAgreements(baseJson, newJson);
+        } catch (JSONException e) {
+            // 解析失败时抛出异常,包含错误信息
+            throw new IllegalArgumentException("JSON字符串解析失败:" + e.getMessage(), e);
+        }
+    }
+
+    // ---------- 以下为重载方法,支持从文件读取JSON并合并 ----------
+
+    /**
+     * 从文件读取JSON字符串并合并(重载方法)
+     * @param baseFilePath 基础JSON文件路径
+     * @param newFilePath 新JSON文件路径
+     * @return 合并后的JSONObject
+     * @throws IOException 当文件读取失败时抛出
+     */
+    public  JSONObject mergeAgreementsFromFile(String baseFilePath, String newFilePath) throws IOException {
+        // 读取基础JSON文件内容为字符串
+        String baseJsonStr = readFileToString(baseFilePath);
+        // 读取新JSON文件内容为字符串
+        String newJsonStr = readFileToString(newFilePath);
+        // 调用JSON字符串合并方法
+        return mergeAgreements(baseJsonStr, newJsonStr);
+    }
+
+    /**
+     * 将文件内容读取为字符串(使用UTF-8编码)
+     * @param filePath 文件路径
+     * @return 文件内容字符串
+     * @throws IOException 当文件不存在或读取失败时抛出
+     */
+    private  String readFileToString(String filePath) throws IOException {
+        // 根据文件路径创建File对象
+        File file = new File(filePath);
+        // 使用try-with-resources自动关闭流:创建文件输入流和Scanner
+        try (FileInputStream fis = new FileInputStream(file);
+             java.util.Scanner scanner = new java.util.Scanner(fis, StandardCharsets.UTF_8.name())) {
+            // 设置Scanner的分隔符为文件开始(即读取整个文件)
+            scanner.useDelimiter("\\A");
+            // 若有内容则返回内容,否则返回空字符串
+            return scanner.hasNext() ? scanner.next() : "";
+        }
+    }
+
+    // ---------- 测试方法入口 ----------
+
+    /**
+     * 主方法:测试入口
+     * @param args 命令行参数
+     */
+    public static void main(String[] args) {
+        // 测试场景1:基础合并 + 新增数据(被注释,可取消注释执行)
+//        testBasicMerge();
+//
+//        // 测试场景2:新JSON为null(被注释,可取消注释执行)
+//        testNewJsonNull();
+
+        // 测试场景3:字段value为null的处理(被注释,可取消注释执行)
+        //testNullValueField();
+    }
+
+    /**
+     * 测试场景1:基础合并逻辑
+     * 验证已有协议字段覆盖、新增协议及字段的合并效果
+     */
+//    private static void testBasicMerge() {
+//        System.out.println("===== 测试场景1:基础合并 =====");
+//        // 基础JSON字符串(原始数据)
+//        String baseJsonStr = " {\n" +
+//                "                    \"借款协议_id\": {\n" +
+//                "                        \"name\": \"借款协议\",\n" +
+//                "                        \"id\": \"借款协议_id\",\n" +
+//                "                        \"fields\": {\n" +
+//                "                            \"借款协议_Field_id\": {\"dataType\": \"String\", \"name\": \"借款协议Field1\", \"value\": \"001\"},\n" +
+//                "                            \"借款协议_Field_id1\": {\"dataType\": \"String\", \"name\": \"借款协议Field2\", \"value\": null}\n" +
+//                "                        }\n" +
+//                "                    },\n" +
+//                "                    \"投资合同_id\": {\n" +
+//                "                        \"name\": \"投资合同\",\n" +
+//                "                        \"id\": \"投资合同_id\",\n" +
+//                "                        \"fields\": {\"投资合同_Field_id1\": {\"dataType\": \"String\", \"name\": \"投资合同Field2\", \"value\": \"小明\"}}\n" +
+//                "                    }\n" +
+//                "                }";
+//
+//        // 新JSON字符串(待合并数据)
+//        String newJsonStr = "{\n" +
+//                "                    \"借款协议_id\": {\n" +
+//                "                        \"name\": \"借款协议(修改)\",\n" +
+//                "                        \"id\": \"借款协议_id\",\n" +
+//                "                        \"fields\": {\n" +
+//                "                            \"借款协议_Field_id1\": {\"dataType\": \"String\", \"name\": \"借款协议Field2\", \"value\": \"002\"},\n" +
+//                "                            \"借款协议_Field_id2\": {\"dataType\": \"String\", \"name\": \"借款协议Field3\", \"value\": \"新字段\"}\n" +
+//                "                        }\n" +
+//                "                    },\n" +
+//                "                    \"业务协议_id\": {\n" +
+//                "                        \"name\": \"业务协议\",\n" +
+//                "                        \"id\": \"业务协议_id\",\n" +
+//                "                        \"fields\": {\"业务协议_Field_id\": {\"dataType\": \"String\", \"name\": \"业务协议Field1\", \"value\": \"19892761659\"}}\n" +
+//                "                    }\n" +
+//                "                }";
+//
+//        try {
+//            // 执行合并
+//            JSONObject mergedJson = mergeAgreements(baseJsonStr, newJsonStr);
+//            // 打印合并结果(格式化输出)
+//            System.out.println("合并结果\n" + JSONObject.toJSONString(mergedJson, true));
+//        } catch (Exception e) {
+//            // 捕获并打印合并失败信息
+//            System.err.println("合并失败:" + e.getMessage());
+//        }
+//        System.out.println("------------------------\n");
+//    }
+//
+//    /**
+//     * 测试场景2:新JSON为null的情况
+//     * 验证当新数据为null时,是否返回基础数据
+//     */
+//    private static void testNewJsonNull() {
+//        System.out.println("===== 测试场景2:新JSON为null =====");
+//        // 基础JSON字符串
+//        String baseJsonStr = "{\"投资合同_id\": {\"name\": \"投资合同\", \"id\": \"投资合同_id\"}}";
+//        try {
+//            // 传入新JSON为null,执行合并
+//            JSONObject mergedJson = mergeAgreements(baseJsonStr, null);
+//            // 打印合并结果
+//            System.out.println("合并结果\n" + JSONObject.toJSONString(mergedJson, true));
+//        } catch (Exception e) {
+//            // 捕获并打印合并失败信息
+//            System.err.println("合并失败:" + e.getMessage());
+//        }
+//        System.out.println("------------------------\n");
+//    }
+
+    /**
+     * 测试场景3:字段value为null的处理
+     * 验证当新字段value为null时,是否保留基础字段的有效数据
+     */
+//    private static void testNullValueField() {
+//        System.out.println("===== 测试场景3:字段value为null =====");
+//        // 基础JSON字符串(包含有效value的字段)
+//        String baseJsonStr = """
+//                {
+//                    "借款协议_id": {
+//                        "fields": {"借款协议_Field_id": {"value": "有效数据"}}
+//                    }
+//                }""";
+//
+//        // 新JSON字符串(包含value为null的字段和新增字段)
+//        String newJsonStr = """
+//                {
+//                    "借款协议_id": {
+//                        "fields": {"借款协议_Field_id": {"value": null}, "新字段": {"value": "新数据"}}
+//                    }
+//                }""";
+//
+//        try {
+//            // 执行合并
+//            JSONObject mergedJson = mergeAgreements(baseJsonStr, newJsonStr);
+//            // 打印合并结果(验证value为null的字段是否被忽略)
+//            System.out.println("合并结果(验证字段value为null时的处理):\n" + JSONObject.toJSONString(mergedJson, true));
+//        } catch (Exception e) {
+//            // 捕获并打印合并失败信息
+//            System.err.println("合并失败:" + e.getMessage());
+//        }
+//    }
+}