Selaa lähdekoodia

Merge branch 'hr'

jtd 3 päivää sitten
vanhempi
säilyke
82355c9d7d

+ 2 - 1
code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/entity/helper/EntityHelper.java

@@ -25,10 +25,11 @@ public class EntityHelper {
     }
 
     public static DynamicObject newEntity(String entityName, Long id){
-        DynamicObject dy =BusinessDataServiceHelper.newDynamicObject(entityName);
+        DynamicObject dy = BusinessDataServiceHelper.newDynamicObject(entityName);
         dy.set(FormConstant.ID_KEY, id);
         return dy;
     }
+
     /**
      * 创建一个状态为"可用"的基础资料对象
      * @param entityName 实体标识

+ 374 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/homs/business/application/event/GenerateEmploymentContractEventServicePlugin.java

@@ -0,0 +1,374 @@
+package nckd.jxccl.hr.homs.business.application.event;
+
+import kd.bos.bec.api.IEventServicePlugin;
+import kd.bos.bec.model.EntityEvent;
+import kd.bos.bec.model.KDBizEvent;
+import kd.bos.coderule.api.CodeRuleInfo;
+import kd.bos.coderule.opplugin.util.OrgUtil;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.QueryEntityType;
+import kd.bos.exception.KDBizException;
+import kd.bos.exception.KDException;
+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.orm.util.CollectionUtils;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.MetadataServiceHelper;
+import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.fi.bcm.business.serviceHelper.UserServiceHelper;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import kd.hr.hbp.business.servicehelper.HRQueryEntityHelper;
+import kd.hr.hbp.common.util.HRArrayUtils;
+import kd.hr.hbp.common.util.HRDateTimeUtils;
+import kd.hr.hbp.common.util.HRObjectUtils;
+import kd.hr.hbp.common.util.HRStringUtils;
+import kd.sdk.hr.hbp.business.helper.ruleengine.RuleEngineServiceHelper;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.base.entity.helper.EntityHelper;
+import nckd.jxccl.hr.homs.common.application.event.GECEventConstant;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 入职生成劳动合同事件服务插件
+ * @author: jtd
+ * @date: 2025/11/23 20:56
+ */
+public class GenerateEmploymentContractEventServicePlugin implements IEventServicePlugin {
+
+    private enum ContractTypeCatEnum {
+        CON(1366793061547512832L, "1010_S"),
+        EMP(1366793180992898048L, "1020_S"),
+        OTHER(1366793317483942912L, "1030_S");
+
+        String value;
+        long pkId;
+
+        private ContractTypeCatEnum(long pkId, String value) {
+            this.pkId = pkId;
+            this.value = value;
+        }
+
+        public String getValue() {
+            return this.value;
+        }
+
+        public long getPkId() {
+            return this.pkId;// 37
+        }
+    }
+
+    private static final Log LOGGER = LogFactory.getLog(GenerateEmploymentContractEventServicePlugin.class);
+    /** 入职变动大类ID */
+    private static final Long CHGEVENT_ID = 1010L;
+    /** 应用编码 */
+    private static final String BIZAPP = "hlcm";
+    /** 场景编码 */
+    private static final String SCENE_NUMBER = "ContractApplyNew";
+    /** 业务组织编码 */
+    private static final String BU_NUMBER = "01";
+    /** 场景输入参数-劳动合同新签申请 */
+    private static final String HLCM_CONTRACTAPPLYNEW = "hlcm_contractapplynew";
+    /** 场景输出参数-合同模板 */
+    private static final String HLCM_CONTRACTTEMPLATE = "hlcm_contracttemplate";
+    /** 根据组织分配查询员工信息 */
+    private static final String GET_EMPLOYEE_INFO_QUERY = "getemployeeinfoquery";
+
+    @Override
+    public Object handleEvent(KDBizEvent evt) {
+        try {
+            EntityEvent entityEvent = (EntityEvent) evt;
+            // 获取 事务变动记录ID
+            List<String> businessKeys = entityEvent.getBusinesskeys();
+            if (CollectionUtils.isEmpty(businessKeys)) {
+                LOGGER.warn("GenerateEmploymentContractEventServicePlugin businessKeys is empty, nothing to process.");
+                return null;
+            }
+            // 根据变动事务ID判断变动大类是否入职
+            List<Long> longList = entityEvent.getBusinesskeys().stream().map(Long::valueOf).collect(Collectors.toList());
+            // 变动大类
+            String chgeventKey = String.join(".", GECEventConstant.CHGCATEGORY_KEY, GECEventConstant.CHGEVENT_KEY);
+            // 数据idKey
+            String dataidKey = String.join(".", GECEventConstant.ENTRY_ENTITY_KEY, GECEventConstant.DATAID_KEY);
+            // 过滤 事务变动ID 并且 变动大类=入职 并且 变动实体编码=组织分配 并且 数据变化=新增
+            QFilter qFilter = new QFilter(GECEventConstant.ID_KEY, QCP.in, longList)
+                    .and(new QFilter(chgeventKey, QCP.equals, CHGEVENT_ID))
+                    .and(new QFilter(String.join(".", GECEventConstant.ENTRY_ENTITY_KEY, GECEventConstant.CHGENTITY_KEY, GECEventConstant.NUMBER_KEY), QCP.equals, GECEventConstant.ASSIGNMENT_ENTITYID))
+                    .and(new QFilter(String.join(".", GECEventConstant.ENTRY_ENTITY_KEY, GECEventConstant.CHGMODE_KEY), QCP.equals, "0"));
+            // 获取事务变动记录
+            DynamicObject[] records = HRBaseServiceHelper.create(GECEventConstant.HPFS_CHGRECORD_ENTITY).queryOriginalArray(dataidKey, new QFilter[]{qFilter});
+            if (HRArrayUtils.isEmpty(records)) {
+                LOGGER.warn("GenerateEmploymentContractEventServicePlugin is empty, nothing to process.");
+                return null;
+            }
+            // 获取组织分配ID
+            List<Long> assignmentIds = Arrays.stream(records).map(record -> record.getLong(dataidKey)).collect(Collectors.toList());
+            // 生成劳动合同
+            generateEmploymentContract(assignmentIds);
+            return null;
+        } catch (Exception e) {
+            LOGGER.error("GenerateEmploymentContractEventServicePlugin Error processing KDBizEvent: {}", e.getMessage(), e);
+            throw e;
+        }
+    }
+
+    private void generateEmploymentContract(List<Long> assignmentIds) throws KDException {
+        // hrpi_assignment.id
+        String assignmentIdKey = String.join(".", GECEventConstant.ASSIGNMENT_ENTITYID, GECEventConstant.ID_KEY);
+        // 查询字段
+        String queryFields = QueryFieldBuilder.create()
+                .add(GECEventConstant.HSPM_PERCONTACT_E_PHONE)
+                .add(GECEventConstant.HSPM_TRIALPERIOD_E_PROBATION)
+                .add(GECEventConstant.HSPM_TRIALPERIOD_E_PROBATIONUNIT)
+                .add(GECEventConstant.HSPM_TRIALPERIOD_E_PREENDDATE)
+                .add(GECEventConstant.HSPM_TRIALPERIOD_E_TRIALSTARTDATE)
+                .add(GECEventConstant.HOUSEHOLDREGISTER_ADDRESSTYPE_ID)
+                .add(GECEventConstant.HOUSEHOLDREGISTER_ADDRESSINFO)
+                .add(GECEventConstant.RESIDENTIALADDRESS_ADDRESSTYPE_ID)
+                .add(GECEventConstant.RESIDENTIALADDRESS_ADDRESSINFO)
+                .add(GECEventConstant.HSPM_PERCRE_E_CREDENTIALSTYPE_ID)
+                .add(GECEventConstant.HSPM_PERCRE_E_NUMBER)
+                .add(GECEventConstant.CNIDARD_CREDENTIALSTYPE_ID)
+                .add(GECEventConstant.CNIDARD_NUMBER)
+                .add(GECEventConstant.HAOS_ADMINORGHR_CORPORATEORG_ID)
+                .add(GECEventConstant.HRPI_EMPPOSORGREL_STARTDATE)
+                .add(GECEventConstant.HRPI_EMPLOYEE_ID)
+                .add(GECEventConstant.HRPI_EMPLOYEE_EMPNUMBER)
+                .add(GECEventConstant.HRPI_EMPLOYEE_NAME)
+                .add(GECEventConstant.HRPI_EMPLOYEE_NATIONALITY_ID)
+                .add(GECEventConstant.HRPI_EMPLOYEE_NATIONALITY_NUMBER)
+                .add(GECEventConstant.HAOS_ADMINORGHR_BELONGCOMPANY_ID)
+                .add(GECEventConstant.HAOS_ADMINORGHR_BELONGCOMPANY_SOURCEVID)
+                .add(GECEventConstant.HAOS_ADMINORGHR_ID)
+                .add(GECEventConstant.HAOS_ADMINORGHR_SOURCEVID)
+                .add(GECEventConstant.HBPM_POSITIONHR_ID)
+                .add(GECEventConstant.HBPM_POSITIONHR_SOURCEVID)
+                .add(GECEventConstant.HRPI_EMPPOSORGREL_JOB_NUMBER)
+                .add(GECEventConstant.HRPI_EMPPOSORGREL_POSTYPE_NUMBER)
+                .add(GECEventConstant.HRPI_EMPPOSORGREL_ORGRELSEQ)
+                .add(GECEventConstant.HRPI_EMPPOSORGREL_ASSIGNMENT_NUMBER)
+                .add(GECEventConstant.HRPI_EMPPOSORGREL_ASSIGNMENT_ORG_NUMBER)
+                .add(GECEventConstant.HRPI_EMPPOSORGREL_ASSIGNMENT_EMPLOYEE_EMPNUMBER)
+                .add(GECEventConstant.HRPI_EMPPOSORGREL_WORKPLACE_NUMBER)
+                .buildSelect();
+        // 过滤条件
+        QFilter qFilter = new QFilter(assignmentIdKey, QCP.in, assignmentIds);
+        // 排序字段
+        String orderBys = QueryFieldBuilder.create().orderBy(GECEventConstant.HRPI_EMPPOSORGREL_STARTDATE, assignmentIdKey).buildOrder();
+        // 根据组织分配获取员工信息
+        QueryEntityType queryEntityType = (QueryEntityType) MetadataServiceHelper.getDataEntityType(GET_EMPLOYEE_INFO_QUERY);
+        DynamicObjectCollection personInfoColl = HRQueryEntityHelper.getInstance().getQueryDyoColl(queryEntityType, queryFields, new QFilter[]{qFilter}, orderBys);
+        // 获取法律实体
+        List<Long> corporateorgIds = personInfoColl.stream().map(personInfo -> personInfo.getLong(GECEventConstant.HAOS_ADMINORGHR_CORPORATEORG_ID)).collect(Collectors.toList());
+        // 获取聘用单位
+        DynamicObject[] signCompanyDyos = HRBaseServiceHelper.create(GECEventConstant.HBSS_SIGNCOMPANY_ENTITY).query(String.join(",", GECEventConstant.LAWENTITY_KEY, GECEventConstant.VID_KEY), new QFilter[]{new QFilter(GECEventConstant.LAWENTITY_KEY, QCP.in, corporateorgIds)});
+        // 处理成 key->法律实体ID value->聘用单位ID
+        Map<Long, DynamicObject> signCompanyMap = Arrays.stream(signCompanyDyos).collect(Collectors.toMap(signCompanyDyo -> signCompanyDyo.getDynamicObject(GECEventConstant.LAWENTITY_KEY).getLong(GECEventConstant.ID_KEY), signCompanyDyo -> signCompanyDyo, (oldValue, newValue) -> newValue));
+        // 构建 劳动合同新签对象
+        List<DynamicObject> contractApplyNewDyoList = new ArrayList<DynamicObject>();
+        HRBaseServiceHelper contractApplyNewServiceHelper = new HRBaseServiceHelper(GECEventConstant.HLCM_CONTRACTAPPLYNEW_ENTITY);
+        for (DynamicObject personInfo : personInfoColl) {
+            DynamicObject contractApplyNewDyo = contractApplyNewServiceHelper.generateEmptyDynamicObject();
+
+            // 组织分配ID
+            Long assignmentId = personInfo.getLong(assignmentIdKey);
+            // 设置 单据编号和合同编号
+            CodeRuleInfo codeRuleInfo = CodeRuleServiceHelper.getCodeRule(contractApplyNewDyo.getDataEntityType().getName(), contractApplyNewDyo, OrgUtil.getMainOrgId(contractApplyNewDyo));
+            if (codeRuleInfo == null) {
+                throw new KDBizException(String.format("没有可使用的编码规则,组织分配ID[%s]", assignmentId));
+            }
+            String number = CodeRuleServiceHelper.readNumber(codeRuleInfo, contractApplyNewDyo);
+            contractApplyNewDyo.set(GECEventConstant.BILL_NO_KEY, number);
+            contractApplyNewDyo.set(GECEventConstant.CONTRACTNUMBER_KEY, number);
+            // 设置 签署业务类型
+            contractApplyNewDyo.set(GECEventConstant.BUSINESSTYPE_KEY, "1");// 默认新签
+            // 设置 合同协议类型
+            contractApplyNewDyo.set(GECEventConstant.PROTOCOLTYPE_KEY, "1");// 默认劳动合同
+            // 设置 单据状态
+            contractApplyNewDyo.set(GECEventConstant.BILL_STATUS_KEY, "A");// 默认暂存
+            // 设置 审批状态
+            contractApplyNewDyo.set(GECEventConstant.AUDIT_STATUS, "A");// 默认劳动合同
+            // 设置 人事管理组织
+            contractApplyNewDyo.set(GECEventConstant.ORG_KEY, personInfo.getDynamicObject(GECEventConstant.ASSIGNMENT_KEY).getDynamicObject(GECEventConstant.ORG_KEY));
+            // 设置 员工
+            contractApplyNewDyo.set(GECEventConstant.ASSIGNMENT_KEY, personInfo.getDynamicObject(GECEventConstant.ASSIGNMENT_KEY));
+            // 设置 员工
+            contractApplyNewDyo.set(GECEventConstant.EMPLOYEE_KEY, personInfo.getDynamicObject(GECEventConstant.ASSIGNMENT_KEY).getDynamicObject(GECEventConstant.EMPLOYEE_KEY));
+            // 设置 工号
+            contractApplyNewDyo.set(GECEventConstant.EMP_NUMBER_KEY, personInfo.getString(GECEventConstant.HRPI_EMPLOYEE_EMPNUMBER));
+            // 设置 姓名
+            contractApplyNewDyo.set(GECEventConstant.EMPNAME_KEY, personInfo.getString(GECEventConstant.HRPI_EMPLOYEE_NAME));
+            // 设置 国籍
+            contractApplyNewDyo.set(GECEventConstant.NATION_KEY, EntityHelper.newEntity(GECEventConstant.HBSS_NATIONALITY_ENTITY, personInfo.getLong(GECEventConstant.HRPI_EMPLOYEE_NATIONALITY_ID)));
+            // 设置 任职类型
+            contractApplyNewDyo.set(GECEventConstant.POSTYPE_KEY, personInfo.getDynamicObject(GECEventConstant.HRPI_EMPPOSORGREL_POSTYPE));
+            // 设置 任职序号
+            contractApplyNewDyo.set(GECEventConstant.ORGRELSEQ_KEY, personInfo.getInt(GECEventConstant.HRPI_EMPPOSORGREL_ORGRELSEQ));
+            // 设置 常驻工作地
+            contractApplyNewDyo.set(GECEventConstant.BASELOCATION_KEY, personInfo.getDynamicObject(GECEventConstant.HRPI_EMPPOSORGREL_WORKPLACE));
+            // 设置 公司
+            contractApplyNewDyo.set(GECEventConstant.CURCOMPANY_KEY, EntityHelper.newEntity(GECEventConstant.ADMINORGHR_ENTITYID, personInfo.getLong(GECEventConstant.HAOS_ADMINORGHR_BELONGCOMPANY_ID)));
+            // 设置 公司历史
+            contractApplyNewDyo.set(GECEventConstant.SIGNEDCOMPANYHIS_KEY, EntityHelper.newEntity(GECEventConstant.ADMINORGHR_ENTITYID, personInfo.getLong(GECEventConstant.HAOS_ADMINORGHR_BELONGCOMPANY_SOURCEVID)));
+            // 设置 部门
+            contractApplyNewDyo.set(GECEventConstant.CURDEPT_KEY, EntityHelper.newEntity(GECEventConstant.ADMINORGHR_ENTITYID, personInfo.getLong(GECEventConstant.HAOS_ADMINORGHR_ID)));
+            // 设置 部门历史
+            contractApplyNewDyo.set(GECEventConstant.DEPARTMENTHIS_KEY, EntityHelper.newEntity(GECEventConstant.ADMINORGHR_ENTITYID, personInfo.getLong(GECEventConstant.HAOS_ADMINORGHR_SOURCEVID)));
+            // 设置 岗位
+            contractApplyNewDyo.set(GECEventConstant.CURPOSTION_KEY, EntityHelper.newEntity(GECEventConstant.HBPM_POSITIONHR, personInfo.getLong(GECEventConstant.HBPM_POSITIONHR_ID)));
+            // 设置 岗位历史
+            contractApplyNewDyo.set(GECEventConstant.POSTIONHIS_KEY, EntityHelper.newEntity(GECEventConstant.HBPM_POSITIONHR, personInfo.getLong(GECEventConstant.HBPM_POSITIONHR_SOURCEVID)));
+            // 设置 职位
+            contractApplyNewDyo.set(GECEventConstant.CURJOB_KEY, personInfo.getDynamicObject(GECEventConstant.HRPI_EMPPOSORGREL_JOB));
+            // 设置 证件类型
+            Long cardTypeId = personInfo.getLong(GECEventConstant.HSPM_PERCRE_E_CREDENTIALSTYPE_ID);
+            cardTypeId = HRObjectUtils.isEmpty(cardTypeId) ? personInfo.getLong(GECEventConstant.CNIDARD_CREDENTIALSTYPE_ID) : cardTypeId;
+            if (HRObjectUtils.isEmpty(cardTypeId)) {
+                throw new KDBizException(String.format("没有可使用的证件类型,组织分配ID[%s]", assignmentId));
+            }
+            contractApplyNewDyo.set(GECEventConstant.CARDTYPE_KEY, EntityHelper.newEntity(GECEventConstant.HBSS_CREDENTIALSTYPE_ENTITY, cardTypeId));
+            // 设置 证件号码
+            String cardNumber = personInfo.getString(GECEventConstant.HSPM_PERCRE_E_NUMBER);
+            cardNumber = HRStringUtils.isBlank(cardNumber) ? personInfo.getString(GECEventConstant.CNIDARD_NUMBER) : cardNumber;
+            if (HRStringUtils.isBlank(cardNumber)) {
+                throw new KDBizException(String.format("没有可使用的证件号码,组织分配ID[%s]", assignmentId));
+            }
+            contractApplyNewDyo.set(GECEventConstant.CARDNUMBER_KEY, cardNumber);
+            // 设置 户籍地址
+            contractApplyNewDyo.set(GECEventConstant.HOUSEHOLDREGISTER_KEY, personInfo.getString(GECEventConstant.HOUSEHOLDREGISTER_ADDRESSINFO));
+            // 设置 居住地址
+            contractApplyNewDyo.set(GECEventConstant.RESIDENTIALADDRESS_KEY, personInfo.getString(GECEventConstant.RESIDENTIALADDRESS_ADDRESSINFO));
+            // 设置 联系方式
+            contractApplyNewDyo.set(GECEventConstant.EMPPHONE_KEY, personInfo.getString(GECEventConstant.HSPM_PERCONTACT_E_PHONE));
+            // 设置应签单位、实签单位
+            DynamicObject signCompanyDyo = signCompanyMap.get(personInfo.getLong(GECEventConstant.HAOS_ADMINORGHR_CORPORATEORG_ID));
+            if (HRObjectUtils.isEmpty(signCompanyDyo)) {
+                throw new KDBizException(String.format("没有可使用的应签/实签单位,组织分配ID[%s]", assignmentId));
+            }
+            contractApplyNewDyo.set(GECEventConstant.SUGGESTSIGNCOMPANY_KEY, signCompanyDyo);
+            contractApplyNewDyo.set(GECEventConstant.ACTUALSIGNCOMPANY_KEY, signCompanyDyo);
+            // 设置 应签单位历史、实签单位历史
+            DynamicObject signCompanyHisDyo = EntityHelper.newEntity(GECEventConstant.HBSS_SIGNCOMPANYHIS_ENTITY, signCompanyDyo.getLong(GECEventConstant.VID_KEY));
+            contractApplyNewDyo.set(GECEventConstant.SUGGESTSIGNCOMPHIS_KEY, signCompanyHisDyo);
+            contractApplyNewDyo.set(GECEventConstant.ACTUALSIGNCOMPANYHIS_KEY, signCompanyHisDyo);
+            // 设置 合同类型
+            contractApplyNewDyo.set(GECEventConstant.CONTRACTTYPE_KEY, EntityHelper.newEntity(GECEventConstant.HBSS_CONTRACTTYPES_ENTITY, 1406559486235128832L));// 默认劳动合同
+            // 设置 合同期限类型
+            contractApplyNewDyo.set(GECEventConstant.PERIODTYPE_KEY, EntityHelper.newEntity(GECEventConstant.HBSS_TIMELIMITTYPE_ENTITY, 1383941525804767232L));// 默认固定期限
+            // 设置 合同开始日期
+            Date startDate = personInfo.getDate(GECEventConstant.HRPI_EMPPOSORGREL_STARTDATE);
+            contractApplyNewDyo.set(GECEventConstant.STARTDATE_KEY, startDate);
+            // 设置 合同期限
+            contractApplyNewDyo.set(GECEventConstant.PERIOD_KEY, 5);// 默认5年
+            // 设置 合同期限单位
+            contractApplyNewDyo.set(GECEventConstant.PERIODUNIT_KEY, "1");// 默认年
+            // 设置 合同结束日期
+            Date endDate = HRDateTimeUtils.addDay(HRDateTimeUtils.addYear(startDate, 5), -1);// +5年-1天
+            contractApplyNewDyo.set(GECEventConstant.ENDDATE_KEY, endDate);
+            // 设置 试用期开始日期
+            contractApplyNewDyo.set(GECEventConstant.PROBATIONSTARTDATE_KEY, personInfo.getDate(GECEventConstant.HSPM_TRIALPERIOD_E_TRIALSTARTDATE));
+            // 设置 试用结束日期
+            contractApplyNewDyo.set(GECEventConstant.PROBATIONENDDATE_KEY, personInfo.getDate(GECEventConstant.HSPM_TRIALPERIOD_E_PREENDDATE));
+            // 设置 试用期限
+            contractApplyNewDyo.set(GECEventConstant.PROBATIONPERIOD_KEY, personInfo.getInt(GECEventConstant.HSPM_TRIALPERIOD_E_PROBATION));
+            // 设置 试用期单位
+            contractApplyNewDyo.set(GECEventConstant.PROBATIONUNIT_KEY, personInfo.getString(GECEventConstant.HSPM_TRIALPERIOD_E_PROBATIONUNIT));
+            // 设置 签署方式
+            contractApplyNewDyo.set(GECEventConstant.SIGNWAY_KEY, "2");// 默认纸质签署
+            // 设置 创建人
+            contractApplyNewDyo.set(GECEventConstant.CREATOR_KEY, UserServiceHelper.getCurrentUserId());
+
+            // 构建 规则引擎所需参数
+            Map requestMap = new HashMap();
+            // 应用ID
+            requestMap.put("bizApp", BIZAPP);
+            // 场景编码
+            requestMap.put("sceneNumber", SCENE_NUMBER);
+            // 组织ID
+            requestMap.put("buNumber", BU_NUMBER);
+            Map inputParams = new HashMap();
+            // 构建参数所需对象 劳动合同新签申请
+            inputParams.put(HLCM_CONTRACTAPPLYNEW, contractApplyNewDyo);
+            requestMap.put("inputParams", inputParams);
+            // 调用规则引擎设置合同信息
+            setContractInfoByRuleEngine(requestMap, contractApplyNewDyo);
+            if (HRObjectUtils.isEmpty(contractApplyNewDyo.get(GECEventConstant.CONTRACTEMPLATE_KEY))) {
+                throw new KDBizException(String.format("没有可使用的合同模板,组织分配ID[%s]", assignmentId));
+            }
+            // 放入集合
+            contractApplyNewDyoList.add(contractApplyNewDyo);
+        }
+        // 查询出合同模板所有属性,否则界面不显示数据,界面通过contractemplatehis显示合同模板数据,数据来源于合同模板中的vid属性和其他相关属性
+        Set<Long> contracTemplateIds = contractApplyNewDyoList.stream().map(contractApplyNewDyo -> contractApplyNewDyo.getLong(String.join(".", GECEventConstant.CONTRACTEMPLATE_KEY, GECEventConstant.ID_KEY))).collect(Collectors.toSet());
+        DynamicObject[] contractTemplateDyos = BusinessDataServiceHelper.load(contracTemplateIds.toArray(), MetadataServiceHelper.getDataEntityType(GECEventConstant.HLCM_CONTRACTTEMPLATE_ENTITY));
+        // 处理成 key->合同模板ID value->合同模板dyo
+        Map<Long, DynamicObject> contractTemplateMap = Arrays.stream(contractTemplateDyos).collect(Collectors.toMap(dynamicObject -> dynamicObject.getLong(GECEventConstant.ID_KEY), dynamicObject -> dynamicObject, (oldValue, newValue) -> newValue));
+        for (DynamicObject contractApplyNewDyo : contractApplyNewDyoList) {
+            contractApplyNewDyo.set(GECEventConstant.CONTRACTEMPLATE_KEY, contractTemplateMap.get(contractApplyNewDyo.getLong(String.join(".", GECEventConstant.CONTRACTEMPLATE_KEY, GECEventConstant.ID_KEY))));
+        }
+        // 生成合同
+        SaveServiceHelper.saveOperate(GECEventConstant.HLCM_CONTRACTAPPLYNEW_ENTITY, contractApplyNewDyoList.toArray(new DynamicObject[0]));
+    }
+
+    private void setContractInfoByRuleEngine(Map requestMap, DynamicObject contractApplyNewDyo) {
+        Map responseMap = RuleEngineServiceHelper.callRuleEngine(requestMap);
+        if (HRStringUtils.equals("200", (String) responseMap.get("responseCode"))) {
+            List<Map<String, Object>> policyResultList = (List)responseMap.get("policyResults");
+            if (policyResultList != null && !policyResultList.isEmpty()) {
+                // 倒转 有多条策略命中的情况下取第一条作为最终数据进行覆盖
+                Collections.reverse(policyResultList);
+                for (Map<String, Object> policyResult : policyResultList) {
+                    List<Map<String, Object>> ruleResultList = (List) policyResult.get("ruleResults");
+                    if (ruleResultList != null && !ruleResultList.isEmpty()) {
+                        for (Map<String, Object> ruleResult : ruleResultList) {
+                            Map<String, Object> matchResults = (Map<String, Object>) ruleResult.get("matchResults");
+                            setContractInfoTemplate(matchResults, contractApplyNewDyo);
+                        }
+                    }
+                    Map<String, Object> defaultResultList = (Map<String, Object>) policyResult.get("defaultResults");
+                    setContractInfoTemplate(defaultResultList, contractApplyNewDyo);
+                }
+            }
+        }
+    }
+
+    private void setContractInfoTemplate(Map<String, Object> results, DynamicObject contractApplyNewDyo) {
+        if (!CollectionUtils.isEmpty(results)) {
+            Object tempObject = results.get(HLCM_CONTRACTTEMPLATE);
+            DynamicObject[] dynamicObjects = new DynamicObject[0];
+            if (tempObject instanceof DynamicObject) {
+                dynamicObjects = new DynamicObject[]{(DynamicObject)tempObject};
+            } else if (tempObject instanceof DynamicObject[]) {
+                dynamicObjects = (DynamicObject[]) tempObject;
+            } else if (tempObject instanceof DynamicObjectCollection) {
+                dynamicObjects = ((DynamicObjectCollection) tempObject).toArray(new DynamicObject[0]);
+            }
+            // 遍历取到第一个
+            for(int index = dynamicObjects.length - 1; index >= 0; --index) {
+                DynamicObject dy = dynamicObjects[index];
+                if (!HRObjectUtils.isEmpty(dy)) {
+                    // 因为在循环内,没法根据合同ID查询类型,所以这里直接赋值
+                    contractApplyNewDyo.set(GECEventConstant.CONTRACTEMPLATE_KEY, dy);
+                    /*Long contractTypeGroupId = dy.getLong(String.join(".", GECEventConstant.CONTRACTTYPE_KEY, GECEventConstant.GROUP_KEY, GECEventConstant.ID_KEY));
+                    if (contractTypeGroupId.equals(ContractTypeCatEnum.CON.getPkId()) || contractTypeGroupId.equals(ContractTypeCatEnum.EMP.getPkId())) {
+                        contractApplyNewDyo.set(GECEventConstant.CONTRACTEMPLATE_KEY, dy);
+                        break;
+                    }*/
+                }
+            }
+        }
+    }
+
+}

