瀏覽代碼

refactor(hr): 重构优秀生考评功能

- 重命名 FromConstant 为 FormConstant
- 新增 ProgressCallback 接口用于进度回调
- 重构 CreateEvalQuestService 类,改名为 batchGenerateEvalQuest
- 优化同级评分人获取逻辑,支持递归查找上级组织人员
- 新增其他部门同级评分人获取逻辑- 重构 HonorStudentConstant 类,整合常量定义
wyc 1 周之前
父節點
當前提交
a56eb1e4c6
共有 15 個文件被更改,包括 658 次插入132 次删除
  1. 11 1
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/FormConstant.java
  2. 19 0
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/schedule/ProgressCallback.java
  3. 1 0
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/org/helper/AdminOrgHelper.java
  4. 3 0
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/org/helper/PersonHelper.java
  5. 11 11
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/orm/helper/QFilterCommonHelper.java
  6. 389 78
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/business/CreateEvalQuestService.java
  7. 71 2
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/common/HonorStudentConstant.java
  8. 52 8
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/CreateEvalQuestFormPlugin.java
  9. 33 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/EvalQuestFormPlugin.java
  10. 3 4
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/EvalQuestTplListPlugin.java
  11. 55 18
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/task/CreateEvalQuestTask.java
  12. 2 2
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/common/PerfManagerFormConstant.java
  13. 4 4
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/PerfManagerBillPlugin.java
  14. 2 2
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/PerfManagerListPlugin.java
  15. 2 2
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/SalAdjtTackerListPlugin.java

+ 11 - 1
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/FromConstant.java → code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/FormConstant.java

@@ -6,7 +6,7 @@ package nckd.jxccl.base.common.constant;
  * @date 2025/6/9 14:55
  * @version 1.0
  */
