ソースを参照

feat(performance): 添加年度绩效归档记录和批量修改功能

- 新增 DOCSTAMP_ENTITYID 等归档记录相关常量定义
- 实现批量修改文件印发及盖章时间的表单插件功能
- 添加选择人员列表并保存印章时间的业务逻辑
- 扩展绩效明细报表插件支持岗位信息和排名管理数据查询
- 实现近N年岗位信息的动态查询和行转列处理功能
wyc 1 週間 前
コミット
85b14f2cff

+ 12 - 0
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/common/PerfManagerFormConstant.java

@@ -85,4 +85,16 @@ public class PerfManagerFormConstant extends FormConstant {
     public static final String POSTWAGEUNITLIST_ENTITYID = "nckd_postwageunitlist";
     public static final String NCKD_YEAR = "nckd_year";
     /*-------------------------------------- 年度绩效归档记录 end --------------------------------------*/
+
+
+    /*-------------------------------------- 年度绩效归档记录 begin --------------------------------------*/
+    /** 文件印发及盖章时间-实体标识 */
+    public static final String DOCSTAMP_ENTITYID = "nckd_docstamp";
+    /** HR员工 */
+    public static final String NCKD_PERSON = "nckd_person";
+    /** 盖章时间 */
+    public static final String NCKD_STAMPTIME = "nckd_stamptime";
+    /** 文件印发时间 */
+    public static final String NCKD_FILEISSUETIME = "nckd_fileissuetime";
+    /*-------------------------------------- 年度绩效归档记录 end --------------------------------------*/
 }

+ 92 - 11
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/print/BatchModDocstampPopupFormPlugin.java

@@ -1,13 +1,32 @@
 package nckd.jxccl.opmc.pm.plugin.form.print;
 
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.utils.ObjectUtils;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.entity.MainEntityType;
+import kd.bos.entity.operate.result.IOperateInfo;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.form.FormShowParameter;
 import kd.bos.form.events.AfterDoOperationEventArgs;
 import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.exception.ValidationException;
