Browse Source

refactor(jxccl-hr): 重构优秀生综合测评问卷生成逻辑

- 新增 EntityHelper 类用于创建基础实体对象
- 添加 PerfPlanRoleEnum 枚举类用于定义评分人员角色
-引入 ValidationException 异常类用于校验逻辑
- 优化 CreateEvalQuestService 类中的问卷生成逻辑
- 更新 PersonHelper 和 AdminOrgHelper 中的组织和人员查询方法
- 调整 HonorStudentConstant 中的常量定义
wyc 3 days ago
parent
commit
d037c3c7cd
16 changed files with 984 additions and 117 deletions
  1. 7 1
      .gitignore
  2. 5 1
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/FormConstant.java
  3. 35 0
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/enums/PerfPlanRoleEnum.java
  4. 26 0
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/exception/ValidationException.java
  5. 69 0
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/entity/helper/EntityHelper.java
  6. 1 1
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/org/helper/AdminOrgHelper.java
  7. 1 1
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/org/helper/PersonHelper.java
  8. 160 65
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/business/CreateEvalQuestService.java
  9. 41 2
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/common/HonorStudentConstant.java
  10. 16 8
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/CreateEvalQuestFormPlugin.java
  11. 139 5
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/EvalQuestFormPlugin.java
  12. 326 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/EvalQuestListPlugin.java
  13. 50 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/EvalQuestSelectOrg.java
  14. 25 20
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/EvalQuestTplListPlugin.java
  15. 74 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/EvalQuestTreeListPlugin.java
  16. 9 13
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/task/CreateEvalQuestTask.java

+ 7 - 1
.gitignore

@@ -5,7 +5,12 @@ build/
 .idea/
 .gradle/
 .git/
+nckd-cosmic-debug/
 /nckd-cosmic-debug/
+**/nckd-cosmic-debug/
+
+out/
+**/out/
 
 
 # 忽略文件
@@ -28,4 +33,5 @@ gradle.properties
 #    git update-index --no-assume-unchanged code/nckd-cosmic-debug/src/main/java/nckd/cosmic/debug/DebugApplication.java
 #    git pull
 #    git update-index --assume-unchanged code/nckd-cosmic-debug/src/main/java/nckd/cosmic/debug/DebugApplication.java
-## -------------------- 让Git停止跟踪文件 end--------------------
+## -------------------- 让Git停止跟踪文件 end--------------------
+

+ 5 - 1
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/FormConstant.java

@@ -110,5 +110,9 @@ public class FormConstant {
     /** 当前org id*/
     public static final String CURR_ORG_ID = "ORG_ID";
     /** 当前org id*/
-    public static final String DEP_EMP = "DEPEMP";
+    public static final String DEP_EMP = "depemp";
+    /** 左树右表-是否包含子部门*/
+    public static final String CHKINCLUDECHILD = "chkincludechild";
+    /**人员*/
+    public static final String PERSON = "PERSON";
 }

+ 35 - 0
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/enums/PerfPlanRoleEnum.java