-public class FromConstant {
+public class FormConstant {
 
     /** ID标识 */
     public static final String ID_KEY = "id";
@@ -60,6 +60,10 @@ public class FromConstant {
      * 组织
      **/
     public static final String ORG_KEY = "ORG";
+    /** 创建组织*/
+    public static final String CREATEORG_KEY = "createorg";
+    /** 所属单位*/
+    public static final String USEORG_KEY = "useorg";
     /**
      * 分录行号
      **/
@@ -101,4 +105,10 @@ public class FromConstant {
     public static final String REFRESH = "refresh";
     /** 多选基础资料*/
     public static final String BASEDATAID_KEY = "fbasedataid";
+    /** 当前user id*/
+    public static final String CURR_USER_ID = "CURRUSER_ID";
+    /** 当前org id*/
+    public static final String CURR_ORG_ID = "ORG_ID";
+    /** 当前org id*/
+    public static final String DEP_EMP = "DEPEMP";
 }

+ 19 - 0
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/schedule/ProgressCallback.java

@@ -0,0 +1,19 @@
+package nckd.jxccl.base.common.schedule;
+
+/**
+* 进度回调接口
+* @author W.Y.C
+* @date 2025/7/12 18:02
+* @version 1.0
+*/
+public interface ProgressCallback {
+
+    /**
+     * 每处理完一个元素时调用
+     * @param processedCount 已处理的元素数量
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/07/12 18:02
+     */
+    void onProgress(int processedCount);
+}

+ 1 - 0
code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/org/helper/AdminOrgHelper.java

@@ -20,6 +20,7 @@ import java.util.Set;
  */
 public class AdminOrgHelper {
 
+    public static final String BOS_ORG = "bos_org";
     /**
      * 获取当前组织下的所有子组织
      * @param adminOrgId 组织id

+ 3 - 0
code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/org/helper/PersonHelper.java

@@ -1,8 +1,11 @@
 package nckd.jxccl.base.org.helper;
 
+import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
 import kd.bos.servicehelper.QueryServiceHelper;
+import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
 import nckd.jxccl.base.orm.helper.QFilterPersonHelper;
 import org.apache.commons.lang3.StringUtils;

+ 11 - 11
code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/orm/helper/QFilterCommonHelper.java

@@ -7,7 +7,7 @@ import kd.bos.logging.Log;
 import kd.bos.logging.LogFactory;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
-import nckd.jxccl.base.common.constant.FromConstant;
+import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.utils.ConvertUtil;
 
 import java.util.List;
@@ -43,7 +43,7 @@ public final class QFilterCommonHelper {
      * @date: 2025/07/07 09:20
      */
     public static QFilter getEnableFilter(boolean enable) {
-        return new QFilter(FromConstant.ENABLE, QCP.equals, enable ? EnableEnum.YES.getCode() : EnableEnum.NO.getCode());
+        return new QFilter(FormConstant.ENABLE, QCP.equals, enable ? EnableEnum.YES.getCode() : EnableEnum.NO.getCode());
     }
 
     /**
@@ -54,7 +54,7 @@ public final class QFilterCommonHelper {
      * @date: 2025/07/07 09:20
      */
     public static QFilter getCurrentVersionFilter() {
-        return new QFilter(FromConstant.IS_CURRENT_VERSION, QCP.equals, EnableEnum.YES.getCode());
+        return new QFilter(FormConstant.IS_CURRENT_VERSION, QCP.equals, EnableEnum.YES.getCode());
     }
 
     /**
@@ -65,7 +65,7 @@ public final class QFilterCommonHelper {
      * @date: 2025/07/07 09:20
      */
     public static QFilter getStatusFilter() {
-        return new QFilter(FromConstant.STATUS, QCP.equals, StatusEnum.C.toString());
+        return new QFilter(FormConstant.STATUS, QCP.equals, StatusEnum.C.toString());
     }
 
     /**
@@ -76,7 +76,7 @@ public final class QFilterCommonHelper {
      * @date: 2025/07/07 09:20
      */
     public static QFilter getDataStatusFilter() {
-        return new QFilter(FromConstant.DATA_STATUS, QCP.equals, EnableEnum.YES.getCode());
+        return new QFilter(FormConstant.DATA_STATUS, QCP.equals, EnableEnum.YES.getCode());
     }
 
     /**
@@ -87,7 +87,7 @@ public final class QFilterCommonHelper {
      * @date: 2025/07/07 09:20
      */
     public static QFilter getBusinessStatusFilter() {
-        return new QFilter(FromConstant.BUSINESS_STATUS, QCP.equals, EnableEnum.YES.getCode());
+        return new QFilter(FormConstant.BUSINESS_STATUS, QCP.equals, EnableEnum.YES.getCode());
     }
 
     /**
@@ -98,7 +98,7 @@ public final class QFilterCommonHelper {
      * @date: 2025/07/07 09:20
      */
     public static QFilter getInitStatusFinishFilter() {
-        return new QFilter(FromConstant.INIT_STATUS, QCP.equals, BillStatus.C.ordinal()+"");
+        return new QFilter(FormConstant.INIT_STATUS, QCP.equals, BillStatus.C.ordinal()+"");
     }
 
     /**
@@ -108,7 +108,7 @@ public final class QFilterCommonHelper {
      * @date: 2025/07/07 17:30
      */
     public static QFilter getBillStatusFilter() {
-        return new QFilter(FromConstant.BILL_STATUS_KEY, QCP.equals, BillStatus.C.toString());
+        return new QFilter(FormConstant.BILL_STATUS_KEY, QCP.equals, BillStatus.C.toString());
     }
 
     /**
@@ -119,7 +119,7 @@ public final class QFilterCommonHelper {
      * @date: 2025/07/07 09:20
      */
     public static QFilter getNumberEqFilter(String number) {
-        return new QFilter(FromConstant.NUMBER_KEY, QCP.equals, number);
+        return new QFilter(FormConstant.NUMBER_KEY, QCP.equals, number);
     }
 
     /**
@@ -130,7 +130,7 @@ public final class QFilterCommonHelper {
      * @date: 2025/07/07 09:20
      */
     public static QFilter getIdEqFilter(Object id) {
-        return new QFilter(FromConstant.ID_KEY, QCP.equals, ConvertUtil.toLong(id));
+        return new QFilter(FormConstant.ID_KEY, QCP.equals, ConvertUtil.toLong(id));
     }
 
     /**
@@ -141,6 +141,6 @@ public final class QFilterCommonHelper {
      * @date: 2025/07/07 09:20
      */
     public static QFilter getIdInFilter(List<Long> idList) {
-        return new QFilter(FromConstant.ID_KEY, QCP.in, idList);
+        return new QFilter(FormConstant.ID_KEY, QCP.in, idList);
     }
 }

+ 389 - 78
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/business/CreateEvalQuestService.java

@@ -1,30 +1,44 @@
 package nckd.jxccl.hr.hstu.business;
 
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.OperateOption;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.entity.MainEntityType;
+import kd.bos.entity.constant.StatusEnum;
+import kd.bos.entity.operate.OperateOptionConst;
+import kd.bos.entity.operate.result.IOperateInfo;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.exception.KDBizException;
 import kd.bos.logging.Log;
 import kd.bos.logging.LogFactory;
 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.bos.servicehelper.user.UserServiceHelper;
 import kd.hr.hbp.business.openservicehelper.hrpi.HRPIDepempServiceHelper;
-import nckd.jxccl.base.common.constant.FromConstant;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.schedule.ProgressCallback;
 import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.common.utils.DateUtil;
+import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.base.org.helper.AdminOrgHelper;
 import nckd.jxccl.base.org.helper.PersonHelper;
 import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
 import nckd.jxccl.hr.hstu.common.HonorStudentConstant;
-
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.StringJoiner;
-
 /**
- * 【优秀生】-生成评价问卷
+ * 【优秀生】-生成评价问卷服务类
  * @author W.Y.C
  * @date 2025/7/2 16:41
  * @version 1.0
@@ -33,7 +47,6 @@ public class CreateEvalQuestService {
 
     private static final Log logger = LogFactory.getLog(CreateEvalQuestService.class);
 
-
     /**
      * 批量生成评价问卷
      * @param evalQuestTplId 评价问卷模板Id
@@ -43,80 +56,212 @@ public class CreateEvalQuestService {
      * @author W.Y.C
      * @date: 2025/07/02 17:24
      */
-    public static void createEvalQuest(Long evalQuestTplId, DynamicObject selectedEvalRule, DynamicObjectCollection selectedPerson) {
+    public static void batchGenerateEvalQuest(Long evalQuestTplId, DynamicObject selectedEvalRule, DynamicObjectCollection selectedPerson, ProgressCallback callback, Long userId,Long orgId) {
 
         //同级人员取数条件:
         //1.最新一条党政职务履历为副处级及以上人员不作为同级取数
         //2.工作性质大类过滤掉编码为H01(居休)、H02(退养)、H03(随任)、H04(女工长假)、H05(长期工伤病休)、H06(长期病休)、H07(其他不在岗)、J(外派)的员工,不做为同级
         //3.除去领导和上级
         //4.入职超半年、职位非协理员
-        logger.info("【职位体系】-生成综合评测问卷-开始");
-
-
-        StringJoiner ruleColum = new StringJoiner(",");
-        //其他同级评分人比例
-        ruleColum.add(HonorStudentConstant.NCKD_PROPORTION);
-        //同级评分人上限人数
-        ruleColum.add(HonorStudentConstant.NCKD_UPPER);
-        //同级评分人下限人数
-        ruleColum.add(HonorStudentConstant.NCKD_LOWER);
-        selectedEvalRule = QueryServiceHelper.queryOne(HonorStudentConstant.EVALUATIONRULE_ENTITYID, ruleColum.toString(), new QFilter[]{QFilterCommonHelper.getIdEqFilter(selectedEvalRule.getPkValue())});
-        int proportion = selectedEvalRule.getInt(HonorStudentConstant.NCKD_PROPORTION);
-        int upper = selectedEvalRule.getInt(HonorStudentConstant.NCKD_UPPER);
-        int lower = selectedEvalRule.getInt(HonorStudentConstant.NCKD_LOWER);
-        int otherLower = lower * proportion / 100;
-        try{
-            // 用工关系状态:正式员工、试用员工、见习期员工
-            for (DynamicObject row : selectedPerson) {
-                DynamicObject depEmpRow = (DynamicObject)row.getDynamicObject(FromConstant.BASEDATAID_KEY);
-
-                Map<String, Object> depEmp = HRPIDepempServiceHelper.getDepemp(ConvertUtil.toLong(depEmpRow.getPkValue()));
-                Long id = ConvertUtil.toLong(depEmp.get("id"));
-                //获取当前人员的组织
-                Long adminOrgId = ConvertUtil.toLong(depEmp.get("adminorg_id"));
-                //获取当前人员的岗位
-                Long positionId = ConvertUtil.toLong(depEmp.get("position_id"));
-
-                //获取领导(获取上上级岗位中的人员作为领导)
-                DynamicObject parentParentPosition = QueryServiceHelper.queryOne("hbpm_positionhr","parent.parent.id as parentId",new QFilter[]{QFilterCommonHelper.getIdEqFilter(positionId)});
-                QFilter positionFilter = new QFilter("position.id", QCP.equals, parentParentPosition.getLong("parentId"));
-                DynamicObjectCollection leadColl =  PersonHelper.getDepEmpEffectivePerson("id,person.name",positionFilter);
+        logger.info("【优秀生考评】-生成综合评测问卷-开始");
+
+        try {
+            // 获取当前用户ID和主组织ID(创建组织)
+            Long currUserId = userId;
+            if(currUserId == null) {
+                currUserId = RequestContext.get().getCurrUserId();
+            }
+            Long currOrgId = orgId;
+            if(currOrgId == null) {
+                currOrgId = RequestContext.get().getOrgId();
+            }
+
+            DynamicObject currUser = UserServiceHelper.getUserInfoByID(currUserId, HonorStudentConstant.ID_KEY);
+
+            // 获取实体元数据
+            MainEntityType bosOrgEntityType = EntityMetadataCache.getDataEntityType(AdminOrgHelper.BOS_ORG);
+            // 基于元数据创建 DynamicObject
+            DynamicObject org = new DynamicObject(bosOrgEntityType);
+            org.set(FormConstant.ID_KEY,currOrgId);
+
+            // 配置操作选项(忽略警告)
+            OperateOption option = OperateOption.create();
+            // 忽略警告
+            option.setVariableValue(OperateOptionConst.IGNOREWARN, Boolean.TRUE.toString());
+            // 忽略交互提示
+            option.setVariableValue(OperateOptionConst.IGNOREINTERACTION, Boolean.TRUE.toString());
+            //单据页面操作
+            //option.setVariableValue(OperateOptionConst.ISFORMVIEWOPERATION, Boolean.TRUE.toString());
+
+            DynamicObject evalQuestTpl = QueryServiceHelper.queryOne(HonorStudentConstant.NCKD_EVALQUESTTPL, "id",new QFilter[]{QFilterCommonHelper.getIdEqFilter(evalQuestTplId)});
+            if (evalQuestTpl == null) {
+                throw new KDBizException("评价问卷模板未找到");
+            }
+
+            StringJoiner ruleColum = new StringJoiner(",");
+            ruleColum.add(FormConstant.ID_KEY);
+            //其他同级评分人比例
+            ruleColum.add(HonorStudentConstant.NCKD_PROPORTION);
+            //同级评分人上限人数
+            ruleColum.add(HonorStudentConstant.NCKD_UPPER);
+            //同级评分人下限人数
+            ruleColum.add(HonorStudentConstant.NCKD_LOWER);
+            selectedEvalRule = QueryServiceHelper.queryOne(HonorStudentConstant.EVALUATIONRULE_ENTITYID, ruleColum.toString(), new QFilter[]{QFilterCommonHelper.getIdEqFilter(selectedEvalRule.getLong(HonorStudentConstant.ID_KEY))});
+            if (selectedEvalRule == null) {
+                throw new KDBizException("评价规则未找到");
+            }
+            
+            int proportion = selectedEvalRule.getInt(HonorStudentConstant.NCKD_PROPORTION);
+            int upper = selectedEvalRule.getInt(HonorStudentConstant.NCKD_UPPER);
+            int lower = selectedEvalRule.getInt(HonorStudentConstant.NCKD_LOWER);
+            
+            // 参数验证
+            if (proportion < 0 || proportion > 100) {
+                throw new KDBizException("其他同级评分人比例必须在0-100之间");
+            }
+            if (lower < 0 || upper < lower) {
+                throw new KDBizException("同级评分人上限人数必须大于等于下限人数且都大于等于0");
+            }
+            
+            //根据比例计算出的其他部门同级评分人的最小人数(同级评分人下限人数*其他同级评分人比例/100=其他同级评分人下限人数)
+            int otherLower = (lower * proportion) / 100;
+
+            for (int i = 0; i < selectedPerson.size(); i++) {
+                DynamicObject row = selectedPerson.get(i);
+                Long personId = null;
+                String personName = null;
+                if(row.containsProperty(FormConstant.BASEDATAID_KEY)){
+                    //获取页面选择的多选基础数据可能会被存放在fbasedataid中
+                    DynamicObject depEmpRow = (DynamicObject)row.getDynamicObject(FormConstant.BASEDATAID_KEY);
+                    if (depEmpRow == null) {
+                        logger.warn("跳过无效的人员数据,索引:{}", i);
+                        continue;
+                    }
+                    personId = ConvertUtil.toLong(depEmpRow.getPkValue());
+                    personName = depEmpRow.getString("person.name");
+                }else{
+                    personId = row.getLong(HonorStudentConstant.ID_KEY);
+                    personName = row.getString("person.name");
+                }
 
-                //获取上级(获取上级岗位中的人员作为上级)
-                DynamicObject parentPosition = QueryServiceHelper.queryOne("hbpm_positionhr","parent.id as parentId",new QFilter[]{QFilterCommonHelper.getIdEqFilter(positionId)});
-                positionFilter = new QFilter("position.id", QCP.equals, parentPosition.getLong("parentId"));
-                DynamicObjectCollection superiorColl = PersonHelper.getDepEmpEffectivePerson("id,person.name",positionFilter);
+                if (personId == null) {
+                    logger.warn("跳过人员ID为空的数据,索引:{}", i);
+                    continue;
+                }
 
-                //获取同级人员
-                Set<Long> excludePersons = new HashSet<>(leadColl.size()+superiorColl.size());
-                excludePersons.add(id);
-                leadColl.forEach(dynamicObject -> excludePersons.add(dynamicObject.getLong(HonorStudentConstant.ID_KEY)));
-                superiorColl.forEach(dynamicObject -> excludePersons.add(dynamicObject.getLong(HonorStudentConstant.ID_KEY)));
-                DynamicObjectCollection peers = getPeers(adminOrgId,excludePersons, proportion,upper, lower, otherLower);
+                MainEntityType depempEntityType = EntityMetadataCache.getDataEntityType(PersonHelper.DEP_EMP_ENTITY_ID);
+                DynamicObject person = new DynamicObject(depempEntityType);
+                person.set(FormConstant.ID_KEY,personId);
 
+                // 主单据创建
+                DynamicObject mainObj = BusinessDataServiceHelper.newDynamicObject(HonorStudentConstant.NCKD_EVALQUEST);
+                //当表单是必填时必须要传结构对象,非必填的只传id就行
+                mainObj.set(HonorStudentConstant.NCKD_EVALPERSON, person);
+                mainObj.set(HonorStudentConstant.NCKD_PERIODSTATE, 0);
+                mainObj.set(HonorStudentConstant.NCKD_EVALUATIONRULE, selectedEvalRule.getLong(HonorStudentConstant.ID_KEY));
+                mainObj.set(HonorStudentConstant.NCKD_EVALQUESTTPL, evalQuestTplId);
+                mainObj.set(HonorStudentConstant.CREATEORG_KEY, org);
+                mainObj.set(HonorStudentConstant.USEORG_KEY, org);
+                mainObj.set(HonorStudentConstant.CREATE_TIME_KEY, new Date());
+                mainObj.set(HonorStudentConstant.CREATOR_KEY, currUser);
+                //默认为已审核
+                mainObj.set(HonorStudentConstant.STATUS, StatusEnum.C.toString());
+                //默认为可用
+                mainObj.set(HonorStudentConstant.ENABLE, EnableEnum.YES.getCode());
 
-                System.out.println();
+
+                DynamicObjectCollection entryEntity = fetchExpectedScorers(row, proportion, upper, lower, otherLower, Boolean.TRUE);
+                if (entryEntity == null || entryEntity.isEmpty()) {
+                    logger.warn("未能获取到有效的评分人列表,人员ID:{}", personId);
+                    continue;
+                }
+                
+                mainObj.set(HonorStudentConstant.NCKD_EVALQUESTENTRY,entryEntity);
+                OperationResult operationResult = SaveServiceHelper.saveOperate(FormConstant.SAVE_KEY,HonorStudentConstant.NCKD_EVALQUEST, new DynamicObject[]{mainObj}, option);
+                if (!operationResult.isSuccess()) {
+                    StringJoiner errorMsg = new StringJoiner(";");
+                    for (IOperateInfo error : operationResult.getAllErrorOrValidateInfo()) {
+                        errorMsg.add(error.getMessage());
+                        logger.error("【优秀生考评】-评价对象【{}】生成综合评测问卷-异常,异常信息:{}",personName,error.getMessage());
+                    }
+                    throw new KDBizException(StrFormatter.format("评价对象【{}】生成综合评测问卷失败,原因:{}",personName,errorMsg.toString()));
+                }
+                if(callback != null) {
+                    callback.onProgress(i);
+                }
             }
 
-        }catch (Exception e){
-            logger.error("【职位体系】-生成综合评测问卷-异常",e);
+        }catch (KDBizException e){
+            logger.error("【优秀生考评】-生成综合评测问卷-业务异常",e);
+            throw e;
+        } catch (Exception e){
+            logger.error("【优秀生考评】-生成综合评测问卷-系统异常",e);
+            throw e;
         }
-        logger.info("【职位体系】-生成综合评测问卷-结束");
+        logger.info("【优秀生考评】-生成综合评测问卷-结束");
     }
 
     /**
-     * 获取同级人员
-     *  1.最新一条党政职务履历为副处级及以上人员不作为同级取数
-     *  2.工作性质大类过滤掉编码为H01(居休)、H02(退养)、H03(随任)、H04(女工长假)、H05(长期工伤病休)、H06(长期病休)、H07(其他不在岗)、J(外派)的员工,不做为同级
-     *  3.除去领导和上级
-     *  4.入职超半年、职位非协理员
-     * @param adminOrgId 当前组织ID
-     * @param excludePersons 排除的ID
-     * @return: kd.bos.dataentity.entity.DynamicObjectCollection
+     * 获取预计参与问卷打分的用户列表
+     * @param person 评分人员
+     * @param proportion 其他同级评分人比例
+     * @param upper 同级评分人上限人数
+     * @param lower 同级评分人下限人数
+     * @param otherLower 根据比例计算出的其他部门同级评分人的最小人数(同级评分人下限人数*其他同级评分人比例/100=其他同级评分人下限人数)
+     * @param isLeadAndSuperior 是否获取上级和领导
+     * @return: void
      * @author W.Y.C
-     * @date: 2025/07/08 17:48
+     * @date: 2025/07/10 17:38
      */
-    private static DynamicObjectCollection getPeers(Long adminOrgId,Set<Long> excludePersons,int proportion,int upper,int lower,int otherLower){
+    public static DynamicObjectCollection fetchExpectedScorers(DynamicObject person,int proportion,int upper,int lower,int otherLower,boolean isLeadAndSuperior) {
+        Long personId = null;
+        if(person.containsProperty(FormConstant.BASEDATAID_KEY)){
+            //获取页面选择的数据可能会被存放在fbasedataid中
+            DynamicObject depEmpRow = (DynamicObject)person.getDynamicObject(FormConstant.BASEDATAID_KEY);
+            if (depEmpRow == null) {
+                logger.warn("获取人员信息失败,BASEDATAID_KEY为空");
+                return new DynamicObjectCollection();
+            }
+            personId = ConvertUtil.toLong(depEmpRow.getPkValue());
+        }else{
+            personId = person.getLong(HonorStudentConstant.ID_KEY);
+        }
+        
+        if (personId == null) {
+            logger.warn("人员ID为空");
+            return new DynamicObjectCollection();
+        }
+
+        Map<String, Object> depEmp = HRPIDepempServiceHelper.getDepemp(personId);
+        if (depEmp == null) {
+            logger.warn("获取人员信息失败,人员ID:{}", personId);
+            return new DynamicObjectCollection();
+        }
+        
+        Long id = ConvertUtil.toLong(depEmp.get(FormConstant.ID_KEY));
+        //获取当前人员的组织
+        Long adminOrgId = ConvertUtil.toLong(depEmp.get("adminorg_id"));
+        //获取当前人员的岗位
+        Long positionId = ConvertUtil.toLong(depEmp.get("position_id"));
+
+        DynamicObjectCollection leadColl = new DynamicObjectCollection();
+        DynamicObjectCollection superiorColl = new DynamicObjectCollection();
+        if(isLeadAndSuperior) {
+            //1:获取领导(获取上上级岗位中的人员作为领导)
+            DynamicObject parentParentPosition = QueryServiceHelper.queryOne("hbpm_positionhr", "parent.parent.id as parentId", new QFilter[]{QFilterCommonHelper.getIdEqFilter(positionId)});
+            if (parentParentPosition != null && parentParentPosition.getLong("parentId") != 0) {
+                QFilter positionFilter = new QFilter("position.id", QCP.equals, parentParentPosition.getLong("parentId"));
+                leadColl = PersonHelper.getDepEmpEffectivePerson("id,person.name", positionFilter);
+            }
+
+            //2:获取上级(获取上级岗位中的人员作为上级)
+            DynamicObject parentPosition = QueryServiceHelper.queryOne("hbpm_positionhr", "parent.id as parentId", new QFilter[]{QFilterCommonHelper.getIdEqFilter(positionId)});
+            if (parentPosition != null && parentPosition.getLong("parentId") != 0) {
+                QFilter positionFilter = new QFilter("position.id", QCP.equals, parentPosition.getLong("parentId"));
+                superiorColl = PersonHelper.getDepEmpEffectivePerson("id,person.name", positionFilter);
+            }
+        }
+
         /*
         同级人数取数逻辑
@@ -124,38 +269,115 @@ public class CreateEvalQuestService {
         ├─ 1:查询当前组织内的同级员工(排除上级、自己)
         │   ├─ 1.1:若人数 >= lower → 只取前 upper 个
-        │   └─ 1.2:否则 继续向上级组织查找(最多 10 层)
+        │   └─ 1.2:否则 继续向上级组织查找【上级组织的人员】(最多 10 层)
         ├─ 2:查询同级其他部门在职人员
+        │   ├─ 2.1:若人数 >= otherLower → 只取前 otherLower 个
+        │   └─ 2.2:否则 继续向上级组织查找【上级组织下所有组织的人员】(最多 10 层)
         └─ 返回最终的同级评分人列表
         */
+        //3:查询当前组织内的同级员工
+        Set<Long> excludePersons = new HashSet<>();
+        excludePersons.add(id);
+        if(isLeadAndSuperior) {
+            leadColl.forEach(dynamicObject -> excludePersons.add(dynamicObject.getLong(HonorStudentConstant.ID_KEY)));
+            superiorColl.forEach(dynamicObject -> excludePersons.add(dynamicObject.getLong(HonorStudentConstant.ID_KEY)));
+        }
+        DynamicObjectCollection peers = getPeers(adminOrgId,excludePersons, proportion,upper, lower, otherLower);
+        peers.forEach(dynamicObject -> excludePersons.add(dynamicObject.getLong(HonorStudentConstant.ID_KEY)));
+
+        //4:同级其他部门在职人员
+        DynamicObjectCollection otherPeers = new DynamicObjectCollection();
+        if(otherLower > 0) {
+            otherPeers = getOtherPeers(adminOrgId, excludePersons, otherLower);
+        }
+
+
+        // 主单据创建
+        DynamicObject mainObj = BusinessDataServiceHelper.newDynamicObject(HonorStudentConstant.NCKD_EVALQUEST);
+        //分录创建
+        DynamicObjectCollection entryEntity = mainObj.getDynamicObjectCollection(HonorStudentConstant.NCKD_EVALQUESTENTRY);
+        ////1:领导、2:上级、3:同级
+        if(isLeadAndSuperior) {
+            builderEvalQuestEntry(leadColl, "1", entryEntity);
+            builderEvalQuestEntry(superiorColl, "2", entryEntity);
+        }
+        builderEvalQuestEntry(peers, "3", entryEntity);
+        builderEvalQuestEntry(otherPeers, "3", entryEntity);
+
+
+        return entryEntity;
+    }
+    
+    private static final int MAX_RECURSION_DEPTH = 10;
+
+    /**
+     * 获取同级人员
+     *  1.最新一条党政职务履历为副处级及以上人员不作为同级取数
+     *  2.工作性质大类过滤掉编码为H01(居休)、H02(退养)、H03(随任)、H04(女工长假)、H05(长期工伤病休)、H06(长期病休)、H07(其他不在岗)、J(外派)的员工,不做为同级
+     *  3.除去领导和上级
+     *  4.入职超半年、职位非协理员
+     * @param adminOrgId 当前组织ID
+     * @param excludePersons 排除的ID
+     * @return: kd.bos.dataentity.entity.DynamicObjectCollection
+     * @author W.Y.C
+     * @date: 2025/07/08 17:48
+     */
+    private static DynamicObjectCollection getPeers(Long adminOrgId,Set<Long> excludePersons,int proportion,int upper,int lower,int otherLower){
         //最终的结果
         DynamicObjectCollection finalResult = new DynamicObjectCollection();
 
         //------ 基本条件 begin------
-        QFilter baseFilter = new QFilter(HonorStudentConstant.ID_KEY, QCP.not_in, excludePersons);
-        //入职超半年
-        baseFilter.and(new QFilter("startdate",QCP.less_equals, DateUtil.minusMonths(DateUtil.now(),6)));
-        //职位非协理员
-        baseFilter.and(new QFilter("position.name", QCP.not_like, "%协理员%"));
+        QFilter baseFilter = getBaseFilter(excludePersons);
         //------ 基本条件 end------
 
         //1.查询当前组织内的同级员工
         QFilter adminOrgFilter = new QFilter("adminorg.id", QCP.equals, adminOrgId);
         DynamicObjectCollection depEmpEffectivePerson = PersonHelper.getDepEmpEffectivePerson("id,person.name", baseFilter, adminOrgFilter);
 
-        //当前组织人数小于下限人数,继续向上级组织查找直到 >= lower
+        //2.当前组织人数小于下限人数,继续向上级组织查找直到 >= lower
         if(depEmpEffectivePerson.size() < lower){
-            recursionGetParentPeers(adminOrgId, depEmpEffectivePerson, baseFilter, lower, 1);
+            Integer count = recursionGetPeers(adminOrgId, depEmpEffectivePerson, baseFilter, lower, 1);
+            logger.info("【优秀生考评】-向上级组织查找次数:{}",count);
         }
-        //截取前 upper 个同级人员
+        //打乱顺序随机抽取前 upper 个
         Collections.shuffle(depEmpEffectivePerson);
-        List<DynamicObject> dynamicObjects = depEmpEffectivePerson.subList(0, depEmpEffectivePerson.size() > upper ? upper-1 : depEmpEffectivePerson.size() -1);
+        int endIndex = Math.min(depEmpEffectivePerson.size(), upper);
+        List<DynamicObject> dynamicObjects = depEmpEffectivePerson.subList(0, endIndex);
         finalResult.addAll(dynamicObjects);
 
-        //2.查询同级其他部门在职人员
+        return finalResult;
+    }
+
+    /**
+     * 查询同级行政组织下的人员(排除同级确认的人员)
+     * @param adminOrgId 当前人员的行政组织id
+     * @param excludePersons 需要排除的人员
+     * @param otherLower
+     * @return: kd.bos.dataentity.entity.DynamicObjectCollection
+     * @author W.Y.C
+     * @date: 2025/07/10 15:14
+     */
+    private static DynamicObjectCollection getOtherPeers(Long adminOrgId,Set<Long> excludePersons,int otherLower){
+        //最终的结果
+        DynamicObjectCollection finalResult = new DynamicObjectCollection();
 
+        //------ 基本条件 begin------
+        QFilter baseFilter = getBaseFilter(excludePersons);
+        //------ 基本条件 end------
+        Long parentOrg = AdminOrgHelper.getParentOrg(adminOrgId);
+        QFilter adminOrgFilter = new QFilter("adminorg.parent.id", QCP.equals, parentOrg);
+        adminOrgFilter.and(new QFilter("adminorg.id", QCP.not_equals, adminOrgId));
+        DynamicObjectCollection depEmpEffectivePerson = PersonHelper.getDepEmpEffectivePerson("id,person.name,adminorg.name", baseFilter, adminOrgFilter);
+        if(depEmpEffectivePerson.size() < otherLower){
+            Integer count = recursionGetParentPeers(parentOrg, depEmpEffectivePerson, baseFilter, otherLower, 1);
+            logger.info("【优秀生考评】-向上级组织查找次数:{}",count);
+        }
+        Collections.shuffle(depEmpEffectivePerson);
+        int endIndex = Math.min(depEmpEffectivePerson.size(), otherLower);
+        List<DynamicObject> dynamicObjects = depEmpEffectivePerson.subList(0, endIndex);
+        finalResult.addAll(dynamicObjects);
         return finalResult;
     }
 
@@ -170,19 +392,108 @@ public class CreateEvalQuestService {
      * @author W.Y.C
      * @date: 2025/07/09 16:03
      */
-    private static void recursionGetParentPeers(Long adminOrgId,DynamicObjectCollection persons,QFilter baseFilter,int lower,Integer count){
+    private static Integer recursionGetPeers(Long adminOrgId,DynamicObjectCollection persons,QFilter baseFilter,int lower,Integer count){
+        if(count > MAX_RECURSION_DEPTH) {
+            logger.warn("达到最大递归深度{}, 组织ID: {}", MAX_RECURSION_DEPTH, adminOrgId);
+            return count;
+        }
+        
         Long parentOrg = AdminOrgHelper.getParentOrg(adminOrgId);
         if(parentOrg == null){
-            return;
+            return count;
         }
+        // 创建一个查询条件,用于筛选属于上级组织的人员
         QFilter adminOrgFilter = new QFilter("adminorg.id", QCP.equals, parentOrg);
-        DynamicObjectCollection depEmpEffectivePerson = PersonHelper.getDepEmpEffectivePerson("id,person.name", baseFilter, adminOrgFilter);
+        DynamicObjectCollection depEmpEffectivePerson = PersonHelper.getDepEmpEffectivePerson("id,person.name,adminorg.name", baseFilter, adminOrgFilter);
         persons.addAll(depEmpEffectivePerson);
-        if(persons.size() >= lower || count > 10){
-            return;
+        if(persons.size() >= lower || count > MAX_RECURSION_DEPTH){
+            return count;
         }else{
             count++;
-            recursionGetParentPeers(parentOrg,persons,baseFilter,lower,count);
+            if(count == MAX_RECURSION_DEPTH) {
+                logger.warn("即将达到最大递归深度, 组织ID: {}", adminOrgId);
+            }
+            return recursionGetPeers(parentOrg,persons,baseFilter,lower,count);
         }
     }
+
+    /**
+     * 递归获取指定管理组织及其所有上级管理组织下的有效人员
+     *
+     * 该方法从指定的管理组织开始,向上遍历其所有上级管理组织,并收集每个管理组织下的有效人员
+     * 直到达到指定的人员数量下限或超过最大递归深度时停止
+     *
+     * @param adminOrgId 当前管理组织的ID,用于启动递归
+     * @param persons 用于存储收集到的所有有效人员的集合
+     * @param baseFilter 基础过滤条件,用于筛选有效人员
+     * @param lower 人员数量下限,当收集到的人员数量达到或超过这个值时停止递归
+     * @param count 当前递归计数,用于防止无限递归,最大为10
+     * @return: java.lang.Integer
+     * @author W.Y.C
+     * @date: 2025/07/10 16:02
+     */
+    private static Integer recursionGetParentPeers(Long adminOrgId,DynamicObjectCollection persons,QFilter baseFilter,int lower,Integer count){
+        if(count > MAX_RECURSION_DEPTH) {
+            logger.warn("达到最大递归深度{}, 组织ID: {}", MAX_RECURSION_DEPTH, adminOrgId);
+            return count;
+        }
+        
+        Long parentOrg = AdminOrgHelper.getParentOrg(adminOrgId);
+        if(parentOrg == null){
+            return count;
+        }
+        // 构建用于筛选上级管理组织下有效人员的过滤条件
+        QFilter adminOrgFilter = new QFilter("adminorg.parent.id", QCP.equals, parentOrg);
+        DynamicObjectCollection depEmpEffectivePerson = PersonHelper.getDepEmpEffectivePerson("id,person.name,adminorg.name", baseFilter, adminOrgFilter);
+        persons.addAll(depEmpEffectivePerson);
+        if(persons.size() >= lower || count > MAX_RECURSION_DEPTH){
+            return count;
+        }else{
+            count++;
+            if(count == MAX_RECURSION_DEPTH) {
+                logger.warn("即将达到最大递归深度, 组织ID: {}", adminOrgId);
+            }
+            return recursionGetParentPeers(parentOrg,persons,baseFilter,lower,count);
+        }
+    }
+
+    /**
+     * 构建问卷分录
+     * @param persons 评分人
+     * @param perfPlanRole 评分角色
+     * @param entryEntity 分录集合
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/07/13 18:52
+     */
+    private static void builderEvalQuestEntry(DynamicObjectCollection persons,String perfPlanRole,DynamicObjectCollection entryEntity){
+        persons.forEach(dynamicObject -> {
+            DynamicObject evalQuest = entryEntity.addNew();
+            long depEmpId = dynamicObject.getLong(HonorStudentConstant.ID_KEY);
+            evalQuest.set(HonorStudentConstant.NCKD_PERSON, depEmpId);
+            evalQuest.set(HonorStudentConstant.NCKD_PERFPLANROLE, perfPlanRole);
+            //获取任职经历
+            Map<String, Object> empOrgRel = HRPIDepempServiceHelper.getDataEmpOrgrel(depEmpId);
+            Long companyId = ConvertUtil.toLong(empOrgRel.get("company_id"));
+            Long jobId = ConvertUtil.toLong(empOrgRel.get("job_id"));
+            evalQuest.set(HonorStudentConstant.NCKD_COMPANY, companyId);
+            evalQuest.set(HonorStudentConstant.NCKD_JOB, jobId);
+        });
+    }
+
+    /**
+     * 查询人员基本条件
+     * @param excludePersons
+     * @return: kd.bos.orm.query.QFilter
+     * @author W.Y.C
+     * @date: 2025/07/10 11:34
+     */
+    private static QFilter getBaseFilter(Set<Long> excludePersons){
+        QFilter baseFilter = new QFilter(HonorStudentConstant.ID_KEY, QCP.not_in, excludePersons);
+        //入职超半年
+        baseFilter.and(new QFilter("startdate",QCP.less_equals, DateUtil.minusMonths(DateUtil.now(),6)));
+        //职位非协理员
+        baseFilter.and(new QFilter("position.name", QCP.not_like, "%协理员%"));
+        return baseFilter;
+    }
 }

+ 71 - 2
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/common/HonorStudentConstant.java

@@ -16,7 +16,7 @@ package nckd.jxccl.hr.hstu.common;
  * @date 2025-05-26 16:28:10
  */
 
-import nckd.jxccl.base.common.constant.FromConstant;
+import nckd.jxccl.base.common.constant.FormConstant;
 
 /**
 * 优秀生常量类
@@ -24,7 +24,7 @@ import nckd.jxccl.base.common.constant.FromConstant;
 * @date 2025/6/18 15:28
 * @version 1.0
 */
-public class HonorStudentConstant extends FromConstant {
+public class HonorStudentConstant extends FormConstant {
 
     /** 评价规则-实体标识 */
     public static final String EVALUATIONRULE_ENTITYID = "nckd_evaluationrule";
@@ -100,6 +100,75 @@ public class HonorStudentConstant extends FromConstant {
     /** 备注 */
     public static final String NCKD_DESCRIPTION = "NCKD_DESCRIPTION";
 
+    /** 综合测评问卷-实体标识 */
+    public static final String NCKD_EVALQUEST = "nckd_evalquest";
+    /** 评测活动 */
+    public static final String NCKD_EVALUATIONRULE = "NCKD_EVALUATIONRULE";
+    /** 综合问卷模板 */
+    public static final String NCKD_EVALQUESTTPL = "NCKD_EVALQUESTTPL";
+    /** 单据体 */
+    public static final String NCKD_EVALQUESTENTRY = "NCKD_EVALQUESTENTRY";
+    /** 员工编码 */
+    public static final String NCKD_PERSON = "NCKD_PERSON";
+    /** 所属公司 */
+    public static final String NCKD_TEXTFIELD = "NCKD_TEXTFIELD";
+    /** 职位 */
+    public static final String NCKD_TEXTFIELD1 = "NCKD_TEXTFIELD1";
+    /** 评分角色 */
+    public static final String NCKD_PERFPLANROLE = "NCKD_PERFPLANROLE";
+    /** 所属公司 */
+    public static final String NCKD_COMPANY = "NCKD_COMPANY";
+    /** 职位 */
+    public static final String NCKD_JOB = "NCKD_JOB";
+    /** 评价对象 */
+    public static final String NCKD_EVALPERSON = "NCKD_EVALPERSON";
+    /** 未评分人数 */
+    public static final String NCKD_NODO = "NCKD_NODO";
+    /** 同级评分人数 */
+    public static final String NCKD_PEEK = "NCKD_PEEK";
+    /** 上级评分人数 */
+    public static final String NCKD_SUPERIOR = "NCKD_SUPERIOR";
+    /** 领导评分人数 */
+    public static final String NCKD_LEAD = "NCKD_LEAD";
+    /** 同级评分总分 */
+    public static final String NCKD_PEEKSCORESSUM = "NCKD_PEEKSCORESSUM";
+    /** 上级评分总分 */
+    public static final String NCKD_SUPERIORSCORESSUM = "NCKD_SUPERIORSCORESSUM";
+    /** 领导评分总分 */
+    public static final String NCKD_LEADSCORESSUM = "NCKD_LEADSCORESSUM";
+    /** 已评分总分 */
+    public static final String NCKD_SUMENTRYSCORE = "NCKD_SUMENTRYSCORE";
+    /** 已评分人数 */
+    public static final String NCKD_HAVEDO = "NCKD_HAVEDO";
+    /** 业绩贡献分 */
+    public static final String NCKD_ALLYEARSCORESUM = "NCKD_ALLYEARSCORESUM";
+    /** 员工绩效得分 */
+    public static final String NCKD_TOPRANKSCORE = "NCKD_TOPRANKSCORE";
+    /** 综合测评得分 */
+    public static final String NCKD_PERFSCORESPART = "NCKD_PERFSCORESPART";
+    /** 优秀生评价积分 */
+    public static final String NCKD_SUMSCORE = "NCKD_SUMSCORE";
+    /** 定格等级 */
+    public static final String NCKD_APPRAISALRESULT = "NCKD_APPRAISALRESULT";
+    /** 公司排名 */
+    public static final String NCKD_COMRANKING = "NCKD_COMRANKING";
+    /** 所在单位排名 */
+    public static final String NCKD_HRRANKING = "NCKD_HRRANKING";
+    /** 公司排名人数 */
+    public static final String NCKD_COMPERSONS = "NCKD_COMPERSONS";
+    /** 所在单位排名人数 */
+    public static final String NCKD_HRPERSONS = "NCKD_HRPERSONS";
+    /** 公司排名/公司排名总人数 */
+    public static final String NCKD_RANKINGANDCOM = "NCKD_RANKINGANDCOM";
+    /** 所在单位排名/所在单位排名人数 */
+    public static final String NCKD_RANKINGANDHR = "NCKD_RANKINGANDHR";
+    /** 公司优秀生排名 */
+    public static final String NCKD_COMPERCENTAGE = "NCKD_COMPERCENTAGE";
+    /** 所在单位优秀生排名 */
+    public static final String NCKD_HRPERCENTAGE = "NCKD_HRPERCENTAGE";
+    /** 评分不了解人数 */
+    public static final String NCKD_UNCLEARS = "NCKD_UNCLEARS";
+
 
 
 

+ 52 - 8
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/CreateEvalQuestFormPlugin.java

@@ -1,5 +1,6 @@
 package nckd.jxccl.hr.hstu.plugin.form;
 
+import kd.bos.context.RequestContext;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.form.CloseCallBack;
@@ -10,20 +11,28 @@ import kd.bos.form.control.Control;
 import kd.bos.form.events.ClosedCallBackEvent;
 import kd.bos.form.events.MessageBoxClosedEvent;
 import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
 import kd.bos.schedule.api.JobInfo;
 import kd.bos.schedule.api.JobType;
 import kd.bos.schedule.form.JobForm;
 import kd.bos.schedule.form.JobFormInfo;
+import kd.bos.servicehelper.QueryServiceHelper;
 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.StrFormatter;
 import nckd.jxccl.hr.hstu.business.CreateEvalQuestService;
 import nckd.jxccl.hr.hstu.common.HonorStudentConstant;
 import nckd.jxccl.hr.hstu.task.CreateEvalQuestTask;
 import nckd.jxccl.hr.hstu.task.CreateEvalQuestTaskClick;
 
+import java.util.ArrayList;
 import java.util.EventObject;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.StringJoiner;
 
 /**
 * 生成评价对象表单插件
@@ -70,17 +79,46 @@ public class CreateEvalQuestFormPlugin extends AbstractFormPlugin implements Plu
     @Override
     public void confirmCallBack(MessageBoxClosedEvent messageBoxClosedEvent) {
         String callBackId = messageBoxClosedEvent.getCallBackId();
-        if(HonorStudentConstant.SUBMIT_KEY.equals(callBackId) && messageBoxClosedEvent.getResult() == MessageBoxResult.Yes){
+        if (HonorStudentConstant.SUBMIT_KEY.equals(callBackId) && messageBoxClosedEvent.getResult() == MessageBoxResult.Yes) {
+            //批量新增综合测评问卷对话框确认事件
             Long evalQuestTplId = ConvertUtil.toLong(this.getView().getFormShowParameter().getCustomParams().get(HonorStudentConstant.NCKD_EVALQUESTTPLID));
-            DynamicObject selectedEvalRule = (DynamicObject)this.getModel().getValue(HonorStudentConstant.NCKD_SELECTEDEVALRULE);
-            DynamicObjectCollection selectedPerson = (DynamicObjectCollection)this.getModel().getValue(HonorStudentConstant.NCKD_SELECTEDPERSON);
+            DynamicObject selectedEvalRule = (DynamicObject) this.getModel().getValue(HonorStudentConstant.NCKD_SELECTEDEVALRULE);
+            DynamicObjectCollection selectedPerson = (DynamicObjectCollection) this.getModel().getValue(HonorStudentConstant.NCKD_SELECTEDPERSON);
+            //------------ 校验是否已生成过问卷(校验规则:评价对象是否已经有对应的评价规则或模板问卷) begin ------------
+            List<Long> idList = new ArrayList<>();
+            selectedPerson.forEach(map ->{
+                DynamicObject depEmpRow = (DynamicObject)map.getDynamicObject(FormConstant.BASEDATAID_KEY);
+                idList.add(depEmpRow.getLong(HonorStudentConstant.ID_KEY));
+            });
+            QFilter filter = new QFilter(HonorStudentConstant.NCKD_EVALPERSON, QCP.in, idList);
+            filter.and(
+                    new QFilter(HonorStudentConstant.NCKD_EVALUATIONRULE,QCP.equals,selectedEvalRule.getLong(HonorStudentConstant.ID_KEY))
+                            .or(new QFilter(HonorStudentConstant.NCKD_EVALQUESTTPL,QCP.equals,evalQuestTplId))
+            );
+            DynamicObjectCollection evalQuestColl = QueryServiceHelper.query(HonorStudentConstant.NCKD_EVALQUEST,HonorStudentConstant.NCKD_EVALPERSON+".person.name",new QFilter[]{filter});
+            if(!evalQuestColl.isEmpty()){
+                StringJoiner exitsPersonName = new StringJoiner(",");
+                evalQuestColl.forEach(dynamicObject -> exitsPersonName.add(dynamicObject.getString(HonorStudentConstant.NCKD_EVALPERSON+".person.name")));
+                this.getView().showErrorNotification(StrFormatter.format("下列人员已存在问卷:{};不能重复生成,请检查!",exitsPersonName.toString()));
+                return;
+            }
+            //------------ 校验是否已生成过问卷(校验规则:评价对象是否已经有对应的评价规则或模板问卷) end ------------
 
-            if(selectedPerson.size() > 30) {
-                //选择生成人员超过30人开启异步任务
+            if (selectedPerson.size() > 10) {
+                //选择生成人员超过10人开启异步任务
                 // 1. 创建任务实例
                 JobInfo jobInfo = new JobInfo();
                 jobInfo.setTaskClassname(CreateEvalQuestTask.class.getName());
-                jobInfo.setParams(new HashMap<>()); // 可传递参数
+                Map<String, Object> params = new HashMap<>();
+                params.put(HonorStudentConstant.NCKD_EVALQUESTTPLID, evalQuestTplId);
+                params.put(HonorStudentConstant.NCKD_SELECTEDEVALRULE, selectedEvalRule);
+                params.put(HonorStudentConstant.NCKD_SELECTEDPERSON, selectedPerson);
+                //调度任务中获取不到当前用户信息
+                long currUserId = RequestContext.get().getCurrUserId();
+                long orgId = RequestContext.get().getOrgId();
+                params.put(HonorStudentConstant.CURR_USER_ID, currUserId);
+                params.put(HonorStudentConstant.CURR_ORG_ID, orgId);
+                jobInfo.setParams(params);
                 jobInfo.setJobType(JobType.REALTIME);
                 jobInfo.setSuccessNotify(Boolean.TRUE);
 
@@ -98,8 +136,14 @@ public class CreateEvalQuestFormPlugin extends AbstractFormPlugin implements Plu
                 JobForm.dispatch(formInfo, this.getView().getParentView());
 
                 this.getView().close();
-            }else{
-                CreateEvalQuestService.createEvalQuest(evalQuestTplId, selectedEvalRule, selectedPerson);
+            } else {
+                try {
+                    CreateEvalQuestService.batchGenerateEvalQuest(evalQuestTplId, selectedEvalRule, selectedPerson, null, null, null);
+                    this.getView().getParentView().showSuccessNotification("生成综合评测问卷成功,可在【查看评价列表】中查看");
+                    this.getView().close();
+                } catch (Exception e) {
+                    this.getView().showErrorNotification("生成评价问卷时出错,原因: " + e.getMessage());
+                }
             }
 
         }

+ 33 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/EvalQuestFormPlugin.java

@@ -0,0 +1,33 @@
+package nckd.jxccl.hr.hstu.plugin.form;
+
+import kd.bos.bill.BillShowParameter;
+import kd.bos.bill.OperationStatus;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.utils.DateUtil;
+
+import java.util.Date;
+import java.util.EventObject;
+
+/**
+ * 综合测评问卷-表单插件
+ * @author W.Y.C
+ * @date 2025/7/13 16:24
+ * @version 1.0
+ */
+public class EvalQuestFormPlugin extends AbstractFormPlugin implements Plugin {
+
+    @Override
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+        BillShowParameter bsp = (BillShowParameter) this.getView().getFormShowParameter();
+        if (bsp.getStatus() == OperationStatus.EDIT) {
+            //查看状态下不会触发业务规则,需要手动处理业务规则的逻辑
+            DynamicObject evaluationRule = (DynamicObject)this.getModel().getValue("nckd_evaluationrule");
+            Date date = evaluationRule.getDate("NCKD_BIZYEAR");
+            this.getModel().setValue("nckd_bizyear", DateUtil.getYear(date));
+        }
+    }
+}

+ 3 - 4
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/EvalQuestTplListPlugin.java

@@ -83,9 +83,8 @@ public class EvalQuestTplListPlugin extends AbstractListPlugin implements Plugin
     @Override
     public void closedCallBack(ClosedCallBackEvent closedCallBackEvent) {
         String actionId = closedCallBackEvent.getActionId();
-        if(HonorStudentConstant.NCKD_CREATEEVALQUEST_ENTITYID.equals(actionId)){
-
-        }else if(HonorStudentConstant.TASK_CREATEEVAL.equals(actionId)){
+        if(HonorStudentConstant.TASK_CREATEEVAL.equals(actionId)){
+            //生成评价对象任务完成回调
             Map<String, Object> result = (Map<String, Object>)closedCallBackEvent.getReturnData();
             if(result != null) {
                 String taskInfoStr = (String) result.get("taskinfo");
@@ -96,7 +95,7 @@ public class EvalQuestTplListPlugin extends AbstractListPlugin implements Plugin
                         String status = taskInfo.getStatus();
                         if (TaskStatusConstant.COMPLETED.equals(status)) {
                             this.getView().showConfirm(
-                                    "操作成功",
+                                    "成功",
                                     "生成综合评测问卷完成,可在【查看评价列表】中查看",
                                     MessageBoxOptions.OK,
                                     ConfirmTypes.Save,

+ 55 - 18
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/task/CreateEvalQuestTask.java

@@ -1,10 +1,29 @@
 package nckd.jxccl.hr.hstu.task;
 
 import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.exception.KDBizException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
 import kd.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import nckd.jxccl.base.common.schedule.ProgressCallback;
+import nckd.jxccl.base.common.utils.ConvertUtil;
+import nckd.jxccl.base.org.helper.PersonHelper;
+import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
+import nckd.jxccl.hr.hstu.business.CreateEvalQuestService;
+import nckd.jxccl.hr.hstu.common.HonorStudentConstant;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.StringJoiner;
 
 /**
  * 生成评价对象异步任务
@@ -12,33 +31,51 @@ import java.util.Map;
  * @date 2025/7/1 15:26
  * @version 1.0
  */
-public class CreateEvalQuestTask extends AbstractTask {
+public class CreateEvalQuestTask extends AbstractTask implements ProgressCallback {
+
+    private static final Log logger = LogFactory.getLog(CreateEvalQuestTask.class);
+
+    private int totalItems;
 
     @Override
     public void execute(RequestContext ctx, Map<String, Object> params) {
         try {
-            int totalSteps = 100;
-            for (int i = 0; i <= totalSteps; i++) {
-                // 检查是否被终止(用户手动终止)
-                if (isStop()) {
-                    stop();
-                    return;
-                }
-
-                // 更新进度(百分比+描述)
-                int progress = (i * 100) / totalSteps;
-                String desc = "处理中:" + i + "/" + totalSteps;
-                feedbackProgress(progress, desc, null);
-
-                // 模拟业务处理(实际替换为业务逻辑)
-                Thread.sleep(100);
-            }
+            Long evalQuestTplId = ConvertUtil.toLong(params.get(HonorStudentConstant.NCKD_EVALQUESTTPLID));
+            Map<String, Object> selectedEvalRuleMap = (Map<String, Object>) params.get(HonorStudentConstant.NCKD_SELECTEDEVALRULE);
+            List<Map<String,Object>> selectedPersonList = (List<Map<String, Object>>) params.get(HonorStudentConstant.NCKD_SELECTEDPERSON);
+            Long crrUserId = ConvertUtil.toLong(params.get(HonorStudentConstant.CURR_USER_ID));
+            Long currOrgId = ConvertUtil.toLong(params.get(HonorStudentConstant.CURR_ORG_ID));
+            totalItems = selectedPersonList.size();
+
+            StringJoiner ruleColum = new StringJoiner(",");
+            ruleColum.add(HonorStudentConstant.ID_KEY);
+            //其他同级评分人比例
+            ruleColum.add(HonorStudentConstant.NCKD_PROPORTION);
+            //同级评分人上限人数
+            ruleColum.add(HonorStudentConstant.NCKD_UPPER);
+            //同级评分人下限人数
+            ruleColum.add(HonorStudentConstant.NCKD_LOWER);
+            DynamicObject selectedEvalRule = QueryServiceHelper.queryOne(HonorStudentConstant.EVALUATIONRULE_ENTITYID, ruleColum.toString(), new QFilter[]{QFilterCommonHelper.getIdEqFilter(selectedEvalRuleMap.get(HonorStudentConstant.ID_KEY))});
+            List<Long> idList = new ArrayList<>();
+            selectedPersonList.forEach(map ->{
+                Map<String, Object> fbasedataid = (Map<String, Object>) map.get(HonorStudentConstant.BASEDATAID_KEY);
+                idList.add(ConvertUtil.toLong(fbasedataid.get(HonorStudentConstant.ID_KEY)));
+            } );
+            DynamicObjectCollection selectedPerson = QueryServiceHelper.query(PersonHelper.DEP_EMP_ENTITY_ID, "id,person.name",new QFilter[]{QFilterCommonHelper.getIdInFilter(idList)});
+            CreateEvalQuestService.batchGenerateEvalQuest(evalQuestTplId, selectedEvalRule, selectedPerson, this, crrUserId, currOrgId);
+
             feedbackProgress(100, "任务完成", null);
         } catch (Exception e) {
-            e.printStackTrace();
+            logger.error("【优秀生考评】-生成综合评测问卷-异常,异常信息:{}",e.getMessage());
             throw new KDBizException("任务执行失败:" + e.getMessage());
         }
     }
 
 
+    @Override
+    public void onProgress(int processedCount) {
+        int progress = (processedCount * 100) / totalItems;
+        String desc = "生成中:" + processedCount + "/" + totalItems;
+        feedbackProgress(progress, desc, null);
+    }
 }

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

@@ -1,6 +1,6 @@
 package nckd.jxccl.opmc.pm.common;
 
-import nckd.jxccl.base.common.constant.FromConstant;
+import nckd.jxccl.base.common.constant.FormConstant;
 
 /**
  * 人员考评页面常量
@@ -8,7 +8,7 @@ import nckd.jxccl.base.common.constant.FromConstant;
  * @date 2025/6/10 17:14
  * @version 1.0
  */
-public class PerfManagerFormConstant extends FromConstant {
+public class PerfManagerFormConstant extends FormConstant {
 
     /** 人员考评实体名称 */
     public static final String ENTITY_NAME_KEY = "nckd_perfmanager";

+ 4 - 4
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/PerfManagerBillPlugin.java

@@ -16,7 +16,7 @@ import kd.bos.orm.query.QFilter;
 import kd.bos.servicehelper.QueryServiceHelper;
 import kd.bos.util.CollectionUtils;
 import kd.sdk.plugin.Plugin;
-import nckd.jxccl.base.common.constant.FromConstant;
+import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.common.utils.DateUtil;
 import nckd.jxccl.base.common.utils.StrFormatter;
@@ -65,9 +65,9 @@ public class PerfManagerBillPlugin extends AbstractBillPlugIn implements Plugin
     public void beforeDoOperation(BeforeDoOperationEventArgs e) {
         String operateKey = ((FormOperate) e.getSource()).getOperateKey();
         DynamicObject dataEntity = this.getModel().getDataEntity();
-        DynamicObjectCollection entrys = dataEntity.getDynamicObjectCollection(FromConstant.ENTRY_ENTITY_KEY);
+        DynamicObjectCollection entrys = dataEntity.getDynamicObjectCollection(FormConstant.ENTRY_ENTITY_KEY);
         List<Date> dateList = entrys.stream().map(entry -> entry.getDate(nckd.jxccl.opmc.pm.common.PerfManagerFormConstant.APPRAISAL_YEAR_KEY)).collect(Collectors.toList());
-        if(FromConstant.SAVE_KEY.equals(operateKey)){
+        if(FormConstant.SAVE_KEY.equals(operateKey)){
             if(CollectionUtils.isNotEmpty(dateList) && !dateList.stream().allMatch(Objects::isNull)) {
                 Date beginYear = dataEntity.getDate(nckd.jxccl.opmc.pm.common.PerfManagerFormConstant.BEGIN_YEAR_KEY);
                 Date endYear = dataEntity.getDate(nckd.jxccl.opmc.pm.common.PerfManagerFormConstant.END_YEAR_KEY);
@@ -125,7 +125,7 @@ public class PerfManagerBillPlugin extends AbstractBillPlugIn implements Plugin
      * @version 1.0
      */
     private void setEntryYear() {
-        EntryGrid entryGrid = this.getControl(FromConstant.ENTRY_ENTITY_KEY);
+        EntryGrid entryGrid = this.getControl(FormConstant.ENTRY_ENTITY_KEY);
         List<FieldEdit> fieldEdits = entryGrid.getFieldEdits();
         for (FieldEdit fieldEdit : fieldEdits) {
             //选择周期结束年份后设置分录考核年份只能在周期开始和结束范围内

+ 2 - 2
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/PerfManagerListPlugin.java

@@ -6,7 +6,7 @@ import kd.bos.list.events.ListRowClickEvent;
 import kd.bos.list.plugin.AbstractListPlugin;
 import kd.bos.mvc.form.FormView;
 import kd.sdk.plugin.Plugin;
-import nckd.jxccl.base.common.constant.FromConstant;
+import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
 import org.apache.commons.lang3.StringUtils;
 
@@ -28,7 +28,7 @@ public class PerfManagerListPlugin extends AbstractListPlugin implements Plugin
     @Override
     public void afterBindData(EventObject e) {
         super.afterBindData(e);
-        this.getView().setVisible(false, FromConstant.SEARCH_DATE_KEY);
+        this.getView().setVisible(false, FormConstant.SEARCH_DATE_KEY);
         IFormView parentView = this.getView().getParentView();
         if(parentView != null &&  ((FormView) parentView).getFormId().equals(PerfManagerFormConstant.PERF_MANAGER_WIZARD_KEY)) {
             //是否向导方式进入

+ 2 - 2
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/SalAdjtTackerListPlugin.java

@@ -2,7 +2,7 @@ package nckd.jxccl.opmc.pm.plugin.form;
 
 import kd.bos.list.plugin.AbstractListPlugin;
 import kd.sdk.plugin.Plugin;
-import nckd.jxccl.base.common.constant.FromConstant;
+import nckd.jxccl.base.common.constant.FormConstant;
 
 import java.util.EventObject;
 
@@ -17,7 +17,7 @@ public class SalAdjtTackerListPlugin extends AbstractListPlugin implements Plugi
     @Override
     public void afterBindData(EventObject e) {
         super.afterBindData(e);
-        this.getView().setVisible(false, FromConstant.SEARCH_DATE_KEY);
+        this.getView().setVisible(false, FormConstant.SEARCH_DATE_KEY);
     }