+ 223 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/homs/common/application/event/GECEventConstant.java

@@ -0,0 +1,223 @@
+package nckd.jxccl.hr.homs.common.application.event;
+
+import nckd.jxccl.base.common.constant.FormConstant;
+
+/**
+ * 入职生成劳动合同事件常量
+ * @author: jtd
+ * @date: 2025/11/23 21:43
+ */
+public class GECEventConstant extends FormConstant {
+
+    /** 劳动合同新签单标识 */
+    public static final String HLCM_CONTRACTAPPLYNEW_ENTITY = "hlcm_contractapplynew";
+    /** 合同期限类型实体标识 */
+    public static final String HBSS_TIMELIMITTYPE_ENTITY = "hbss_timelimittype";
+    /** 合同模板实体标识 */
+    public static final String HLCM_CONTRACTTEMPLATE_ENTITY = "hlcm_contracttemplate";
+    /** 聘用单位实体标识 */
+    public static final String HBSS_SIGNCOMPANY_ENTITY = "hbss_signcompany";
+    /** 聘用单位历史实体标识 */
+    public static final String HBSS_SIGNCOMPANYHIS_ENTITY = "hbss_signcompanyhis";
+    /** 证件类型实体标识 */
+    public static final String HBSS_CREDENTIALSTYPE_ENTITY = "hbss_credentialstype";
+    /** 合同类型实体标识 */
+    public static final String HBSS_CONTRACTTYPES_ENTITY = "hbss_contracttypes";
+    /** 国籍实体标识 */
+    public static final String HBSS_NATIONALITY_ENTITY = "hbss_nationality";
+    /** 合同模板历史实体标识 */
+    public static final String HLCM_CONTRACTTEMPLATEHIS_ENTITY = "hlcm_contracttemplatehis";
+
+    /** 证件号码 */
+    public static final String CARDNUMBER_KEY = "cardnumber";
+    /** 联系电话 */
+    public static final String EMPPHONE_KEY = "empphone";
+    /** 合同编号 */
+    public static final String CONTRACTNUMBER_KEY = "contractnumber";
+    /** 合同开始日期 */
+    public static final String STARTDATE_KEY = "startdate";
+    /** 签署方式 */
+    public static final String SIGNWAY_KEY = "signway";
+    /** 证件类型 */
+    public static final String CARDTYPE_KEY = "cardtype";
+    /** 员工 */
+    public static final String ASSIGNMENT_KEY = "assignment";
+    /** 姓名 */
+    public static final String EMPNAME_KEY = "empname";
+    /** 户籍地址 */
+    public static final String HOUSEHOLDREGISTER_KEY = "householdregister";
+    /** 居住地址 */
+    public static final String RESIDENTIALADDRESS_KEY = "residentialaddress";
+    /** 合同模板 */
+    public static final String CONTRACTEMPLATE_KEY = "contractemplate";
+    /** 合同模板 */
+    public static final String CONTRACTEMPLATEHIS_KEY = "contractemplatehis";
+    /** 变动类型 */
+    public static final String CHGCATEGORY_KEY = "chgcategory";
+    /** 所属变动大类 */
+    public static final String CHGEVENT_KEY = "chgevent";
+    /** 事务变动记录实体标识 */
+    public static final String HPFS_CHGRECORD_ENTITY = "hpfs_chgrecord";
+    /** 变动实体 */
+    public static final String CHGENTITY_KEY = "chgentity";
+    /** 数据id */
+    public static final String DATAID_KEY = "dataid";
+    /** 数据变化 */
+    public static final String CHGMODE_KEY = "chgmode";
+    /** 应签单位 */
+    public static final String SUGGESTSIGNCOMPANY_KEY = "suggestsigncompany";
+    /** 实签单位 */
+    public static final String ACTUALSIGNCOMPANY_KEY = "actualsigncompany";
+    /** 法律实体 */
+    public static final String LAWENTITY_KEY = "lawentity";
+    /** 历史版本ID */
+    public static final String VID_KEY = "vid";
+    /** 合同类型 */
+    public static final String CONTRACTTYPE_KEY = "contracttype";
+    /** 合同期限类型 */
+    public static final String PERIODTYPE_KEY = "periodtype";
+    /** 合同期限 */
+    public static final String PERIOD_KEY = "period";
+    /** 合同期限单位 */
+    public static final String PERIODUNIT_KEY = "periodunit";
+    /** 合同结束日期 */
+    public static final String ENDDATE_KEY = "enddate";
+    /** 试用期开始日期 */
+    public static final String PROBATIONSTARTDATE_KEY = "probationstartdate";
+    /** 试用期结束日期 */
+    public static final String PROBATIONENDDATE_KEY = "probationenddate";
+    /** 试用期限 */
+    public static final String PROBATIONPERIOD_KEY = "probationperiod";
+    /** 试用期单位 */
+    public static final String PROBATIONUNIT_KEY = "probationunit";
+    /** 公司 */
+    public static final String CURCOMPANY_KEY = "curcompany";
+    /** 部门 */
+    public static final String CURDEPT_KEY = "curdept";
+    /** 岗位 */
+    public static final String CURPOSTION_KEY = "curpostion";
+    /** 职位 */
+    public static final String CURJOB_KEY = "curjob";
+    /** 合同大类 */
+    public static final String GROUP_KEY = "group";
+    /** 签署业务类型 */
+    public static final String BUSINESSTYPE_KEY = "businesstype";
+    /** 合同协议类型 */
+    public static final String PROTOCOLTYPE_KEY = "protocoltype";
+    /** 国籍 */
+    public static final String NATION_KEY = "nation";
+    /** 公司历史 */
+    public static final String SIGNEDCOMPANYHIS_KEY = "signedcompanyhis";
+    /** 行政组织历史 */
+    public static final String DEPARTMENTHIS_KEY = "departmenthis";
+    /** 岗位历史 */
+    public static final String POSTIONHIS_KEY = "postionhis";
+    /** 应签单位历史 */
+    public static final String SUGGESTSIGNCOMPHIS_KEY = "suggestsigncomphis";
+    /** 实签单位历史 */
+    public static final String ACTUALSIGNCOMPANYHIS_KEY = "actualsigncompanyhis";
+    /** 任职类型 */
+    public static final String POSTYPE_KEY = "postype";
+    /** 任职序号 */
+    public static final String ORGRELSEQ_KEY = "orgrelseq";
+    /** 常驻工作地 */
+    public static final String BASELOCATION_KEY = "baselocation";
+
+    /** 任职经历.开始日期 */
+    public static final String HRPI_EMPPOSORGREL_STARTDATE = "startdate";
+    /** 任职经历.职位 */
+    public static final String HRPI_EMPPOSORGREL_JOB = "job";
+    /** 任职经历.职位.编码 */
+    public static final String HRPI_EMPPOSORGREL_JOB_NUMBER = "job.number";
+    /** 任职经历.任职序号 */
+    public static final String HRPI_EMPPOSORGREL_ORGRELSEQ = "orgrelseq";
+    /** 任职经历.任职类型 */
+    public static final String HRPI_EMPPOSORGREL_POSTYPE = "postype";
+    /** 任职经历.任职类型.编码 */
+    public static final String HRPI_EMPPOSORGREL_POSTYPE_NUMBER = "postype.number";
+    /** 任职经历.组织分配 */
+    public static final String HRPI_EMPPOSORGREL_ASSIGNMENT = "assignment";
+    /** 任职经历.组织分配.组织分配号 */
+    public static final String HRPI_EMPPOSORGREL_ASSIGNMENT_NUMBER = "assignment.number";
+    /** 任职经历.组织分配.人事管理组织.编码 */
+    public static final String HRPI_EMPPOSORGREL_ASSIGNMENT_ORG_NUMBER = "assignment.org.number";
+    /** 任职经历.员工.编码 */
+    public static final String HRPI_EMPPOSORGREL_ASSIGNMENT_EMPLOYEE_EMPNUMBER = "assignment.employee.empnumber";
+    /** 任职经历.常驻工作地 */
+    public static final String HRPI_EMPPOSORGREL_WORKPLACE = "workplace";
+    /** 任职经历.常驻工作地.编码 */
+    public static final String HRPI_EMPPOSORGREL_WORKPLACE_NUMBER = "workplace.number";
+
+    /** 联系方式.ID */
+    public static final String HSPM_PERCONTACT_E_ID = "hspm_percontact_e.id";
+    /** 联系方式.手机号码 */
+    public static final String HSPM_PERCONTACT_E_PHONE = "hspm_percontact_e.phone";
+
+    /** 试用期.ID */
+    public static final String HSPM_TRIALPERIOD_E_ID = "hspm_trialperiod_e.id";
+    /** 试用期.试用期长度 */
+    public static final String HSPM_TRIALPERIOD_E_PROBATION = "hspm_trialperiod_e.probation";
+    /** 试用期.试用期单位 */
+    public static final String HSPM_TRIALPERIOD_E_PROBATIONUNIT = "hspm_trialperiod_e.probationunit";
+    /** 试用期.预计转正日期 */
+    public static final String HSPM_TRIALPERIOD_E_PREENDDATE = "hspm_trialperiod_e.preenddate";
+    /** 试用期.试用开始日期 */
+    public static final String HSPM_TRIALPERIOD_E_TRIALSTARTDATE = "hspm_trialperiod_e.trialstartdate";
+
+    /** 户口所在地.ID */
+    public static final String HOUSEHOLDREGISTER_ID = "householdregister.id";
+    /** 户口所在地.地址类型.ID */
+    public static final String HOUSEHOLDREGISTER_ADDRESSTYPE_ID = "householdregister.addresstype.id";
+    /** 户口所在地.详细地址信息 */
+    public static final String HOUSEHOLDREGISTER_ADDRESSINFO = "householdregister.addressinfo";
+
+    /** 现居住地址.ID */
+    public static final String RESIDENTIALADDRESS_ID = "residentialaddress.id";
+    /** 现居住地址.地址类型.ID */
+    public static final String RESIDENTIALADDRESS_ADDRESSTYPE_ID = "residentialaddress.addresstype.id";
+    /** 现居住地址.详细地址信息 */
+    public static final String RESIDENTIALADDRESS_ADDRESSINFO = "residentialaddress.addressinfo";
+
+    /** 证件信息.ID */
+    public static final String HSPM_PERCRE_E_ID = "hspm_percre_e.id";
+    /** 证件信息.证件类型.ID */
+    public static final String HSPM_PERCRE_E_CREDENTIALSTYPE_ID = "hspm_percre_e.credentialstype.id";
+    /** 证件信息.证件号码 */
+    public static final String HSPM_PERCRE_E_NUMBER = "hspm_percre_e.number";
+
+    /** 中国居民身份证.ID */
+    public static final String CNIDARD_ID = "cnidard.id";
+    /** 中国居民身份证.证件类型.ID */
+    public static final String CNIDARD_CREDENTIALSTYPE_ID = "cnidard.credentialstype.id";
+    /** 中国居民身份证.证件号码 */
+    public static final String CNIDARD_NUMBER = "cnidard.number";
+
+    /** HR行政组织.ID */
+    public static final String HAOS_ADMINORGHR_ID = "haos_adminorghr.id";
+    /** HR行政组织.法律实体.ID */
+    public static final String HAOS_ADMINORGHR_CORPORATEORG_ID = "haos_adminorghr.corporateorg.id";
+
+    /** 员工.ID */
+    public static final String HRPI_EMPLOYEE_ID = "hrpi_employee.id";
+    /** 员工.工号 */
+    public static final String HRPI_EMPLOYEE_EMPNUMBER = "hrpi_employee.empnumber";
+    /** 员工.姓名 */
+    public static final String HRPI_EMPLOYEE_NAME = "hrpi_employee.name";
+    /** 员工.国籍.ID */
+    public static final String HRPI_EMPLOYEE_NATIONALITY_ID = "hrpi_employee.nationality.id";
+    /** 员工.国籍.编码 */
+    public static final String HRPI_EMPLOYEE_NATIONALITY_NUMBER = "hrpi_employee.nationality.number";
+
+    /** HR行政组织.所属公司.ID */
+    public static final String HAOS_ADMINORGHR_BELONGCOMPANY_ID = "haos_adminorghr.belongcompany.id";
+    /** HR行政组织.所属公司.关联历史版本 */
+    public static final String HAOS_ADMINORGHR_BELONGCOMPANY_SOURCEVID = "haos_adminorghr.belongcompany.sourcevid";
+    /** HR行政组织.关联历史版本 */
+    public static final String HAOS_ADMINORGHR_SOURCEVID = "haos_adminorghr.sourcevid";
+
+    /** 岗位信息维护.ID */
+    public static final String HBPM_POSITIONHR_ID = "hbpm_positionhr.id";
+    /** 岗位信息维护.关联历史版本 */
+    public static final String HBPM_POSITIONHR_SOURCEVID = "hbpm_positionhr.sourcevid";
+
+}