@@ -0,0 +1,35 @@
+package nckd.jxccl.base.common.enums;
+
+/**
+* 【优秀生】-评分人员角色枚举
+* @author W.Y.C
+* @date 2025/7/14 14:25
+* @version 1.0
+*/
+public enum PerfPlanRoleEnum {
+
+    /** 领导 */
+    LEAD("1", "领导"),
+    /** 上级 */
+    SUPERIOR("2", "上级"),
+    /** 同级 */
+    PEERS("3", "同级"),
+    /** 同级其他部门人员 */
+    OTHER_PEERS("3", "同级其他部门人员");
+
+    private final String code;
+    private final String name;
+
+    PerfPlanRoleEnum(String code, String name) {
+        this.code = code;
+        this.name = name;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getName() {
+        return name;
+    }
+}

+ 26 - 0
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/exception/ValidationException.java

@@ -0,0 +1,26 @@
+package nckd.jxccl.base.common.exception;
+
+import kd.bos.exception.ErrorCode;
+import kd.bos.exception.KDBizException;
+import kd.bos.exception.KDException;
+
+/**
+ * 校验异常类
+ * @author W.Y.C
+ * @date 2025/7/16 16:24
+ * @version 1.0
+ */
+public class ValidationException extends KDBizException {
+
+    public ValidationException(String message) {
+        super(new ErrorCode("", message), new Object[0]);
+    }
+
+    public ValidationException(ErrorCode errorCode, Object... args) {
+        super(errorCode, args);
+    }
+
+    public ValidationException(Throwable cause, ErrorCode errorCode, Object... args) {
+        super(cause, errorCode, args);
+    }
+}

+ 69 - 0
code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/entity/helper/EntityHelper.java

@@ -0,0 +1,69 @@
+package nckd.jxccl.base.entity.helper;
+
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.entity.MainEntityType;
+import kd.bos.entity.constant.StatusEnum;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.org.helper.AdminOrgHelper;
+
+import java.util.Date;
+
+/**
+ * 实体(DynamicObject) 帮助类
+ * @author W.Y.C
+ * @date 2025/7/17 19:58
+ * @version 1.0
+ */
+public class EntityHelper {
+
+    /**
+     * 创建一个状态为"可用"的基础资料对象
+     * @param entityName 实体标识
+     * @return: kd.bos.dataentity.entity.DynamicObject 新创建的基础资料实体,状态已设置为可用(数据状态:已审核、使用状态:可用),并设置创建时间、创建人、创建组织、业务单元
+     * @author W.Y.C
+     * @date: 2025/07/17 20:06
+     */
+    public static DynamicObject newAvailableBasicEntity(String entityName){
+        return newAvailableBasicEntity(entityName, null, null);
+    }
+
+    /**
+     * 创建一个状态为"可用"的基础资料对象
+     * @param entityName 实体标识
+     * @param org 组织id
+     * @param currUser 当前用户
+     * @return: kd.bos.dataentity.entity.DynamicObject 新创建的基础资料实体,状态已设置为可用(数据状态:已审核、使用状态:可用),并设置创建时间、创建人、创建组织、业务单元
+     * @author W.Y.C
+     * @date: 2025/07/17 20:13
+     */
+    public static DynamicObject newAvailableBasicEntity(String entityName,DynamicObject org, DynamicObject currUser){
+        if(currUser == null){
+            DynamicObject buildEvalResult = BusinessDataServiceHelper.newDynamicObject(entityName);
+            Long currUserId =  RequestContext.get().getCurrUserId();
+            //当前登录用户
+            currUser = UserServiceHelper.getUserInfoByID(currUserId, FormConstant.ID_KEY);
+        }
+        if(org == null){
+            Long currOrgId =  RequestContext.get().getOrgId();
+            // 获取苍穹平台组织实体元数据
+            MainEntityType bosOrgEntityType = EntityMetadataCache.getDataEntityType(AdminOrgHelper.BOS_ORG);
+            // 基于元数据创建 DynamicObject
+            org = new DynamicObject(bosOrgEntityType);
+            org.set(FormConstant.ID_KEY,currOrgId);
+        }
+
+        DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject(entityName);
+        dynamicObject.set(FormConstant.CREATEORG_KEY, org);
+        dynamicObject.set(FormConstant.USEORG_KEY, org);
+        dynamicObject.set(FormConstant.CREATE_TIME_KEY, new Date());
+        dynamicObject.set(FormConstant.CREATOR_KEY, currUser);
+        dynamicObject.set(FormConstant.STATUS, StatusEnum.C.toString());
+        dynamicObject.set(FormConstant.ENABLE, EnableEnum.YES.getCode());
+        return dynamicObject;
+    }
+}

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

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

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

@@ -52,7 +52,7 @@ public class PersonHelper {
 
         // 防止 selectFields 为空
         if (StringUtils.isBlank(selectFields)) {
-            selectFields = "id";
+            selectFields = FormConstant.ID_KEY;
         }
         // 执行查询并返回结果
         return QueryServiceHelper.query(DEP_EMP_ENTITY_ID, selectFields, filterList.toArray(new QFilter[0]));

+ 160 - 65
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/business/CreateEvalQuestService.java

@@ -22,25 +22,29 @@ 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.FormConstant;
+import nckd.jxccl.base.common.enums.PerfPlanRoleEnum;
 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.entity.helper.EntityHelper;
 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.ArrayList;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.StringJoiner;
+import java.util.stream.Collectors;
+
 /**
- * 【优秀生】-生成评价问卷服务类
+ * 【优秀生】-生成评价问卷服务类(生成优秀生评价预计打分人员核心逻辑)
  * @author W.Y.C
  * @date 2025/7/2 16:41
  * @version 1.0
@@ -49,17 +53,20 @@ public class CreateEvalQuestService {
 
     private static final Log logger = LogFactory.getLog(CreateEvalQuestService.class);
 
+    /** 最大递归层级 */
+    private static final int MAX_RECURSION_DEPTH = 10;
+
     /**
      * 批量生成评价问卷
-     * @param evalQuestTplId 评价问卷模板Id
+     * @param selectedEvalRule 评价问卷模板
      * @param selectedEvalRule 评价规则
      * @param selectedPerson 被评价人
      * @return: void
      * @author W.Y.C
      * @date: 2025/07/02 17:24
      */
-    public static void batchGenerateEvalQuest(Long evalQuestTplId, DynamicObject selectedEvalRule, DynamicObjectCollection selectedPerson, ProgressCallback callback, Long userId,Long orgId) {
-        logger.info("【优秀生考评】-生成综合评测问卷-开始");
+    public static void batchGenerateEvalQuest(DynamicObject evalQuestTpl, DynamicObject selectedEvalRule, DynamicObjectCollection selectedPerson, ProgressCallback callback, Long userId,Long orgId) {
+        logger.info("【优秀生考评】-生成综合评测问卷开始,参数: evalQuestTplId={},selectedEvalRule={} ,selectedPersonSize={}", evalQuestTpl.getLong(FormConstant.ID_KEY), selectedEvalRule.getLong(FormConstant.ID_KEY),selectedPerson.size());
         try {
             // 获取当前用户ID和主组织ID(创建组织)
             Long currUserId = userId;
@@ -70,7 +77,7 @@ public class CreateEvalQuestService {
             if(currOrgId == null) {
                 currOrgId = RequestContext.get().getOrgId();
             }
-
+            //当前登录用户
             DynamicObject currUser = UserServiceHelper.getUserInfoByID(currUserId, HonorStudentConstant.ID_KEY);
 
             // 获取实体元数据
@@ -88,10 +95,9 @@ public class CreateEvalQuestService {
             //单据页面操作
             //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("评价问卷模板未找到");
-            }
+            //查询模板
+            evalQuestTpl = BusinessDataServiceHelper.loadSingle(evalQuestTpl.getLong(HonorStudentConstant.ID_KEY),HonorStudentConstant.NCKD_EVALQUESTTPL_ENTITYID);
+
 
             StringJoiner ruleColum = new StringJoiner(",");
             ruleColum.add(FormConstant.ID_KEY);
@@ -101,7 +107,8 @@ public class CreateEvalQuestService {
             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))});
+            //查询评价规则
+            selectedEvalRule = BusinessDataServiceHelper.loadSingle(selectedEvalRule.getLong(HonorStudentConstant.ID_KEY),HonorStudentConstant.EVALUATIONRULE_ENTITYID);
             if (selectedEvalRule == null) {
                 throw new KDBizException("评价规则未找到");
             }
@@ -130,7 +137,7 @@ public class CreateEvalQuestService {
                     //获取页面选择的多选基础数据可能会被存放在fbasedataid中
                     DynamicObject depEmpRow = (DynamicObject)row.getDynamicObject(FormConstant.BASEDATAID_KEY);
                     if (depEmpRow == null) {
-                        logger.warn("跳过无效的人员数据,索引:{}", i);
+                        logger.warn("【优秀生考评】-跳过无效的人员数据,索引:{}", i);
                         continue;
                     }
                     personId = ConvertUtil.toLong(depEmpRow.getPkValue());
@@ -141,20 +148,20 @@ public class CreateEvalQuestService {
                 }
 
                 if (personId == null) {
-                    logger.warn("跳过人员ID为空的数据,索引:{}", i);
+                    logger.warn("【优秀生考评】-跳过人员ID为空的数据,索引:{}", i);
                     continue;
                 }
 
-                MainEntityType depempEntityType = EntityMetadataCache.getDataEntityType(PersonHelper.DEP_EMP_ENTITY_ID);
-                DynamicObject person = new DynamicObject(depempEntityType);
+                MainEntityType depEmpEntityType = EntityMetadataCache.getDataEntityType(PersonHelper.DEP_EMP_ENTITY_ID);
+                DynamicObject person = new DynamicObject(depEmpEntityType);
                 person.set(FormConstant.ID_KEY,personId);
 
                 //表头构建
-                DynamicObject mainObj = prepareMainObject(person, org, currUser, selectedEvalRule, evalQuestTplId);
+                DynamicObject mainObj = prepareMainObject(person, org, currUser, selectedEvalRule, evalQuestTpl);
                 //分录构建
                 DynamicObjectCollection entryEntity = fetchExpectedScorers(row, proportion, upper, lower, otherLower, Boolean.TRUE);
                 if (entryEntity == null || entryEntity.isEmpty()) {
-                    logger.warn("未能获取到有效的评分人列表,人员ID:{}", personId);
+                    logger.warn("【优秀生考评】-未能获取到有效的评分人列表,人员ID:{}", personId);
                     continue;
                 }
                 mainObj.set(HonorStudentConstant.NCKD_EVALQUESTENTRY,entryEntity);
@@ -176,8 +183,17 @@ public class CreateEvalQuestService {
         logger.info("【优秀生考评】-生成综合评测问卷-结束");
     }
 
+    /**
+     * 保存问卷
+     * @param mainObj 主单
+     * @param option 操作选项
+     * @param personName 评价对象名称
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/07/15 16:06
+     */
     private static void saveQuest(DynamicObject mainObj, OperateOption option, String personName) {
-        OperationResult operationResult = SaveServiceHelper.saveOperate(FormConstant.SAVE_KEY,HonorStudentConstant.NCKD_EVALQUEST, new DynamicObject[]{mainObj}, option);
+        OperationResult operationResult = SaveServiceHelper.saveOperate(FormConstant.SAVE_KEY,HonorStudentConstant.NCKD_EVALQUEST_ENTITYID, new DynamicObject[]{mainObj}, option);
         if (!operationResult.isSuccess()) {
             StringJoiner errorMsg = new StringJoiner(";");
             for (IOperateInfo error : operationResult.getAllErrorOrValidateInfo()) {
@@ -194,23 +210,17 @@ public class CreateEvalQuestService {
      * @param org
      * @param currUser
      * @param selectedEvalRule
-     * @param evalQuestTplId
+     * @param evalQuestTpl
      * @return: kd.bos.dataentity.entity.DynamicObject
      * @author W.Y.C
      * @date: 2025/07/14 09:44
      */
-    private static DynamicObject prepareMainObject(DynamicObject person, DynamicObject org, DynamicObject currUser, DynamicObject selectedEvalRule, Long evalQuestTplId) {
-        DynamicObject mainObj = BusinessDataServiceHelper.newDynamicObject(HonorStudentConstant.NCKD_EVALQUEST);
+    private static DynamicObject prepareMainObject(DynamicObject person, DynamicObject org, DynamicObject currUser, DynamicObject selectedEvalRule, DynamicObject evalQuestTpl) {
+        DynamicObject mainObj = EntityHelper.newAvailableBasicEntity(HonorStudentConstant.NCKD_EVALQUEST_ENTITYID,org,currUser);
         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());
+        mainObj.set(HonorStudentConstant.NCKD_PERIODSTATE, "0");
+        mainObj.set(HonorStudentConstant.NCKD_EVALUATIONRULE, selectedEvalRule);
+        mainObj.set(HonorStudentConstant.NCKD_EVALQUESTTPL, evalQuestTpl);
         return mainObj;
     }
 
@@ -227,12 +237,29 @@ public class CreateEvalQuestService {
      * @date: 2025/07/10 17:38
      */
     public static DynamicObjectCollection fetchExpectedScorers(DynamicObject person,int proportion,int upper,int lower,int otherLower,boolean isLeadAndSuperior) {
+        return fetchExpectedScorers(person, null,null, proportion, upper, lower, otherLower, isLeadAndSuperior);
+    }
+    /**
+     * 获取预计参与问卷打分的用户列表
+     * @param person 评分人员
+     * @param adminColl 需要圈定的组织范围
+     * @param excludePersonList 需要排除的人员
+     * @param proportion 其他同级评分人比例
+     * @param upper 同级评分人上限人数
+     * @param lower 同级评分人下限人数
+     * @param otherLower 根据比例计算出的其他部门同级评分人的最小人数(同级评分人下限人数*其他同级评分人比例/100=其他同级评分人下限人数)
+     * @param isLeadAndSuperior 是否获取上级和领导
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/07/10 17:38
+     */
+    public static DynamicObjectCollection fetchExpectedScorers(DynamicObject person,DynamicObjectCollection adminColl,List<Long> excludePersonList,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为空");
+                logger.warn("【优秀生考评】-获取人员信息失败,BASEDATAID_KEY为空");
                 return new DynamicObjectCollection();
             }
             personId = ConvertUtil.toLong(depEmpRow.getPkValue());
@@ -241,13 +268,13 @@ public class CreateEvalQuestService {
         }
         
         if (personId == null) {
-            logger.warn("人员ID为空");
+            logger.warn("【优秀生考评】-人员ID为空");
             return new DynamicObjectCollection();
         }
 
         Map<String, Object> depEmp = HRPIDepempServiceHelper.getDepemp(personId);
         if (depEmp == null) {
-            logger.warn("获取人员信息失败,人员ID:{}", personId);
+            logger.warn("【优秀生考评】-获取人员信息失败,人员ID:{}", personId);
             return new DynamicObjectCollection();
         }
         
@@ -264,14 +291,24 @@ public class CreateEvalQuestService {
             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);
+                DynamicObjectCollection leadTempColl = PersonHelper.getDepEmpEffectivePerson("id,person.name", positionFilter);
+                Collections.shuffle(leadTempColl);
+                if(!leadTempColl.isEmpty()){
+                    DynamicObject lead = leadTempColl.get(0);
+                    leadColl.add(lead);
+                }
             }
 
             //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);
+                DynamicObjectCollection superiorTempColl = PersonHelper.getDepEmpEffectivePerson("id,person.name", positionFilter);
+                Collections.shuffle(superiorTempColl);
+                if(!superiorTempColl.isEmpty()){
+                    DynamicObject superior = superiorTempColl.get(0);
+                    superiorColl.add(superior);
+                }
             }
         }
 
@@ -292,12 +329,25 @@ public class CreateEvalQuestService {
         */
         //3:查询当前组织内的同级员工
         Set<Long> excludePersons = new HashSet<>();
+        if(excludePersonList != null && !excludePersonList.isEmpty()){
+            excludePersons.addAll(excludePersonList);
+        }
         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);
+        //如果有选择组织范围则查询组织范围内的人员,否则使用评价对象的组织
+        List<Long> adminOrgIds = new ArrayList<>();
+        if(adminColl != null && !adminColl.isEmpty()){
+            adminColl.forEach(map ->{
+                DynamicObject baseData = map.getDynamicObject(HonorStudentConstant.BASEDATAID_KEY);
+                adminOrgIds.add(baseData.getLong(HonorStudentConstant.ID_KEY));
+            } );
+        }else{
+            adminOrgIds.add(adminOrgId);
+        }
+        DynamicObjectCollection peers = getPeers(adminOrgIds,excludePersons, proportion,upper, lower, otherLower);
         peers.forEach(dynamicObject -> excludePersons.add(dynamicObject.getLong(HonorStudentConstant.ID_KEY)));
 
         //4:同级其他部门在职人员
@@ -308,22 +358,20 @@ public class CreateEvalQuestService {
 
 
         // 主单据创建
-        DynamicObject mainObj = BusinessDataServiceHelper.newDynamicObject(HonorStudentConstant.NCKD_EVALQUEST);
+        DynamicObject mainObj = BusinessDataServiceHelper.newDynamicObject(HonorStudentConstant.NCKD_EVALQUEST_ENTITYID);
         //分录创建
         DynamicObjectCollection entryEntity = mainObj.getDynamicObjectCollection(HonorStudentConstant.NCKD_EVALQUESTENTRY);
         ////1:领导、2:上级、3:同级
         if(isLeadAndSuperior) {
-            builderEvalQuestEntry(leadColl, "1", entryEntity);
-            builderEvalQuestEntry(superiorColl, "2", entryEntity);
+            builderEvalQuestEntry(leadColl, PerfPlanRoleEnum.LEAD, entryEntity);
+            builderEvalQuestEntry(superiorColl, PerfPlanRoleEnum.SUPERIOR, entryEntity);
         }
-        builderEvalQuestEntry(peers, "3", entryEntity);
-        builderEvalQuestEntry(otherPeers, "3", entryEntity);
+        builderEvalQuestEntry(peers, PerfPlanRoleEnum.PEERS, entryEntity);
+        builderEvalQuestEntry(otherPeers, PerfPlanRoleEnum.OTHER_PEERS, entryEntity);
 
 
         return entryEntity;
     }
-    
-    private static final int MAX_RECURSION_DEPTH = 10;
 
     /**
      * 获取同级人员
@@ -331,13 +379,13 @@ public class CreateEvalQuestService {
      *  2.工作性质大类过滤掉编码为H01(居休)、H02(退养)、H03(随任)、H04(女工长假)、H05(长期工伤病休)、H06(长期病休)、H07(其他不在岗)、J(外派)的员工,不做为同级
      *  3.除去领导和上级
      *  4.入职超半年、职位非协理员
-     * @param adminOrgId 当前组织ID
+     * @param adminOrgIds 当前组织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){
+    private static DynamicObjectCollection getPeers(List<Long> adminOrgIds,Set<Long> excludePersons,int proportion,int upper,int lower,int otherLower){
         //最终的结果
         DynamicObjectCollection finalResult = new DynamicObjectCollection();
 
@@ -346,14 +394,18 @@ public class CreateEvalQuestService {
         //------ 基本条件 end------
 
         //1.查询当前组织内的同级员工
-        QFilter adminOrgFilter = new QFilter("adminorg.id", QCP.equals, adminOrgId);
+        QFilter adminOrgFilter = new QFilter("adminorg.id", QCP.in, adminOrgIds);
         DynamicObjectCollection depEmpEffectivePerson = PersonHelper.getDepEmpEffectivePerson("id,person.name", baseFilter, adminOrgFilter);
-
+        depEmpEffectivePerson.forEach(dynamicObject -> excludePersons.add(dynamicObject.getLong("id")));
         //2.当前组织人数小于下限人数,继续向上级组织查找直到 >= lower
-        if(depEmpEffectivePerson.size() < lower){
-            Integer count = recursionGetPeers(adminOrgId, depEmpEffectivePerson, baseFilter, lower, 1);
-            logger.info("【优秀生考评】-向上级组织查找次数:{}",count);
+        for (Long adminOrgId : adminOrgIds) {
+            if(depEmpEffectivePerson.size() < lower){
+                Integer count = recursionGetPeers(adminOrgId, depEmpEffectivePerson, baseFilter, lower, 1);
+                depEmpEffectivePerson.forEach(dynamicObject -> excludePersons.add(dynamicObject.getLong("id")));
+                logger.info("【优秀生考评】-向上级组织查找次数:{}",count);
+            }
         }
+
         //打乱顺序随机抽取前 upper 个
         Collections.shuffle(depEmpEffectivePerson);
         int endIndex = Math.min(depEmpEffectivePerson.size(), upper);
@@ -381,8 +433,12 @@ public class CreateEvalQuestService {
         //------ 基本条件 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);
+        depEmpEffectivePerson.forEach(dynamicObject -> excludePersons.add(dynamicObject.getLong("id")));
+
+        //2.当前组织人数小于下限人数,继续向上级组织查找直到 >= lower
         if(depEmpEffectivePerson.size() < otherLower){
             Integer count = recursionGetParentPeers(parentOrg, depEmpEffectivePerson, baseFilter, otherLower, 1);
             logger.info("【优秀生考评】-向上级组织查找次数:{}",count);
@@ -407,24 +463,25 @@ public class CreateEvalQuestService {
      */
     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);
+            logger.warn("【优秀生考评】-达到最大递归深度{}。结束递归, 组织ID: {}", MAX_RECURSION_DEPTH, adminOrgId);
             return count;
         }
         
         Long parentOrg = AdminOrgHelper.getParentOrg(adminOrgId);
         if(parentOrg == null){
+            logger.warn("【优秀生考评】-达到最大递归深度或组织为空");
             return count;
         }
         // 创建一个查询条件,用于筛选属于上级组织的人员
         QFilter adminOrgFilter = new QFilter("adminorg.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){
+        if(persons.size() >= lower){
             return count;
         }else{
             count++;
             if(count == MAX_RECURSION_DEPTH) {
-                logger.warn("即将达到最大递归深度, 组织ID: {}", adminOrgId);
+                logger.warn("【优秀生考评】-即将达到最大递归深度, 组织ID: {}", adminOrgId);
             }
             return recursionGetPeers(parentOrg,persons,baseFilter,lower,count);
         }
@@ -447,24 +504,25 @@ public class CreateEvalQuestService {
      */
     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);
+            logger.warn("【优秀生考评】-达到最大递归深度{}。结束递归, 组织ID: {}", MAX_RECURSION_DEPTH, adminOrgId);
             return count;
         }
         
         Long parentOrg = AdminOrgHelper.getParentOrg(adminOrgId);
         if(parentOrg == null){
+            logger.warn("【优秀生考评】-达到最大递归深度或组织为空");
             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){
+        if(persons.size() >= lower){
             return count;
         }else{
             count++;
             if(count == MAX_RECURSION_DEPTH) {
-                logger.warn("即将达到最大递归深度, 组织ID: {}", adminOrgId);
+                logger.warn("【优秀生考评】-即将达到最大递归深度, 组织ID: {}", adminOrgId);
             }
             return recursionGetParentPeers(parentOrg,persons,baseFilter,lower,count);
         }
@@ -479,23 +537,59 @@ public class CreateEvalQuestService {
      * @author W.Y.C
      * @date: 2025/07/13 18:52
      */
-    private static void builderEvalQuestEntry(DynamicObjectCollection persons,String perfPlanRole,DynamicObjectCollection entryEntity){
+    private static void builderEvalQuestEntry(DynamicObjectCollection persons,PerfPlanRoleEnum perfPlanRole,DynamicObjectCollection entryEntity){
+        List<Long> personIds = new ArrayList<>(persons.size());
+        persons.forEach(person -> personIds.add(person.getLong(HonorStudentConstant.ID_KEY)));
+        //获取任职经历
+        List<Map<String, Object>> empOrgRelList = HRPIDepempServiceHelper.listEmpOrgrels(personIds);
+        //根据ID分组,获取所属公司和职位
+        Map<Long, Map<String, DynamicObject>> empOrgRelMap = empOrgRelList.stream()
+                .filter(item -> item.get(FormConstant.DEP_EMP) != null)
+                .collect(Collectors.toMap(
+                        item -> ConvertUtil.toLong(item.get(FormConstant.DEP_EMP)),
+                        item -> {
+                            Map<String, DynamicObject> innerMap = new HashMap<>(2);
+                            Long companyId = ConvertUtil.toLong(item.get("company"));
+                            if(companyId != null && companyId > 0){
+                                DynamicObject company = BusinessDataServiceHelper.loadSingle(companyId, "haos_adminorghr");
+                                innerMap.put("company", company);
+                            }
+                            Long jobId = ConvertUtil.toLong(item.get("job"));
+                            if(jobId != null && jobId > 0){
+                                DynamicObject job = BusinessDataServiceHelper.loadSingle(jobId, "hbjm_jobhr");
+                                innerMap.put("job", job);
+                            }
+                            return innerMap;
+                        },
+                        (existing, replacement) -> replacement
+                ));
+        //获取人员对象
+        DynamicObject[] dbPersons = BusinessDataServiceHelper.load(PersonHelper.DEP_EMP_ENTITY_ID,
+                "id,person.id,person.name,person.number,adminorg.number,adminorg.name,position.name,position.number",
+                new QFilter[]{new QFilter(FormConstant.ID_KEY, QCP.in, personIds)});
+        Map<Long, DynamicObject> personMap = new HashMap<>(dbPersons.length);
+        for (DynamicObject obj : dbPersons) {
+            personMap.put(obj.getLong(FormConstant.ID_KEY), obj);
+        }
+
         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);
+            DynamicObject person = personMap.get(depEmpId);
+            evalQuest.set(HonorStudentConstant.NCKD_PERSON, person);
+            evalQuest.set(HonorStudentConstant.NCKD_PERFPLANROLE, perfPlanRole.getCode());
             //获取任职经历
-            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);
+            Map<String, DynamicObject> empOrgRel = empOrgRelMap.get(depEmpId);
+            evalQuest.set(HonorStudentConstant.NCKD_COMPANY, empOrgRel.get("company"));
+            evalQuest.set(HonorStudentConstant.NCKD_JOB, empOrgRel.get("job"));
+            //备注生成人员对应的角色
+            evalQuest.set(HonorStudentConstant.NCKD_REMARK, StrFormatter.format("系统生成:{}",perfPlanRole.getName()));
+
         });
     }
 
     /**
-     * 查询人员基本条件
+     * 查询人员打分人员基本条件
      * @param excludePersons
      * @return: kd.bos.orm.query.QFilter
      * @author W.Y.C
@@ -507,6 +601,7 @@ public class CreateEvalQuestService {
         baseFilter.and(new QFilter("startdate",QCP.less_equals, DateUtil.minusMonths(DateUtil.now(),6)));
         //职位非协理员
         baseFilter.and(new QFilter("position.name", QCP.not_like, "%协理员%"));
+        // TODO 1.最新一条党政职务履历为副处级及以上人员不作为同级取数 2.工作性质大类过滤掉编码为H01(居休)、H02(退养)、H03(随任)、H04(女工长假)、H05(长期工伤病休)、H06(长期病休)、H07(其他不在岗)、J(外派)的员工,不做为同级
         return baseFilter;
     }
 }

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

@@ -80,7 +80,7 @@ public class HonorStudentConstant extends FormConstant {
     public static final String NCKD_SELECTEDEVALRULE = "NCKD_SELECTEDEVALRULE";
     /** 人员 */
     public static final String NCKD_SELECTEDPERSON = "NCKD_SELECTEDPERSON";
-    /** 优秀生综合测评模板 */
+    /** 优秀生综合测评模板ID */
     public static final String NCKD_EVALQUESTTPLID = "nckd_evalquesttplid";
     /** 生成评价对象 */
     public static final String OP_CREATEEVAL = "createeval";
@@ -101,7 +101,7 @@ public class HonorStudentConstant extends FormConstant {
     public static final String NCKD_DESCRIPTION = "NCKD_DESCRIPTION";
 
     /** 综合测评问卷-实体标识 */
-    public static final String NCKD_EVALQUEST = "nckd_evalquest";
+    public static final String NCKD_EVALQUEST_ENTITYID = "nckd_evalquest";
     /** 评测活动 */
     public static final String NCKD_EVALUATIONRULE = "NCKD_EVALUATIONRULE";
     /** 综合问卷模板 */
@@ -116,6 +116,8 @@ public class HonorStudentConstant extends FormConstant {
     public static final String NCKD_TEXTFIELD1 = "NCKD_TEXTFIELD1";
     /** 评分角色 */
     public static final String NCKD_PERFPLANROLE = "NCKD_PERFPLANROLE";
+    /** 备份 */
+    public static final String NCKD_REMARK = "NCKD_REMARK";
     /** 所属公司 */
     public static final String NCKD_COMPANY = "NCKD_COMPANY";
     /** 职位 */
@@ -168,8 +170,45 @@ public class HonorStudentConstant extends FormConstant {
     public static final String NCKD_HRPERCENTAGE = "NCKD_HRPERCENTAGE";
     /** 评分不了解人数 */
     public static final String NCKD_UNCLEARS = "NCKD_UNCLEARS";
+    /** 综合测评问卷-生成问卷 */
+    public static final String OP_GENERATE_QUEST = "GENERATEQUEST";
+    /** 综合测评问卷-刷新同级人员 */
+    public static final String OP_REFRESH_PEERS = "refreshpeers";
+    /** 综合测评问卷-同级评分人组织选择弹窗 */
+    public static final String NCKD_EVALQUEST_SELECTORG = "nckd_evalquest_selectorg";
+    /** 选择组织范围(可选)*/
+    public static final String NCKD_MULSELECTORG = "nckd_mulselectorg";
 
 
 
 
+    /** 综合测评答卷-实体标识 */
+    public static final String NCKD_EVALRESULT_ENTITYID = "nckd_evalresult";
+    /** 单据体 */
+    public static final String NCKD_ENTRYENTITY = "NCKD_ENTRYENTITY";
+    /** 缺省id字段 */
+    public static final String ID = "ID";
+    /** 分录行号 */
+    public static final String SEQ = "SEQ";
+    /** 综合测评答卷单据体 */
+    public static final String NCKD_EVALRESULTENTRY = "nckd_evalresultentry";
+    /** 综合测评问卷模板分录 */
+    public static final String NCKD_EVALQUESTTPLENTRY = "nckd_evalquesttplentry";
+    /** 综合测评问卷模板分录 */
+    public static final String NCKD_EVALQUESTTPLENTRYBAS_ENTITYID = "nckd_evalquesttplentrybas";
+    /** 评价分数 */
+    public static final String NCKD_PERFSCORES = "NCKD_PERFSCORES";
+    /** 综合测评问卷 */
+    public static final String NCKD_EVALQUEST = "NCKD_EVALQUEST";
+    /** 评分人 */
+    public static final String NCKD_SCORER = "NCKD_SCORER";
+    /** 评分状态 */
+    public static final String NCKD_PERFPLANSTATE = "NCKD_PERFPLANSTATE";
+    /** 不了解 */
+    public static final String NCKD_UNCLEAR = "NCKD_UNCLEAR";
+    /** 操作备注 */
+    public static final String NCKD_HOWDOMARK = "NCKD_HOWDOMARK";
+
+
+
 }

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

@@ -11,6 +11,7 @@ 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.mvc.list.ListView;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
 import kd.bos.schedule.api.JobInfo;
@@ -35,7 +36,7 @@ import java.util.Map;
 import java.util.StringJoiner;
 
 /**
-* 生成评价对象表单插件
+* 生成评价对象(弹窗)表单插件
 * @author W.Y.C
 * @date 2025/7/1 10:19
 * @version 1.0
@@ -51,7 +52,7 @@ public class CreateEvalQuestFormPlugin extends AbstractFormPlugin implements Plu
     public void afterCreateNewData(EventObject e) {
         super.afterCreateNewData(e);
         Long evalQuestTplId = ConvertUtil.toLong(this.getView().getFormShowParameter().getCustomParams().get(HonorStudentConstant.NCKD_EVALQUESTTPLID));
-        this.getModel().setValue(HonorStudentConstant.NCKD_EVALQUESTTPLID, evalQuestTplId);
+        this.getModel().setValue(HonorStudentConstant.NCKD_EVALQUESTTPL, evalQuestTplId);
     }
 
     @Override
@@ -81,7 +82,7 @@ public class CreateEvalQuestFormPlugin extends AbstractFormPlugin implements Plu
         String callBackId = messageBoxClosedEvent.getCallBackId();
         if (HonorStudentConstant.SUBMIT_KEY.equals(callBackId) && messageBoxClosedEvent.getResult() == MessageBoxResult.Yes) {
             //批量新增综合测评问卷对话框确认事件
-            Long evalQuestTplId = ConvertUtil.toLong(this.getView().getFormShowParameter().getCustomParams().get(HonorStudentConstant.NCKD_EVALQUESTTPLID));
+            DynamicObject evalQuestTpl = ((DynamicObject)this.getModel().getValue(HonorStudentConstant.NCKD_EVALQUESTTPL));
             DynamicObject selectedEvalRule = (DynamicObject) this.getModel().getValue(HonorStudentConstant.NCKD_SELECTEDEVALRULE);
             DynamicObjectCollection selectedPerson = (DynamicObjectCollection) this.getModel().getValue(HonorStudentConstant.NCKD_SELECTEDPERSON);
             //------------ 校验是否已生成过问卷(校验规则:评价对象是否已经有对应的评价规则或模板问卷) begin ------------
@@ -93,9 +94,9 @@ public class CreateEvalQuestFormPlugin extends AbstractFormPlugin implements Plu
             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))
+                            .or(new QFilter(HonorStudentConstant.NCKD_EVALQUESTTPL,QCP.equals,evalQuestTpl.getLong(FormConstant.ID_KEY)))
             );
-            DynamicObjectCollection evalQuestColl = QueryServiceHelper.query(HonorStudentConstant.NCKD_EVALQUEST,HonorStudentConstant.NCKD_EVALPERSON+".person.name",new QFilter[]{filter});
+            DynamicObjectCollection evalQuestColl = QueryServiceHelper.query(HonorStudentConstant.NCKD_EVALQUEST_ENTITYID,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")));
@@ -110,7 +111,7 @@ public class CreateEvalQuestFormPlugin extends AbstractFormPlugin implements Plu
                 JobInfo jobInfo = new JobInfo();
                 jobInfo.setTaskClassname(CreateEvalQuestTask.class.getName());
                 Map<String, Object> params = new HashMap<>();
-                params.put(HonorStudentConstant.NCKD_EVALQUESTTPLID, evalQuestTplId);
+                params.put(HonorStudentConstant.NCKD_EVALQUESTTPL, evalQuestTpl);
                 params.put(HonorStudentConstant.NCKD_SELECTEDEVALRULE, selectedEvalRule);
                 params.put(HonorStudentConstant.NCKD_SELECTEDPERSON, selectedPerson);
                 //调度任务中获取不到当前用户信息
@@ -125,7 +126,13 @@ public class CreateEvalQuestFormPlugin extends AbstractFormPlugin implements Plu
                 // 2. 配置进度界面
                 JobFormInfo formInfo = new JobFormInfo(jobInfo);
                 formInfo.setCaption("生成综合评测问卷进度");
-                formInfo.setCloseCallBack(new CloseCallBack(EvalQuestTplListPlugin.class.getName(), HonorStudentConstant.TASK_CREATEEVAL));
+
+                String billFormId = ((ListView) this.getView().getParentView()).getBillFormId();
+                if(HonorStudentConstant.NCKD_EVALQUESTTPL_ENTITYID.equals(billFormId)) {
+                    formInfo.setCloseCallBack(new CloseCallBack(EvalQuestTplListPlugin.class.getName(), HonorStudentConstant.TASK_CREATEEVAL));
+                }else if(HonorStudentConstant.NCKD_EVALQUEST_ENTITYID.equals(billFormId)) {
+                    formInfo.setCloseCallBack(new CloseCallBack(EvalQuestListPlugin.class.getName(), HonorStudentConstant.TASK_CREATEEVAL));
+                }
                 formInfo.setClickClassName(CreateEvalQuestTaskClick.class.getName());
                 // 允许用户中途取消
                 //formInfo.setCanStop(true);
@@ -138,7 +145,8 @@ public class CreateEvalQuestFormPlugin extends AbstractFormPlugin implements Plu
                 this.getView().close();
             } else {
                 try {
-                    CreateEvalQuestService.batchGenerateEvalQuest(evalQuestTplId, selectedEvalRule, selectedPerson, null, null, null);
+                    CreateEvalQuestService.batchGenerateEvalQuest(evalQuestTpl, selectedEvalRule, selectedPerson, null, null, null);
+                    this.getView().getParentView().invokeOperation(FormConstant.REFRESH);
                     this.getView().getParentView().showSuccessNotification("生成综合评测问卷成功,可在【查看评价列表】中查看");
                     this.getView().close();
                 } catch (Exception e) {

+ 139 - 5
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/EvalQuestFormPlugin.java

@@ -2,14 +2,43 @@ package nckd.jxccl.hr.hstu.plugin.form;
 
 import kd.bos.bill.BillShowParameter;
 import kd.bos.bill.OperationStatus;
+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.MainEntityType;
+import kd.bos.entity.operate.OperateOptionConst;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.entity.property.MulBasedataProp;
+import kd.bos.form.CloseCallBack;
+import kd.bos.form.FieldTip;
+import kd.bos.form.FormShowParameter;
+import kd.bos.form.ShowType;
+import kd.bos.form.control.EntryGrid;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.events.BeforeDoOperationEventArgs;
+import kd.bos.form.events.ClosedCallBackEvent;
+import kd.bos.form.events.PreOpenFormEventArgs;
 import kd.bos.form.plugin.AbstractFormPlugin;
+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.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.enums.PerfPlanRoleEnum;
 import nckd.jxccl.base.common.utils.DateUtil;
+import nckd.jxccl.base.org.helper.PersonHelper;
+import nckd.jxccl.hr.hstu.business.CreateEvalQuestService;
+import nckd.jxccl.hr.hstu.common.HonorStudentConstant;
 
+import javax.naming.ldap.Control;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.EventObject;
+import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * 综合测评问卷-表单插件
@@ -19,15 +48,120 @@ import java.util.EventObject;
  */
 public class EvalQuestFormPlugin extends AbstractFormPlugin implements Plugin {
 
+    private final static Log logger = LogFactory.getLog(EvalQuestFormPlugin.class);
+
     @Override
-    public void afterBindData(EventObject e) {
-        super.afterBindData(e);
+    public void beforeBindData(EventObject e) {
+        super.beforeBindData(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));
+            DynamicObject evaluationRule = (DynamicObject)this.getModel().getValue(HonorStudentConstant.NCKD_EVALUATIONRULE);
+            Date date = evaluationRule.getDate(HonorStudentConstant.NCKD_BIZYEAR);
+            //initValue 方法赋值可避免触发值更新事件(例:检测到您有更改内容,是否不保存直接退出?的检测)
+            this.getModel().initValue(HonorStudentConstant.NCKD_BIZYEAR, DateUtil.getYear(date));
         }
     }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs e) {
+        String operateKey = e.getOperateKey();
+        if(HonorStudentConstant.OP_REFRESH_PEERS.equalsIgnoreCase(operateKey)){
+            //刷新同级人员
+
+
+            DynamicObject dataEntity = this.getModel().getDataEntity();
+            DynamicObjectCollection dynamicObjectCollection = dataEntity.getDynamicObjectCollection(HonorStudentConstant.NCKD_MULSELECTORG);
+            //弹出选择组织窗口
+            FormShowParameter showParameter = new FormShowParameter();
+            showParameter.setFormId(HonorStudentConstant.NCKD_EVALQUEST_SELECTORG);
+            showParameter.getOpenStyle().setShowType(ShowType.Modal);
+            showParameter.setCloseCallBack(new CloseCallBack(this, HonorStudentConstant.NCKD_EVALQUEST_SELECTORG));
+
+            showParameter.setCaption("同级评分人组织范围选择");
+            this.getView().showForm(showParameter);
+        }
+    }
+
+
+
+    @Override
+    public void closedCallBack(ClosedCallBackEvent closedCallBackEvent) {
+        String actionId = closedCallBackEvent.getActionId();
+        if(HonorStudentConstant.NCKD_EVALQUEST_SELECTORG.equalsIgnoreCase(actionId)){
+            //------------- 刷新同级人员 begin -------------
+            //获取子页面选择的组织范围
+            DynamicObjectCollection mulSelectOrg = (DynamicObjectCollection)closedCallBackEvent.getReturnData();
+            if(mulSelectOrg != null) {
+                //获取评价规则
+                DynamicObject dataEntity = this.getModel().getDataEntity();
+                DynamicObject evaluationRule = dataEntity.getDynamicObject(HonorStudentConstant.NCKD_EVALUATIONRULE);
+                evaluationRule = BusinessDataServiceHelper.loadSingle(evaluationRule.getLong(HonorStudentConstant.ID_KEY), HonorStudentConstant.EVALUATIONRULE_ENTITYID);
+
+                //获取评价对象
+                DynamicObject person = dataEntity.getDynamicObject(HonorStudentConstant.NCKD_EVALPERSON);
+                int proportion = evaluationRule.getInt(HonorStudentConstant.NCKD_PROPORTION);
+                int upper = evaluationRule.getInt(HonorStudentConstant.NCKD_UPPER);
+                int lower = evaluationRule.getInt(HonorStudentConstant.NCKD_LOWER);
+                //根据比例计算出的其他部门同级评分人的最小人数(同级评分人下限人数*其他同级评分人比例/100=其他同级评分人下限人数)
+                int otherLower = (lower * proportion) / 100;
+
+                //获取当前分录的数据
+                DynamicObjectCollection entryColl = this.getModel().getEntryEntity(HonorStudentConstant.NCKD_EVALQUESTENTRY);
+                //排除当前已有的领导和上级(避免评分人员重复)
+                List<Long> excludePersonList = entryColl.stream().filter(entry -> !entry.getString(HonorStudentConstant.NCKD_PERFPLANROLE).equals(PerfPlanRoleEnum.PEERS.getCode()))
+                        .map(entry -> entry.getDynamicObject(HonorStudentConstant.NCKD_PERSON).getLong(FormConstant.ID_KEY)).collect(Collectors.toList());
+
+                //获取同级打分人员
+                DynamicObjectCollection freshPeers = CreateEvalQuestService.fetchExpectedScorers(person, mulSelectOrg, excludePersonList, proportion, upper, lower, otherLower, Boolean.FALSE);
+
+                //删除原同级人员
+                entryColl.removeIf(dynamicObject -> dynamicObject.getString(HonorStudentConstant.NCKD_PERFPLANROLE).equals(PerfPlanRoleEnum.PEERS.getCode()));
+                //添加新生成的同级人员
+                entryColl.addAll(freshPeers);
+                //将选择的组织范围持久化到表头中(可以追随从什么组织范围选定的人员)
+                Object[] mulSelectOrgIds = new Object[mulSelectOrg.size()];
+                for (int i = 0; i < mulSelectOrg.size(); i++) {
+                    DynamicObject dynamicObject = mulSelectOrg.get(i);
+                    DynamicObject baseData = dynamicObject.getDynamicObject(FormConstant.BASEDATAID_KEY);
+                    mulSelectOrgIds[i] = baseData.getLong(FormConstant.ID_KEY);
+                }
+                //多选基础资料赋值
+                this.getModel().setValue(HonorStudentConstant.NCKD_MULSELECTORG, mulSelectOrgIds);
+                this.getView().updateView(HonorStudentConstant.NCKD_MULSELECTORG);
+
+
+
+                // 提前同步缓存(平台不建议调用updateEntryCache,因为苍穹平台默认会在所有action请求结束后自动调用updateCache方法,手动调用可能会引发性能问题)。
+                // 但是这里需要提前刷新缓存,确保执行保存前(invokeOperation("save"))数据被刷新能正确保存。该分录数据量不大,出现性能问题的概率不大
+                this.getModel().updateEntryCache(entryColl);
+                //刷新前端分录数据(一定要在刷新缓存后调用,不然会出现数据不一致的问题)
+                this.getView().updateView(HonorStudentConstant.NCKD_EVALQUESTENTRY);
+
+
+                //持久化到数据库
+                OperateOption option = OperateOption.create();
+                option.setVariableValue(OperateOptionConst.ISSHOWMESSAGE, Boolean.FALSE+"");
+                OperationResult operationResult = this.getView().invokeOperation(FormConstant.SAVE_KEY,option);
+                if(operationResult.isSuccess()){
+                    this.getView().showSuccessNotification("刷新同级人员并保存成功,本次更新人员以蓝色标识");
+
+                    //选中以颜色标识,增加体验性
+                    List<Integer> selectRows = new ArrayList<>();
+                    for (int i = 0; i < entryColl.size(); i++) {
+                        DynamicObject dynamicObject = entryColl.get(i);
+                        if(dynamicObject.getLong(FormConstant.ID_KEY) < 1){
+                            selectRows.add(i);
+                        }
+                    }
+                    //更新人员以蓝色背景色标识
+                    EntryGrid entryGrid = this.getControl(HonorStudentConstant.NCKD_EVALQUESTENTRY);
+                    int[] selectedRowArray = selectRows.stream().mapToInt(Integer::intValue).toArray();
+                    entryGrid.setRowBackcolor("#add4ff", selectedRowArray);
+                }
+            }
+            //------------- 刷新同级人员 end -------------
+        }
+    }
+
 }

+ 326 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/EvalQuestListPlugin.java

@@ -0,0 +1,326 @@
+package nckd.jxccl.hr.hstu.plugin.form;
+
+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.db.tx.TX;
+import kd.bos.db.tx.TXHandle;
+import kd.bos.entity.EntityMetadataCache;
+import kd.bos.entity.MainEntityType;
+import kd.bos.entity.constant.StatusEnum;
+import kd.bos.entity.datamodel.ListSelectedRowCollection;
+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.form.CloseCallBack;
+import kd.bos.form.ConfirmCallBackListener;
+import kd.bos.form.ConfirmTypes;
+import kd.bos.form.FormShowParameter;
+import kd.bos.form.MessageBoxOptions;
+import kd.bos.form.ShowType;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.events.ClosedCallBackEvent;
+import kd.bos.form.events.SetFilterEvent;
+import kd.bos.list.plugin.AbstractListPlugin;
+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.portal.util.SerializationUtils;
+import kd.bos.schedule.api.TaskInfo;
+import kd.bos.schedule.api.TaskStatusConstant;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.enums.PerfPlanRoleEnum;
+import nckd.jxccl.base.common.exception.ValidationException;
+import nckd.jxccl.base.common.utils.ConvertUtil;
+import nckd.jxccl.base.common.utils.StrFormatter;
+import nckd.jxccl.base.entity.helper.EntityHelper;
+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 org.apache.commons.lang3.StringUtils;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
+
+/**
+* 【优秀生】-综合测评问卷
+* @author W.Y.C
+* @date 2025/7/14 15:39
+* @version 1.0
+*/
+public class EvalQuestListPlugin extends AbstractListPlugin implements Plugin {
+
+    private static final Log logger = LogFactory.getLog(EvalQuestListPlugin.class);
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs e) {
+        String operateKey = e.getOperateKey();
+        if(HonorStudentConstant.OP_CREATEEVAL.equalsIgnoreCase(operateKey)){
+            //获取评价对象
+            ListSelectedRowCollection selectedRows = this.getSelectedRows();
+            FormShowParameter showParameter = new FormShowParameter();
+            showParameter.setFormId(HonorStudentConstant.NCKD_CREATEEVALQUEST_ENTITYID);
+            showParameter.getOpenStyle().setShowType(ShowType.Modal);
+            showParameter.setCloseCallBack(new CloseCallBack(this, HonorStudentConstant.NCKD_CREATEEVALQUEST_ENTITYID));
+            showParameter.setCaption("生成综合测评问卷");
+            this.getView().showForm(showParameter);
+        }else if(HonorStudentConstant.OP_GENERATE_QUEST.equalsIgnoreCase(operateKey)){
+            //--------------- 生成问卷(答卷) begin ---------------
+            ListSelectedRowCollection selectedRows = this.getSelectedRows();
+            List<Long> selectedRowIds = this.getSelectedRows().stream()
+                    .map(selectedRow -> ConvertUtil.toLong(selectedRow.getPrimaryKeyValue()))
+                    .collect(Collectors.toList());
+
+            StringJoiner fields = new StringJoiner(",");
+            fields.add(FormConstant.ID_KEY);
+            //评测状态
+            fields.add(HonorStudentConstant.NCKD_PERIODSTATE);
+            //问卷模板
+            fields.add(HonorStudentConstant.NCKD_EVALQUESTTPL+"."+FormConstant.ID_KEY);
+            //被评价人
+            fields.add(HonorStudentConstant.NCKD_EVALPERSON+"."+FormConstant.PERSON+"."+FormConstant.ID_KEY);
+            fields.add(HonorStudentConstant.NCKD_EVALPERSON+"."+FormConstant.PERSON+"."+FormConstant.NAME_KEY);
+            //分录
+            fields.add(HonorStudentConstant.NCKD_EVALQUESTENTRY+"."+FormConstant.ID_KEY);
+            fields.add(HonorStudentConstant.NCKD_EVALQUESTENTRY+"."+HonorStudentConstant.NCKD_PERSON+"."+FormConstant.ID_KEY);
+            fields.add(HonorStudentConstant.NCKD_EVALQUESTENTRY+"."+HonorStudentConstant.NCKD_PERSON+"."+HonorStudentConstant.PERSON+"."+FormConstant.ID_KEY);
+            fields.add(HonorStudentConstant.NCKD_EVALQUESTENTRY+"."+HonorStudentConstant.NCKD_PERFPLANROLE);
+            DynamicObject[] evalQuestArray = BusinessDataServiceHelper.load(HonorStudentConstant.NCKD_EVALQUEST_ENTITYID,
+                    fields.toString(),
+                    new QFilter[]{QFilterCommonHelper.getIdInFilter(selectedRowIds)});
+
+            // 1.校验状态
+            StringJoiner errorJoin = new StringJoiner("\n");
+            Arrays.stream(evalQuestArray).forEach(evalQuest -> {
+                String periodState = evalQuest.getString(HonorStudentConstant.NCKD_PERIODSTATE);
+                if(!periodState.equals("0")){
+                    DynamicObject person = evalQuest.getDynamicObject(HonorStudentConstant.NCKD_EVALPERSON).getDynamicObject(FormConstant.PERSON);
+                    errorJoin.add(StrFormatter.format("评价对象【{}】的测评状态非【未开始】,只有【未开始】状态的才能生成问卷",person.getString(FormConstant.NAME_KEY)));
+                }
+            });
+            if(errorJoin.length() > 0){
+                throw new ValidationException(errorJoin.toString());
+            }
+
+            // 2.校验打分人是否有领导和上级
+            validateEvaluatorRoles(evalQuestArray);
+
+            // 3.构建并保存答卷
+            buildAndSaveEvalResult(evalQuestArray);
+            // 4.刷新列表
+            this.getView().invokeOperation(FormConstant.REFRESH);
+            this.getView().showSuccessNotification("生成问卷成功");
+
+            //--------------- 生成问卷(答卷) end ---------------
+        }
+    }
+
+    /**
+     * 构建并保存答卷
+     * @param evalQuestArray 问卷列表
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/07/16 20:38
+     */
+    private void buildAndSaveEvalResult(DynamicObject[] evalQuestArray) throws KDBizException {
+        Long currUserId =  RequestContext.get().getCurrUserId();
+        Long 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);
+
+
+        //获取所有问卷模板
+        Set<Long> evalQuestTplSet = new HashSet<>();
+        Map<Long,List<DynamicObject>> evalQuestTplMap = new HashMap<>();
+        Arrays.stream(evalQuestArray).forEach(evalQuest -> {
+            long evalQuestTplId = evalQuest.getDynamicObject(HonorStudentConstant.NCKD_EVALQUESTTPL).getLong(FormConstant.ID_KEY);
+            evalQuestTplSet.add(evalQuestTplId);
+        });
+        DynamicObjectCollection evalQuestTplColl = QueryServiceHelper.query(HonorStudentConstant.NCKD_EVALQUESTTPL_ENTITYID,"id,NCKD_EVALQUESTTPLENTRY.*",new QFilter[]{new QFilter(FormConstant.ID_KEY, QCP.in,evalQuestTplSet)});
+        for (DynamicObject evalQuestTpl : evalQuestTplColl) {
+            long id = evalQuestTpl.getLong(HonorStudentConstant.ID_KEY);
+            evalQuestTplMap.computeIfAbsent(id, k -> new ArrayList<>()).add(evalQuestTpl);
+        }
+
+        //构建答卷
+        MainEntityType depEmpEntityType = EntityMetadataCache.getDataEntityType(PersonHelper.DEP_EMP_ENTITY_ID);
+        MainEntityType evalQuestTplEntryEntityType = EntityMetadataCache.getDataEntityType(HonorStudentConstant.NCKD_EVALQUESTTPLENTRYBAS_ENTITYID);
+        List<DynamicObject> evalResultToSaveList = new ArrayList<>();
+        Arrays.stream(evalQuestArray).forEach(evalQuest -> {
+            //评价对象
+            DynamicObject evalPerson = evalQuest.getDynamicObject(HonorStudentConstant.NCKD_EVALPERSON);
+            //循环评分人
+            DynamicObjectCollection entryColl = evalQuest.getDynamicObjectCollection(HonorStudentConstant.NCKD_EVALQUESTENTRY);
+            entryColl.forEach(entry -> {
+                //获取分录数据
+                long entryId = entry.getLong(FormConstant.ID_KEY);
+                DynamicObject personDynamicObject = QueryServiceHelper.queryOne(HonorStudentConstant.NCKD_EVALQUEST_ENTITYID,
+                        HonorStudentConstant.NCKD_EVALQUESTENTRY+"."+HonorStudentConstant.NCKD_PERSON+"."+FormConstant.ID_KEY+" personId",
+                        new QFilter[]{new QFilter(HonorStudentConstant.NCKD_EVALQUESTENTRY+"."+FormConstant.ID_KEY, QCP.equals, entryId)});
+                //评分人
+                long scorerId = personDynamicObject.getLong("personId");
+
+                DynamicObject buildEvalResult = EntityHelper.newAvailableBasicEntity(HonorStudentConstant.NCKD_EVALRESULT_ENTITYID);
+                buildEvalResult(evalQuest, buildEvalResult, evalPerson, depEmpEntityType, scorerId);
+
+                //构建答卷分录
+                DynamicObjectCollection entryEntity = buildEvalResult.getDynamicObjectCollection(HonorStudentConstant.NCKD_EVALRESULTENTRY);
+                long evalQuestTplId = evalQuest.getDynamicObject(HonorStudentConstant.NCKD_EVALQUESTTPL).getLong(FormConstant.ID_KEY);
+                List<DynamicObject> evalQuestTplList = evalQuestTplMap.get(evalQuestTplId);
+                if (evalQuestTplList != null) {
+                    evalQuestTplList.sort(Comparator.comparingInt(
+                            obj -> obj.getInt("nckd_evalquesttplentry.seq")
+                    ));
+                    for (DynamicObject evalQuestTpl : evalQuestTplList) {
+                        DynamicObject resultEntry = entryEntity.addNew();
+                        long evalQuestTplEntryId = evalQuestTpl.getLong("nckd_evalquesttplentry.id");
+                        DynamicObject evalQuestTplEntry = new DynamicObject(evalQuestTplEntryEntityType);
+                        evalQuestTplEntry.set(FormConstant.ID_KEY, evalQuestTplEntryId);
+                        resultEntry.set(HonorStudentConstant.NCKD_EVALQUESTTPLENTRY,evalQuestTplEntry);
+
+                    }
+                }
+                evalResultToSaveList.add(buildEvalResult);
+            });
+        });
+        OperateOption option = OperateOption.create();
+        // 忽消息提示
+        option.setVariableValue(OperateOptionConst.ISSHOWMESSAGE, Boolean.TRUE.toString());
+        //开启事务确保事务一致性
+        try (TXHandle t = TX.required(HonorStudentConstant.OP_GENERATE_QUEST)) {
+            try {
+                OperationResult operationResult = SaveServiceHelper.saveOperate(FormConstant.SAVE_KEY,HonorStudentConstant.NCKD_EVALQUEST_ENTITYID, evalResultToSaveList.toArray(new DynamicObject[0]), option);
+                if (!operationResult.isSuccess()) {
+                    StringJoiner errorMsg = new StringJoiner("\n");
+                    for (IOperateInfo error : operationResult.getAllErrorOrValidateInfo()) {
+                        errorMsg.add(error.getMessage());
+                    }
+                    throw new ValidationException(errorMsg.toString());
+                }
+                //修改问卷状态
+                for (DynamicObject evalQuest : evalQuestArray) {
+                    //问卷修改为就绪状态
+                    evalQuest.set(HonorStudentConstant.NCKD_PERIODSTATE,"3");
+                }
+                SaveServiceHelper.update(evalQuestArray);
+            } catch (Throwable e) {
+                // 异常回滚
+                t.markRollback();
+                throw e;
+            }
+        }
+
+    }
+
+    private static void buildEvalResult(DynamicObject evalQuest, DynamicObject mainObj, DynamicObject evalPerson, MainEntityType depEmpEntityType, long scorerId) {
+        //评价对象
+        mainObj.set(HonorStudentConstant.NCKD_PERSON, evalPerson);
+        //评分人
+        DynamicObject scorerPerson = new DynamicObject(depEmpEntityType);
+        scorerPerson.set(FormConstant.ID_KEY, scorerId);
+        mainObj.set(HonorStudentConstant.NCKD_SCORER, scorerPerson);
+        //对应问卷
+        mainObj.set(HonorStudentConstant.NCKD_EVALQUEST, evalQuest);
+        //评分状态
+        mainObj.set(HonorStudentConstant.NCKD_PERFPLANSTATE, "0");
+    }
+
+    @Override
+    public void closedCallBack(ClosedCallBackEvent closedCallBackEvent) {
+        String actionId = closedCallBackEvent.getActionId();
+        if(HonorStudentConstant.TASK_CREATEEVAL.equalsIgnoreCase(actionId)){
+            //生成评价问卷任务完成回调
+            Map<String, Object> result = (Map<String, Object>)closedCallBackEvent.getReturnData();
+            if(result != null) {
+                String taskInfoStr = (String) result.get("taskinfo");
+                if (StringUtils.isNotBlank(taskInfoStr)) {
+                    TaskInfo taskInfo = SerializationUtils.fromJsonString(taskInfoStr, TaskInfo.class);
+                    if (taskInfo.isTaskEnd()) {
+                        String status = taskInfo.getStatus();
+                        if (TaskStatusConstant.COMPLETED.equalsIgnoreCase(status)) {
+                            //刷新列表
+                            this.getView().invokeOperation(FormConstant.REFRESH);
+                            //成功提示
+                            this.getView().showConfirm(
+                                    "成功提示",
+                                    "生成综合评测问卷完成",
+                                    MessageBoxOptions.OK,
+                                    ConfirmTypes.Save,
+                                    new ConfirmCallBackListener()
+                            );
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void setFilter(SetFilterEvent e) {
+        super.setFilter(e);
+        e.setOrderBy("createtime asc");
+    }
+
+
+    /**
+     * 校验评分人是否有领导和上级评分人
+     * @param load 选择的问卷
+     * @return: java.lang.String
+     * @author W.Y.C
+     * @date: 2025/07/16 16:19
+     */
+    private static void validateEvaluatorRoles(DynamicObject[] load) throws ValidationException {
+        StringJoiner errorMsg = new StringJoiner("\n");
+        Arrays.stream(load).forEach(dynamicObject -> {
+            DynamicObject person = dynamicObject.getDynamicObject(HonorStudentConstant.NCKD_EVALPERSON).getDynamicObject(FormConstant.PERSON);
+            DynamicObjectCollection entryColl = dynamicObject.getDynamicObjectCollection(HonorStudentConstant.NCKD_EVALQUESTENTRY);
+
+            long leadCount = entryColl.stream()
+                    .filter(entry -> PerfPlanRoleEnum.LEAD.getCode().equals(entry.getString(HonorStudentConstant.NCKD_PERFPLANROLE)))
+                    .count();
+
+            long superiorCount = entryColl.stream()
+                    .filter(entry -> PerfPlanRoleEnum.SUPERIOR.getCode().equals(entry.getString(HonorStudentConstant.NCKD_PERFPLANROLE)))
+                    .count();
+
+            if (leadCount < 1) {
+                errorMsg.add(StrFormatter.format(
+                        "评价对象【{}】缺少领导评分人",
+                        person.getString(FormConstant.NAME_KEY)));
+            }
+
+            if (superiorCount < 1) {
+                errorMsg.add(StrFormatter.format(
+                        "评价对象【{}】缺少上级评分人",
+                        person.getString(FormConstant.NAME_KEY)));
+            }
+        });
+        if(errorMsg.length() > 0){
+            throw new ValidationException(errorMsg.toString());
+        }
+    }
+}

+ 50 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/EvalQuestSelectOrg.java

@@ -0,0 +1,50 @@
+package nckd.jxccl.hr.hstu.plugin.form;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.form.ConfirmCallBackListener;
+import kd.bos.form.MessageBoxOptions;
+import kd.bos.form.control.Control;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.utils.ConvertUtil;
+import nckd.jxccl.hr.hstu.common.HonorStudentConstant;
+
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+* 同级评分人组织范围选择(弹窗)插件
+* @author W.Y.C
+* @date 2025/7/15 14:45
+* @version 1.0
+*/
+public class EvalQuestSelectOrg extends AbstractFormPlugin implements Plugin {
+
+    @Override
+    public void registerListener(EventObject e) {
+        this.addClickListeners(HonorStudentConstant.OP_BTN_OK);
+    }
+
+    @Override
+    public void afterCreateNewData(EventObject e) {
+        super.afterCreateNewData(e);
+        //获取综合评测问卷已选的组织范围赋值到弹窗中
+        DynamicObjectCollection mulSelectOrg = this.getView().getParentView().getModel().getDataEntity().getDynamicObjectCollection(HonorStudentConstant.NCKD_MULSELECTORG);
+        this.getModel().setValue(HonorStudentConstant.NCKD_MULSELECTORG, mulSelectOrg);
+    }
+
+    @Override
+    public void click(EventObject evt) {
+        Control source = (Control) evt.getSource();
+        if (HonorStudentConstant.OP_BTN_OK.equals(source.getKey())) {
+            //获取选择的组织范围
+            DynamicObjectCollection mulSelectOrg = (DynamicObjectCollection)this.getModel().getValue(HonorStudentConstant.NCKD_MULSELECTORG);
+            //将选择的数据返回给父页面
+            this.getView().returnDataToParent(mulSelectOrg);
+            this.getView().close();
+        }
+    }
+
+}

+ 25 - 20
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/EvalQuestTplListPlugin.java

@@ -38,26 +38,28 @@ public class EvalQuestTplListPlugin extends AbstractListPlugin implements Plugin
         String operateKey = ((FormOperate) args.getSource()).getOperateKey();
         if(HonorStudentConstant.OP_CREATEEVAL.equals(operateKey)) {
             ListSelectedRowCollection selectedRows = this.getSelectedRows();
-            if (selectedRows.size() != 1) {
+            /*if (selectedRows.size() != 1) {
                 this.getView().showErrorNotification("请选择一条数据!");
                 args.setCancel(true);
                 return;
-            }
-            DynamicObject basedata = BusinessDataServiceHelper.loadSingle(
-                    selectedRows.get(0).getPrimaryKeyValue(),
-                    HonorStudentConstant.NCKD_EVALQUESTTPL_ENTITYID,
-                    HonorStudentConstant.STATUS+","+HonorStudentConstant.ENABLE
-            );
-            if (basedata != null) {
-                String status = basedata.getString(HonorStudentConstant.STATUS);
-                Boolean enable = basedata.getBoolean(HonorStudentConstant.ENABLE);
+            }*/
+            if(!selectedRows.isEmpty()) {
+                DynamicObject basedata = BusinessDataServiceHelper.loadSingle(
+                        selectedRows.get(0).getPrimaryKeyValue(),
+                        HonorStudentConstant.NCKD_EVALQUESTTPL_ENTITYID,
+                        HonorStudentConstant.STATUS + "," + HonorStudentConstant.ENABLE
+                );
+                if (basedata != null) {
+                    String status = basedata.getString(HonorStudentConstant.STATUS);
+                    Boolean enable = basedata.getBoolean(HonorStudentConstant.ENABLE);
 
-                // 是否可用
-                boolean isAvailable = StatusEnum.C.toString().equals(status) && Boolean.TRUE.equals(enable);
-                if(!isAvailable){
-                    this.getView().showErrorNotification(StrFormatter.format("【{}】该问卷模板状态不可用",selectedRows.get(0).getName()));
-                    args.setCancel(true);
-                    return;
+                    // 是否可用
+                    boolean isAvailable = StatusEnum.C.toString().equals(status) && Boolean.TRUE.equals(enable);
+                    if (!isAvailable) {
+                        this.getView().showErrorNotification(StrFormatter.format("【{}】该问卷模板状态不可用", selectedRows.get(0).getName()));
+                        args.setCancel(true);
+                        return;
+                    }
                 }
             }
         }
@@ -74,17 +76,20 @@ public class EvalQuestTplListPlugin extends AbstractListPlugin implements Plugin
             showParameter.setFormId(HonorStudentConstant.NCKD_CREATEEVALQUEST_ENTITYID);
             showParameter.getOpenStyle().setShowType(ShowType.Modal);
             showParameter.setCloseCallBack(new CloseCallBack(this, HonorStudentConstant.NCKD_CREATEEVALQUEST_ENTITYID));
-            showParameter.setCustomParam(HonorStudentConstant.NCKD_EVALQUESTTPLID,selectedRows.get(0).getPrimaryKeyValue());
-            showParameter.setCaption("批量新增综合测评问卷");
+            if(!selectedRows.isEmpty()) {
+                showParameter.setCustomParam(HonorStudentConstant.NCKD_EVALQUESTTPLID, selectedRows.get(0).getPrimaryKeyValue());
+            }
+            showParameter.setCaption("生成综合测评问卷");
             this.getView().showForm(showParameter);
         }
     }
 
+
     @Override
     public void closedCallBack(ClosedCallBackEvent closedCallBackEvent) {
         String actionId = closedCallBackEvent.getActionId();
         if(HonorStudentConstant.TASK_CREATEEVAL.equals(actionId)){
-            //生成评价对象任务完成回调
+            //生成评价问卷任务完成回调
             Map<String, Object> result = (Map<String, Object>)closedCallBackEvent.getReturnData();
             if(result != null) {
                 String taskInfoStr = (String) result.get("taskinfo");
@@ -95,7 +100,7 @@ public class EvalQuestTplListPlugin extends AbstractListPlugin implements Plugin
                         String status = taskInfo.getStatus();
                         if (TaskStatusConstant.COMPLETED.equals(status)) {
                             this.getView().showConfirm(
-                                    "成功",
+                                    "成功提示",
                                     "生成综合评测问卷完成,可在【查看评价列表】中查看",
                                     MessageBoxOptions.OK,
                                     ConfirmTypes.Save,

+ 74 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/plugin/form/EvalQuestTreeListPlugin.java

@@ -0,0 +1,74 @@
+package nckd.jxccl.hr.hstu.plugin.form;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.list.events.BuildTreeListFilterEvent;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.hr.hbp.business.dao.factory.HRBaseDaoFactory;
+import kd.hr.hbp.common.constants.org.OrgTreeDynEnum;
+import kd.hr.hbp.common.util.org.model.OrgTreeModel;
+import kd.hr.hbp.formplugin.web.org.template.AdminOrgTreeListTemplate;
+import kd.sdk.hr.hspm.common.utils.PropertyHelper;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.utils.ConvertUtil;
+import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
+
+import java.util.EventObject;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+* 【优秀生】-综合测评问卷左树右表
+* @author W.Y.C
+* @date 2025/7/15 20:33
+* @version 1.0
+*/
+public class EvalQuestTreeListPlugin extends AdminOrgTreeListTemplate {
+
+    public EvalQuestTreeListPlugin() {
+        super(new OrgTreeModel(OrgTreeDynEnum.ADMIN_STRUCT.getDynEntity(), OrgTreeDynEnum.ADMIN_MAIN_ENTITY.getDynEntity(), Boolean.TRUE, Boolean.TRUE, Boolean.FALSE));
+    }
+
+    @Override
+    public void afterBindData(EventObject e) {
+        super.afterBindData(e);
+        this.getView().setVisible(false, FormConstant.SEARCH_DATE_KEY);
+    }
+
+    @Override
+    public void afterCreateNewData(EventObject e) {
+        //设置默认开启包含子部门
+        DynamicObject dynamicObject = this.getModel().getDataEntity();
+        if(PropertyHelper.existProperty(dynamicObject, FormConstant.CHKINCLUDECHILD)) {
+            this.getModel().setValue(FormConstant.CHKINCLUDECHILD, Boolean.TRUE);
+        }
+    }
+
+    @Override
+    protected QFilter buildNodeClickFilter(BuildTreeListFilterEvent buildTreeListFilterEvent) {
+        String focusNodeId = buildTreeListFilterEvent.getNodeId().toString();
+        QFilter nodeClickFilter = null;
+        if(!this.getTreeModel().getRoot().getId().equalsIgnoreCase(focusNodeId)){
+
+            QFilter focusNodeIdFilter = new QFilter("adminorg.id", QCP.equals, Long.valueOf(focusNodeId));
+            Boolean chkIncludeChild = ConvertUtil.toBoolean(this.getModel().getValue(FormConstant.CHKINCLUDECHILD));
+            //是否包含子部门
+            if(chkIncludeChild) {
+                DynamicObject structlongnumber = HRBaseDaoFactory.getInstance(this.getEntityName()).queryOne("structlongnumber", new QFilter[]{QFilterCommonHelper.getCurrentVersionFilter(), QFilterCommonHelper.getInitStatusFinishFilter(),
+                        QFilterCommonHelper.getDataStatusFilter(), focusNodeIdFilter});
+                String focusNodeLongNumber = (String) structlongnumber.get("structlongnumber");
+                QFilter filter = new QFilter("structlongnumber", QCP.like, focusNodeLongNumber + "%");
+                DynamicObjectCollection dynamicObjects = HRBaseDaoFactory.getInstance(this.getEntityName()).queryColl("adminorg.id adminorg", new QFilter[]{filter, QFilterCommonHelper.getCurrentVersionFilter(), QFilterCommonHelper.getInitStatusFinishFilter()}, null);
+                Set<Long> ids = new HashSet<>(dynamicObjects.size());
+                dynamicObjects.forEach(dynamicObject -> ids.add(dynamicObject.getLong("adminorg")));
+                nodeClickFilter = new QFilter("nckd_evalperson.adminorg.id", QCP.in, ids);
+            }else{
+                nodeClickFilter = new QFilter("nckd_evalperson.adminorg.id", QCP.in, ConvertUtil.toLong(focusNodeId));
+            }
+        }
+        return nodeClickFilter;
+    }
+}

+ 9 - 13
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/task/CreateEvalQuestTask.java

@@ -9,6 +9,7 @@ import kd.bos.logging.LogFactory;
 import kd.bos.orm.query.QFilter;
 import kd.bos.schedule.executor.AbstractTask;
 import kd.bos.servicehelper.QueryServiceHelper;
+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.org.helper.PersonHelper;
@@ -36,29 +37,24 @@ public class CreateEvalQuestTask extends AbstractTask implements ProgressCallbac
     @Override
     public void execute(RequestContext ctx, Map<String, Object> params) {
         try {
-            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);
+
+            Map<String, Object> evalQuestTplMap = ConvertUtil.toMap(params.get(HonorStudentConstant.NCKD_EVALQUESTTPL));
+            Map<String, Object> selectedEvalRuleMap = ConvertUtil.toMap(params.get(HonorStudentConstant.NCKD_SELECTEDEVALRULE));
+            List<Map<String,Object>> selectedPersonList = ConvertUtil.toList(params.get(HonorStudentConstant.NCKD_SELECTEDPERSON));
             Long crrUserId = ConvertUtil.toLong(params.get(HonorStudentConstant.KEY_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))});
+
+            DynamicObject evalQuestTpl = QueryServiceHelper.queryOne(HonorStudentConstant.NCKD_EVALQUESTTPL_ENTITYID, FormConstant.ID_KEY, new QFilter[]{QFilterCommonHelper.getIdEqFilter(evalQuestTplMap.get(HonorStudentConstant.ID_KEY))});
+            DynamicObject selectedEvalRule = QueryServiceHelper.queryOne(HonorStudentConstant.EVALUATIONRULE_ENTITYID, FormConstant.ID_KEY, 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);
+            CreateEvalQuestService.batchGenerateEvalQuest(evalQuestTpl, selectedEvalRule, selectedPerson, this, crrUserId, currOrgId);
 
             feedbackProgress(100, "任务完成", null);
         } catch (Exception e) {