+import nckd.jxccl.base.common.utils.ConvertUtil;
+import nckd.jxccl.base.entity.helper.EntityHelper;
+import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
 
 import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.List;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
 
 /**
 * 批量修改文件印发及盖章时间
@@ -18,18 +37,80 @@ import java.util.Map;
 */
 public class BatchModDocstampPopupFormPlugin extends AbstractFormPlugin implements Plugin {
 
+
     @Override
     public void afterDoOperation(AfterDoOperationEventArgs e) {
         String operateKey = e.getOperateKey();
-        if(e.getOperationResult() != null && e.getOperationResult().isSuccess())
-        if(FormConstant.AFFIRM_OP.equalsIgnoreCase(operateKey)){
-            Date fileIssueTime = this.getModel().getDataEntity(true).getDate("nckd_fileissuetime");
-            Date stampTime = this.getModel().getDataEntity(true).getDate("nckd_stamptime");
-            Map<String,Date> returnDate = new  HashMap<>();
-            returnDate.put("nckd_fileissuetime",fileIssueTime);
-            returnDate.put("nckd_stamptime",stampTime);
-            this.getView().returnDataToParent(returnDate);
-            this.getView().close();
+        if(e.getOperationResult() != null && e.getOperationResult().isSuccess()) {
+            FormShowParameter showParameter = this.getView().getFormShowParameter();
+            //获取列表选择的人员
+            Object personIds = showParameter.getCustomParam("personIds");
+            List<Object> list = ConvertUtil.toList(personIds);
+            if (FormConstant.AFFIRM_OP.equalsIgnoreCase(operateKey)) {
+                Date fileIssueTime = this.getModel().getDataEntity(true).getDate(PerfManagerFormConstant.NCKD_FILEISSUETIME);
+                Date stampTime = this.getModel().getDataEntity(true).getDate(PerfManagerFormConstant.NCKD_STAMPTIME);
+                DynamicObjectCollection query = QueryServiceHelper.query(PerfManagerFormConstant.DOCSTAMP_ENTITYID,
+                        FormConstant.ID_KEY + "," + String.join(",", FormConstant.NCKD_PERSON, FormConstant.ID_KEY),
+                        new QFilter[]{new QFilter(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), QCP.in, list)});
+                List<Long> ids = query.stream().map(dynamicObject -> dynamicObject.getLong(FormConstant.ID_KEY)).collect(Collectors.toList());
+                MainEntityType docStampEntityType = EntityMetadataCache.getDataEntityType(PerfManagerFormConstant.DOCSTAMP_ENTITYID);
+                DynamicObject[] load = BusinessDataServiceHelper.load(ids.toArray(new Long[0]), docStampEntityType);
+                // 提取查询结果中的人员ID
+                List<DynamicObject> save = new java.util.ArrayList<>();
+                java.util.Set<Long> foundPersonIds = new java.util.HashSet<>();
+                for (DynamicObject dynamicObject : load) {
+                    Long personId = dynamicObject.getLong(String.join(",", FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+                    foundPersonIds.add(personId);
+                    dynamicObject.set(PerfManagerFormConstant.NCKD_STAMPTIME, stampTime);
+                    dynamicObject.set(PerfManagerFormConstant.NCKD_FILEISSUETIME, stampTime);
+                    save.add(dynamicObject);
+                }
+
+                // 计算不在查询结果中的人员ID(即没有文档印章记录的人员ID)
+                java.util.List<Long> missingPersonIds = new java.util.ArrayList<>();
+                for (Object personId : list) {
+                    Long id = ConvertUtil.toLong(personId);
+                    if (!foundPersonIds.contains(id)) {
+                        missingPersonIds.add(id);
+                    }
+                }
+                for (Long missingPersonId : missingPersonIds) {
+                    DynamicObject dynamicObject = EntityHelper.newEntity(PerfManagerFormConstant.DOCSTAMP_ENTITYID);
+                    DynamicObject employee = EntityHelper.newEntity(FormConstant.HRPI_EMPLOYEE,missingPersonId);
+                    dynamicObject.set(FormConstant.NCKD_PERSON,employee);
+                    dynamicObject.set(PerfManagerFormConstant.NCKD_STAMPTIME, stampTime);
+                    dynamicObject.set(PerfManagerFormConstant.NCKD_FILEISSUETIME, stampTime);
+                    save.add(dynamicObject);
+
+                }
+                OperationResult operationResult = SaveServiceHelper.saveOperate(
+                        PerfManagerFormConstant.DOCSTAMP_ENTITYID,
+                        save.toArray(new DynamicObject[0]),
+                        OperateOption.create());
+
+                if (!operationResult.isSuccess()) {
+                    StringJoiner errorMsg = new StringJoiner("\n");
+                    for (IOperateInfo error : operationResult.getAllErrorOrValidateInfo()) {
+                        errorMsg.add(error.getMessage());
+                    }
+                    if (!ObjectUtils.isEmpty(operationResult.getMessage())) {
+                        errorMsg.add(operationResult.getMessage());
+                    }
+                    throw new ValidationException("保存失败" + errorMsg.toString());
+                }else{
+                    this.getView().close();
+                }
+
+
+
+               /* Date fileIssueTime = this.getModel().getDataEntity(true).getDate("nckd_fileissuetime");
+                Date stampTime = this.getModel().getDataEntity(true).getDate("nckd_stamptime");
+                Map<String, Date> returnDate = new HashMap<>();
+                returnDate.put("nckd_fileissuetime", fileIssueTime);
+                returnDate.put("nckd_stamptime", stampTime);
+                this.getView().returnDataToParent(returnDate);
+                this.getView().close();*/
+            }
         }
     }
 }

+ 34 - 6
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/print/PerfBatchPrintListPlugin.java

@@ -2,6 +2,8 @@ package nckd.jxccl.opmc.pm.plugin.form.print;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.dataentity.utils.StringUtils;
 import kd.bos.entity.datamodel.events.PackageDataEvent;
 import kd.bos.entity.report.ReportColumn;
@@ -11,15 +13,16 @@ import kd.bos.form.ShowType;
 import kd.bos.form.control.Toolbar;
 import kd.bos.form.control.events.ItemClickEvent;
 import kd.bos.form.events.ClosedCallBackEvent;
+import kd.bos.form.events.PreOpenFormEventArgs;
+import kd.bos.report.ReportList;
 import kd.bos.report.plugin.AbstractReportFormPlugin;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
-import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.common.utils.DateUtil;
 
-import java.util.Date;
+import java.util.ArrayList;
 import java.util.EventObject;
-import java.util.Map;
+import java.util.List;
 
 /**
 * 人员考评套打列表
@@ -30,6 +33,14 @@ import java.util.Map;
 */
 public class PerfBatchPrintListPlugin extends AbstractReportFormPlugin implements Plugin {
 
+    @Override
+    public void preOpenForm(PreOpenFormEventArgs e) {
+        // 1. 获取表单显示参数对象
+        FormShowParameter showParameter = (FormShowParameter) e.getSource();
+        // 2. 设置页面标题
+        showParameter.setCaption("套打列表");
+        super.preOpenForm(e);
+    }
     @Override
     public void registerListener(EventObject e) {
         super.registerListener(e);
@@ -42,11 +53,28 @@ public class PerfBatchPrintListPlugin extends AbstractReportFormPlugin implement
         String itemKey = evt.getItemKey();
         String operationKey = evt.getOperationKey();
         if("nckd_baritemap".equalsIgnoreCase(itemKey)){
+            ReportList reportList = this.getView().getControl(FormConstant.REPORTLISTAP);
+            int[] selectedRowIndexes = reportList.getEntryState().getSelectedRows();
+            List<Long> personIds =  new ArrayList<>();
+            if(selectedRowIndexes != null && selectedRowIndexes.length > 0) {
+                for (int selectedRowIndex : selectedRowIndexes) {
+                    DynamicObject rowData = reportList.getReportModel().getRowData(selectedRowIndex);
+                    personIds.add(rowData.getDynamicObject(FormConstant.NCKD_PERSON).getLong(FormConstant.ID_KEY));
+                }
+            }else{
+                int rowCount = reportList.getReportModel().getRowCount();
+                DynamicObjectCollection rowDataColl = reportList.getReportModel().getRowData(1, rowCount, false);
+                for (DynamicObject dynamicObject : rowDataColl) {
+                    long personId = dynamicObject.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+                    personIds.add(personId);
+                }
+            }
             FormShowParameter showParameter = new FormShowParameter();
             showParameter.setFormId("nckd_batchmoddocstamppopu");
             showParameter.getOpenStyle().setShowType(ShowType.Modal);
             showParameter.setCloseCallBack(new CloseCallBack(this, "nckd_batchmoddocstamppopu"));
             showParameter.setCaption("批量修改文件印发及盖章时间");
+            showParameter.setCustomParam( "personIds", personIds);
             this.getView().showForm(showParameter);
         }
     }
@@ -57,15 +85,15 @@ public class PerfBatchPrintListPlugin extends AbstractReportFormPlugin implement
         if("nckd_batchmoddocstamppopu".equalsIgnoreCase(actionId)){
             Object returnData = closedCallBackEvent.getReturnData();
             if(returnData != null){
-                Map<String, Date> returnDate = ConvertUtil.toMap(returnData);
-                this.getPageCache().put("fileIssueTime", JSON.toJSONString(returnDate));
+                /*Map<String, Date> returnDate = ConvertUtil.toMap(returnData);
+                this.getPageCache().put("fileIssueTime", JSON.toJSONString(returnDate));*/
                 this.getView().invokeOperation(FormConstant.REFRESH_OP);
             }
         }
     }
 
 
-    @Override
+   @Override
     public void packageData(PackageDataEvent e) {
         String fileIssueTime = this.getPageCache().get("fileIssueTime");
         if(StringUtils.isNotEmpty(fileIssueTime)){

+ 159 - 5
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/print/PrintPerfDetailReportListDataPlugin.java

@@ -2,6 +2,9 @@ package nckd.jxccl.opmc.pm.plugin.form.print;
 
 import kd.bos.algo.DataSet;
 import kd.bos.algo.GroupbyDataSet;
+import kd.bos.algo.JoinType;
+import kd.bos.algo.Row;
+import kd.bos.common.enums.EnableEnum;
 import kd.bos.dataentity.entity.LocaleString;
 import kd.bos.entity.EntityMetadataCache;
 import kd.bos.entity.QueryEntityType;
@@ -11,22 +14,30 @@ import kd.bos.entity.report.AbstractReportColumn;
 import kd.bos.entity.report.AbstractReportListDataPlugin;
 import kd.bos.entity.report.DynamicReportColumnEvent;
 import kd.bos.entity.report.FastFilter;
+import kd.bos.entity.report.IReportBatchQueryInfo;
 import kd.bos.entity.report.ReportColumn;
 import kd.bos.entity.report.ReportQueryParam;
 import kd.bos.mvc.list.ListDataProvider;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.QueryServiceHelper;
 import kd.hr.hbp.business.servicehelper.HRQueryEntityHelper;
+import kd.hr.hbp.common.cache.HRPageCache;
 import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.algo.GroupMaxStrFunction;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.utils.DateUtil;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
 
 import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
 
 /**
 * 年度绩效结果明细-报表取数插件
@@ -42,6 +53,7 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
         super.getDynamicColumns(event);
     }
 
+
     @Override
     public DataSet query(ReportQueryParam reportQueryParam, Object o) throws Throwable {
 
@@ -55,6 +67,8 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
 
         int currentYear = LocalDate.now().getYear() - 1; // 不包含今年
         Date endDate = DateUtil.toDate(LocalDate.of(currentYear, 1, 1));
+
+
         //查询最近5年的数据
         /*QFilter qFilter = new QFilter(PerfManagerFormConstant.NCKD_BEGINYEAR, QCP.less_equals, endDate)
                 .and(new QFilter(PerfManagerFormConstant.NCKD_ENDYEAR, QCP.large_equals, beginDate));*/
@@ -69,13 +83,17 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
         // 执行基础查询
         QueryEntityType queryEntityType = (QueryEntityType) EntityMetadataCache.getDataEntityType("annualperfdetailquery");
         DataSet dataSet = HRQueryEntityHelper.getInstance().getQueryDataSet(queryEntityType,queryFieldBuilder.buildSelect(), new QFilter[]{qFilter}, null);
-
-        dataSet.print( true);
+        DataSet dataSetCopy = dataSet.copy();
+        Set<Long> personIds = new HashSet();
+        while (dataSetCopy.hasNext()) {
+            Row next = dataSetCopy.next();
+            Long personId = next.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+            personIds.add(personId);
+        }
 
         //过滤调实际结束实际之外的年份分录
         DataSet withYearFields = dataSet.filter("year("+String.join(".", PerfManagerFormConstant.PERFMANAGERENTRY, PerfManagerFormConstant.NCKD_APPRAISALYEAR) +") " +
                 "<= year(CASE WHEN nckd_actendyear is not null THEN nckd_actendyear ELSE "+PerfManagerFormConstant.NCKD_ENDYEAR+" end)");
-        withYearFields.print(true);
 
         // 动态添加年份字段;行转列
         int tempIndex = 1;
@@ -86,7 +104,6 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
             );
             tempIndex++;
         }
-        withYearFields.print(true);
 
         // 按分组字段聚合
         GroupbyDataSet groupbyDataSet = withYearFields.groupBy(new String[]{
@@ -116,8 +133,145 @@ public class PrintPerfDetailReportListDataPlugin extends AbstractReportListDataP
         }
         IAppCache nckdPm = AppCache.get("nckd_pm");
 
+        DataSet annualPerfDetailQueryDataSet = groupbyDataSet.finish();
+
+//        获取近5年的岗位信息
+        DataSet positionDateSet = getPositions(fiveYearsAgo, currentYear,personIds);
+        String[] mainTableFieldNames = annualPerfDetailQueryDataSet.getRowMeta().getFieldNames();
+        String[] pivotFieldNames = positionDateSet.getRowMeta().getFieldNames();
+        annualPerfDetailQueryDataSet = annualPerfDetailQueryDataSet.join(positionDateSet, JoinType.LEFT)
+                .on(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY))
+                .select(mainTableFieldNames, pivotFieldNames).finish();
+
+        DataSet perfRankMgmtDataSet = getPerfRankMgmt(personIds, fiveYearsAgo, currentYear);
+
+        mainTableFieldNames = annualPerfDetailQueryDataSet.getRowMeta().getFieldNames();
+        pivotFieldNames = perfRankMgmtDataSet.getRowMeta().getFieldNames();
+        java.util.List<String> filteredFieldNames = new java.util.ArrayList<>();
+        for (String fieldName : pivotFieldNames) {
+            if (!"nckd_person.id".equals(fieldName)) {
+                filteredFieldNames.add(fieldName);
+            }
+        }
+        pivotFieldNames = filteredFieldNames.toArray(new String[0]);
+        return annualPerfDetailQueryDataSet.join(perfRankMgmtDataSet, JoinType.LEFT)
+                .on(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY))
+                .select(mainTableFieldNames, pivotFieldNames).finish();
+    }
+
+    private static DataSet getPerfRankMgmt(Set<Long> personIds, int beginYear, int endYear) {
+        // 年度绩效排名管理
+        QueryFieldBuilder perfRankMgmtFieldBuilder = QueryFieldBuilder.create()
+                .addIdNumberName(FormConstant.NCKD_PERSON)
+                .add("nckd_toprank")
+                .addGroup(new String[]{"nckd_perfrankmgmt"},"nckd_topranks","nckd_theyear");
+        QFilter perfRankMgmtFilter = new QFilter("nckd_isranking", "=", EnableEnum.YES.getCode())
+                .and(FormConstant.NCKD_PERSON, QCP.in, personIds)
+                .and("nckd_perfrankmgmt.nckd_theyear",QCP.large_equals, beginYear)
+                .and("nckd_perfrankmgmt.nckd_theyear",QCP.less_equals, endYear);
+        DataSet perfRankMgmtDataSet = QueryServiceHelper.queryDataSet("PrintPerfDetailReportListDataPlugin_perfRank", "nckd_perfrankmgmtentry", perfRankMgmtFieldBuilder.buildSelect(), new QFilter[]{perfRankMgmtFilter}, perfRankMgmtFieldBuilder.buildOrder(), 10000);
+        int tempIndex = 1;
+        for (int year = beginYear; year <= endYear; year++) {
+            //排名
+            String toprankField = "CASE WHEN nckd_perfrankmgmt.nckd_theyear = "+year+" THEN nckd_toprank ELSE null END";
+            perfRankMgmtDataSet = perfRankMgmtDataSet.addField(toprankField, "perfrankmgmt_topran_temp_" + tempIndex);
+            //总人数
+            String topranksField = "CASE WHEN nckd_perfrankmgmt.nckd_theyear = "+year+" THEN nckd_perfrankmgmt.nckd_topranks ELSE null END";
+            perfRankMgmtDataSet = perfRankMgmtDataSet.addField(topranksField, "perfrankmgmt_topranks_temp_" + tempIndex);
+            tempIndex++;
+        }
+        GroupbyDataSet groupbyDataSet = perfRankMgmtDataSet.groupBy(new String[]{String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY)});
+        tempIndex = 1;
+        for (int year = beginYear; year <= endYear; year++) {
+            groupbyDataSet.max("perfrankmgmt_topran_temp_"+tempIndex, "perfrank_topran_"+tempIndex);
+            groupbyDataSet.max("perfrankmgmt_topranks_temp_"+tempIndex, "perfrank_topranks_"+tempIndex);
+            tempIndex++;
+        }
+        return groupbyDataSet.finish();
+    }
+
+
+    /**
+     * 获取近N年的岗位信息
+     * 用于查询每个员工在指定年份范围内每年年底(12月31日)所在的有效岗位
+     * 当一个员工在同一年有多个有效岗位时,选择结束日期最晚的那个岗位
+     *
+     * @param beginYear 起始年份(包含)
+     * @param endYear   结束年份(包含)
+     * @return 包含每个员工每年岗位信息的数据集
+     */
+    private DataSet getPositions(int beginYear,int endYear,Set<Long> personIds){
+        // 查询全职任职关系,获取员工、岗位、任职起止时间等信息
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.STARTDATE)  // 任职开始日期
+                .add(FormConstant.ENDDATE)    // 任职结束日期
+                .addIdNumberName(FormConstant.POSITION_KEY)  // 岗位信息(ID、编码、名称)
+                .addIdNumberNameWithExtras(new String[]{FormConstant.EMPLOYEE_KEY},FormConstant.EMP_NUMBER_KEY)  // 员工信息(ID、编码、姓名)+ 工号
+                .orderDesc(FormConstant.EMPLOYEE_KEY,FormConstant.STARTDATE);  // 按员工ID和开始日期降序排列
+        // 筛选条件:只查询全职任职(IS_PRIMARY = YES)
+        QFilter qFilter = new QFilter(FormConstant.IS_PRIMARY,QCP.equals, EnableEnum.YES.getCode());
+        if(!personIds.isEmpty()){
+            qFilter = new QFilter(FormConstant.EMPLOYEE_KEY,QCP.in,personIds.toArray());
+        }
+        DataSet dataSet = QueryServiceHelper.queryDataSet("PrintPerfDetailReportListDataPlugin_EMPREL", FormConstant.HRPI_EMPPOSORGREL, queryFieldBuilder.buildSelect(), new QFilter[]{qFilter}, queryFieldBuilder.buildOrder(), 1000000);
+
+        int tempIndex = 1;
+        // 遍历指定年份范围,为每一年生成对应的岗位查询字段
+        for (int year = beginYear; year <= endYear; year++) {
+            // 获取每年12-31 23:59:59作为目标日期,用于判断该员工当年年底的岗位状态
+            Date targetDate = DateUtil.toDate(LocalDateTime.of(year, 12, 31, 23, 59, 59));
+            String targetDateStr = DateUtil.format(targetDate, DateUtil.NORM_DATE_PATTERN); // 使用日期格式而非日期时间格式
+
+            // 创建复合字段:如果在目标日期员工处于有效任职状态,则生成"结束日期|岗位名称"格式的字符串
+            // 这样使用GroupMaxStrFunction时,会根据结束日期选择最晚的那个任职记录
+            //为什么用“00000000000000|”,不用null。因为当GroupMaxStrFunction进行字符串比较时,有效的日期值(如'20231231235959|经济技术部部长')会比'00000000000000|'大,从而被正确选择
+            String combinedField = "CASE WHEN " + FormConstant.STARTDATE + " <= to_date('" + targetDateStr + "','yyyy-MM-dd') " +
+                    "AND " + FormConstant.ENDDATE + " >= to_date('" + targetDateStr + "','yyyy-MM-dd') " +
+                    "THEN CONCAT(TO_CHAR(" + FormConstant.ENDDATE + ", 'YYYYMMDDHHmmss'), '|', " +
+                    String.join(".", FormConstant.POSITION_KEY, FormConstant.NAME_KEY) + ") ELSE '00000000000000|' END";
+
+            // 添加岗位复合字段(包含结束日期和岗位名称)
+            dataSet = dataSet.addField(combinedField, "position_temp_" + tempIndex);
+
+            // 同时创建仅包含日期部分的字段,用于后续解析时去除日期部分,只保留岗位名称
+            String dateField = "CASE WHEN " + FormConstant.STARTDATE + " <= to_date('" + targetDateStr + "','yyyy-MM-dd') " +
+                    "AND " + FormConstant.ENDDATE + " >= to_date('" + targetDateStr + "','yyyy-MM-dd') " +
+                    "THEN CONCAT(TO_CHAR(" + FormConstant.ENDDATE + ", 'YYYYMMDDHHmmss'),'|') ELSE '00000000000000|' END";
+            dataSet = dataSet.addField(dateField, "position_date_" + tempIndex);
+
+//            dataSet = dataSet.addField("'"+year+"'", "position_year_" + tempIndex);
+            tempIndex++;
+        }
+
+        // 行转列处理:按员工ID分组,使用GroupMaxStrFunction获取每个员工每年结束日期最晚的任职记录
+        tempIndex = 1;
+        GroupMaxStrFunction groupMaxStr = new GroupMaxStrFunction();
+        // 按员工ID进行分组聚合
+        GroupbyDataSet groupbyDataSet = dataSet.groupBy(new String[]{String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY)});
+        // 对每一年的数据进行聚合,获取结束日期最晚的任职记录
+        for (int year = beginYear; year <= endYear; year++) {
+            // 获取岗位复合字段的最大值(即结束日期最晚的记录)
+            groupbyDataSet.agg(new GroupMaxStrFunction(),"position_temp_"+tempIndex, "position_max_"+tempIndex);
+            // 同时获取日期字段的最大值,用于后续解析
+            groupbyDataSet.agg(groupMaxStr,"position_date_"+tempIndex, "position_date_max_"+tempIndex);
+            tempIndex++;
+        }
         DataSet result = groupbyDataSet.finish();
-        result.print( true);
+
+        // 解析复合字段,提取真正的岗位名称
+        // 通过去除日期部分(YYYYMMDDHHmmss|)来获取原始的岗位名称
+        tempIndex = 1;
+        for (int year = beginYear; year <= endYear; year++) {
+            final int currentYearIndex = year - beginYear + 1;
+            result = result.addField(
+                    // 如果岗位字段有效(不为默认值'00000000000000|'),则去除日期部分,只保留岗位名称
+                    "CASE WHEN position_max_" + tempIndex + " IS NOT NULL AND position_max_" + tempIndex + " != '00000000000000|' THEN " +
+                            "REPLACE(position_max_" + tempIndex + ",position_date_max_"+tempIndex+",'') " +  // 去除日期部分,得到岗位名称
+                            "ELSE null END",
+                    "position_" + tempIndex  // 最终的岗位名称字段
+            );
+            tempIndex++;
+        }
         return result;
     }