+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/business/.gitkeep


+ 237 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/business/application/task/SpecialJobDurationRefreshTask.java

@@ -0,0 +1,237 @@
+package nckd.jxccl.hr.hspm.business.application.task;
+
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.exception.KDException;
+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.operation.SaveServiceHelper;
+import nckd.jxccl.base.common.utils.ConvertUtil;
+import nckd.jxccl.hr.hspm.common.application.task.SJDRTaskConstant;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 特殊工种时长刷新作业
+ * @author: jtd
+ * @date: 2025/11/21 23:23
+ */
+public class SpecialJobDurationRefreshTask extends AbstractTask {
+
+    private static final Log LOGGER = LogFactory.getLog(SpecialJobDurationRefreshTask.class);
+
+    private enum EntityType {
+        EMPPOSORGREL(SJDRTaskConstant.HRPI_EMPPOSORGREL, SJDRTaskConstant.POSITION_KEY),
+        SPECIALWORKLOG(SJDRTaskConstant.NCKD_HSPM_SPECWRKLOG_E_ENTITY, SJDRTaskConstant.NCKD_POSITION_KEY);
+
+        private final String entity;
+        private final String positionKey;
+
+        EntityType(String entity, String positionKey) {
+            this.entity = entity;
+            this.positionKey = positionKey;
+        }
+
+        public String getEntity() {
+            return this.entity;
+        }
+
+        public String getPositionKey() {
+            return this.positionKey;
+        }
+    }
+
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+        // 初始化
+        Boolean init = ConvertUtil.toBoolean(map.get("init"), false);
+        if (init) {
+            // 初始化任职经历处理
+            initProcess(EntityType.EMPPOSORGREL);
+            // 初始化特殊工种记录处理
+            initProcess(EntityType.SPECIALWORKLOG);
+        } else {
+            // 任职经历处理
+            process(EntityType.EMPPOSORGREL);
+            // 特殊工种记录处理
+            process(EntityType.SPECIALWORKLOG);
+        }
+    }
+
+    /**
+     * 计算时长
+     * @param entityType
+     * @param dataDy
+     * @param lastDyObj
+     */
+    private void calculateMon(EntityType entityType, DynamicObject dataDy, DynamicObject lastDyObj) {
+        // 工种截止时间
+        Date workEndDate = dataDy.getDynamicObject(entityType.getPositionKey()).getDate(SJDRTaskConstant.NCKD_WORKENDDATE_KEY);
+        // 开始日期
+        Date startDate = dataDy.getDate(SJDRTaskConstant.STARTDATE);
+        // 结束日期
+        Date endDate = dataDy.getDate(SJDRTaskConstant.ENDDATE);
+        // 如果结束日期 晚于 工种截止时间 则结束日期取工种截止时间
+        if (workEndDate != null && endDate.after(workEndDate)) {
+            endDate = workEndDate;
+        }
+        // 结束日期-1
+        Calendar endCalendar = Calendar.getInstance();
+        endCalendar.setTime(endDate);
+        endCalendar.add(Calendar.DATE, -1);
+        endDate = endCalendar.getTime();
+        // 先计算两个日期的月份差,考虑到跨年的情况,取绝对值
+        int month = Math.abs((startDate.getMonth()+1) - (endDate.getMonth()+1));
+        // 计算是否满一个月
+        int startDay = startDate.getDate();
+        int endDay = endDate.getDate();
+        // 如果跨月 并且 结束日期小于开始日期 则月-1(不满一个月所以需要减)
+        if (month > 0 && endDay < startDay) {
+            month -= 1;
+        }
+        // 设置从事时长
+        dataDy.set(SJDRTaskConstant.NCKD_SERVICEMON_KEY, month);
+        // 累计从事时长
+        BigDecimal totalmon = BigDecimal.ZERO;
+        // 如果从事时长大于0
+        if (month > 0) {
+            // 获取特殊工种年限
+            int specwrkyrs = dataDy.getDynamicObject(entityType.getPositionKey()).getDynamicObject(SJDRTaskConstant.NCKD_SPEJOBPROP_KEY).getInt(SJDRTaskConstant.NCKD_SPECWRKYRS_KEY);
+            // 累计从事时长=从事时长/特殊工种年限*12,保留两位小数向下取整
+            totalmon = new BigDecimal(month).divide(new BigDecimal(specwrkyrs*12), 2, RoundingMode.DOWN);
+        }
+        // 获取上一条数据的累计从事时长 相加
+        if (lastDyObj != null) {
+            BigDecimal lastTotalmon = lastDyObj.getBigDecimal(SJDRTaskConstant.NCKD_TOTALMON_KEY);
+            totalmon = lastTotalmon.add(totalmon);
+        }
+        dataDy.set(SJDRTaskConstant.NCKD_TOTALMON_KEY, totalmon);
+    }
+
+    /**
+     * 获取数据
+     * @param entityType
+     * @param customFilter
+     * @return
+     */
+    private List<DynamicObject> getDataDys(EntityType entityType, QFilter customFilter) {
+        // 员工编码
+        String empNumberKey = String.join(".", SJDRTaskConstant.EMPLOYEE_KEY, SJDRTaskConstant.EMP_NUMBER_KEY);
+        // 特殊工种性质
+        String speJobPropKey = String.join(".", entityType.getPositionKey(), SJDRTaskConstant.NCKD_SPEJOBPROP_KEY);
+        // 工种截止时间
+        String workEndDateKey = String.join(".", entityType.getPositionKey(), SJDRTaskConstant.NCKD_WORKENDDATE_KEY);
+        // 特殊工种年限
+        String specwrkyrsKey = String.join(".", speJobPropKey, SJDRTaskConstant.NCKD_SPECWRKYRS_KEY);
+        // 查询 开始日期、结束日期、员工编码、工种截止时间、特殊工种年限、从事时间、累计从事时间
+        String selectProperties = String.join(",", SJDRTaskConstant.STARTDATE, SJDRTaskConstant.ENDDATE, empNumberKey, workEndDateKey, specwrkyrsKey, SJDRTaskConstant.NCKD_SERVICEMON_KEY, SJDRTaskConstant.NCKD_TOTALMON_KEY);
+        // 过滤 特殊工种性质有值
+        QFilter qFilter = new QFilter(speJobPropKey, QCP.large_than, 0L);
+        // 并且 特殊工种年限>0
+        qFilter.and(new QFilter(specwrkyrsKey, QCP.large_than, 0L));
+        if (customFilter != null) {
+            qFilter.and(customFilter);
+        }
+        // 排序 员工编码、开始时间、结束时间、特殊工种性质编码
+        String orderBy = String.join(",", empNumberKey, SJDRTaskConstant.STARTDATE, SJDRTaskConstant.ENDDATE, String.join(".", entityType.getPositionKey(), SJDRTaskConstant.INDEX_KEY));
+        // 获取数据
+        DynamicObject[] dataDys = BusinessDataServiceHelper.load(entityType.getEntity(), selectProperties, new QFilter[]{qFilter}, orderBy);
+        // 过滤 工种截止时间没有值 或 工种截止时间晚于开始日期
+        List<DynamicObject> result = new ArrayList<DynamicObject>();
+        for (int i = 0; i < dataDys.length; i++) {
+            DynamicObject dataDy = dataDys[i];
+            DynamicObject positionDy = dataDy.getDynamicObject(entityType.getPositionKey());
+            Date startDate = dataDy.getDate(SJDRTaskConstant.STARTDATE);
+            Date workEndDate = positionDy.getDate(SJDRTaskConstant.NCKD_WORKENDDATE_KEY);
+            if (workEndDate == null || workEndDate.after(startDate)) {
+                result.add(dataDy);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 初始化数据处理
+     * @param entityType
+     */
+    private void initProcess(EntityType entityType) {
+        // 获取 所有员工的数据
+        List<DynamicObject> empPosRgelDys = getDataDys(entityType, null);
+        // 分组 员工编码
+        Map<String, List<DynamicObject>> dataDyMap = empPosRgelDys.stream().collect(Collectors.groupingBy(dataDy -> dataDy.getDynamicObject(SJDRTaskConstant.EMPLOYEE_KEY).getString(SJDRTaskConstant.NUMBER_KEY)));
+        // 处理数据
+        List<DynamicObject> data = new ArrayList<DynamicObject>();
+        for (List<DynamicObject> dataDyList : dataDyMap.values() ) {
+            for (int i = 0, size = dataDyList.size(); i < size; i++) {
+                DynamicObject dataDy = dataDyList.get(i);
+                calculateMon(entityType, dataDy, i > 0 ? dataDyList.get(i-1) : null);
+                data.add(dataDy);
+            }
+        }
+        // 保存
+        SaveServiceHelper.save((DynamicObject[]) data.toArray());
+    }
+
+    /**
+     * 数据处理
+     */
+    private void process(EntityType entityType) {
+        // 过滤 雇佣阶段最新记录=1 并且 在职=1
+        QFilter qFilter = new QFilter(SJDRTaskConstant.EMPSTAGELATESTRECORD_KEY, QCP.equals, EnableEnum.YES.getCode());
+        qFilter.and(SJDRTaskConstant.ISHIRED_KEY, QCP.equals, EnableEnum.YES.getCode());
+        // 查询所有员工当前生效的雇佣信息
+        DynamicObject[] empentrelDys = BusinessDataServiceHelper.load(SJDRTaskConstant.HRPI_EMPENTREL, SJDRTaskConstant.EMPLOYEE_KEY, new QFilter[]{qFilter});
+        // 当前在职的员工
+        List<Long> employeeIds = Arrays.stream(empentrelDys).map(empentrelDy -> empentrelDy.getDynamicObject(SJDRTaskConstant.EMPLOYEE_KEY).getLong(SJDRTaskConstant.ID_KEY)).collect(Collectors.toList());
+        // 获取 所有在职员工的数据
+        qFilter = new QFilter(SJDRTaskConstant.EMPLOYEE_KEY, QCP.in, employeeIds);
+        List<DynamicObject> allDatalDys = getDataDys(entityType, qFilter);
+        // 分组 根据员工编码
+        Map<String, List<DynamicObject>> allDataDyMap = allDatalDys.stream().collect(Collectors.groupingBy(dataDy -> dataDy.getDynamicObject(SJDRTaskConstant.EMPLOYEE_KEY).getString(SJDRTaskConstant.EMP_NUMBER_KEY)));
+        // 获取 昨天(不含时间)
+        Date yesterday = Date.from(LocalDate.now().minusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
+        // 过滤 开始日期<=昨天 并且 结束日期>=昨天
+        qFilter = new QFilter(SJDRTaskConstant.STARTDATE, QCP.less_equals, yesterday).and(SJDRTaskConstant.ENDDATE, QCP.large_equals, yesterday);
+        // 获取 所有员工生效中的数据
+        List<DynamicObject> dataDys = getDataDys(entityType, qFilter);
+        // 处理数据
+        for (int i = 0, is = dataDys.size(); i < is; i++) {
+            DynamicObject dataDy = dataDys.get(i);
+            // 获取 员工编码
+            String empNumber = dataDy.getDynamicObject(SJDRTaskConstant.EMPLOYEE_KEY).getString(SJDRTaskConstant.EMP_NUMBER_KEY);
+            // 从所有数据中获取当前员工数据的上一条数据
+            List<DynamicObject> dataDyList = allDataDyMap.get(empNumber);
+            DynamicObject lastDataDy = null;
+            for (int j = 0, js = dataDyList.size(); j < js; j++) {
+                DynamicObject tempDy = dataDyList.get(j);
+                if (tempDy.getLong(SJDRTaskConstant.ID_KEY) == dataDy.getLong(SJDRTaskConstant.ID_KEY) && j > 0) {
+                    lastDataDy = dataDyList.get(j-1);
+                    break;
+                }
+            }
+            // 判断获取到的上一条任职经历是否等于当前生效任职经历的上一条数据
+            if (lastDataDy != null && i > 0 && lastDataDy.getLong(SJDRTaskConstant.ID_KEY) == dataDys.get(i-1).getLong(SJDRTaskConstant.ID_KEY)) {
+                lastDataDy = dataDys.get(i-1);
+            }
+            // 计算时长
+            calculateMon(entityType, dataDy, lastDataDy);
+        }
+        // 保存
+        SaveServiceHelper.save((DynamicObject[]) dataDys.toArray());
+    }
+}

+ 22 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/common/AppflgConstant.java

@@ -0,0 +1,22 @@
+/**
+ * This is a kingdee cosmic template project that is automatically generated by the Kingdee cosmic development assistant plugin. 
+ * If there are any issues during the use process, you can provide feedback to the kingdee developer community website.
+ * Website: https://developer.kingdee.com/developer?productLineId=29
+ * Author: liebin.zheng
+ * Generate Date: 2025-05-26 16:28:10
+ */
+package nckd.jxccl.hr.hspm.common;
+
+/**
+ * hr云init应用-通用常量类<br>
+ * 代码中不能存在硬编码敏感信息,如账号、密码、http外链、ftp外链、邮箱等。<br>
+ * 标识或缓存的常量,需以"KEY_"、"FID_"、"ENTRY_"或"SUBENTRY_"作为变量的前缀。<br>
+ *
+ * @author nckd
+ * @date 2025-05-26 16:28:10
+ */
+public class AppflgConstant {
+	
+	public static final String KEY_APP_NAME = "hr-init";
+
+}

+ 39 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/common/application/task/SJDRTaskConstant.java

@@ -0,0 +1,39 @@
+package nckd.jxccl.hr.hspm.common.application.task;
+
+import nckd.jxccl.base.common.constant.FormConstant;
+
+/**
+ * 特殊工种时长刷新作业常量
+ * @author: jtd
+ * @date: 2025/11/21 23:37
+ */
+public class SJDRTaskConstant extends FormConstant {
+
+    /** 特殊工种性质 */
+    public static final String NCKD_SPEJOBPROP_KEY = "nckd_spejobprop";
+
+    /** 工种截止时间 */
+    public static final String NCKD_WORKENDDATE_KEY = "nckd_workenddate";
+
+    /** 特殊工种年限 */
+    public static final String NCKD_SPECWRKYRS_KEY = "nckd_specwrkyrs";
+
+    /** 从事时间 */
+    public static final String NCKD_SERVICEMON_KEY = "nckd_servicemon";
+
+    /** 累计从事时间 */
+    public static final String NCKD_TOTALMON_KEY = "nckd_totalmon";
+
+    /** 特殊工作记录实体标识 */
+    public static final String NCKD_HSPM_SPECWRKLOG_E_ENTITY = "nckd_hspm_specwrklog_e";
+
+    /** 岗位 */
+    public static final String NCKD_POSITION_KEY = "nckd_position";
+
+    /** 在职 */
+    public static final String ISHIRED_KEY = "ishired";
+
+    /** 雇佣阶段最新记录 */
+    public static final String EMPSTAGELATESTRECORD_KEY = "empstagelatestrecord";
+
+}

+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/mservice/.gitkeep


+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/plugin/form/.gitkeep


+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/plugin/operate/.gitkeep


+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/plugin/other/.gitkeep


+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/plugin/report/.gitkeep


+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/plugin/workflow/.gitkeep


+ 0 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hspm/webapi/.gitkeep