package nckd.jxccl.hr.sdm.plugin.operate;

import com.google.common.collect.Lists;
import kd.bos.coderule.api.CodeRuleInfo;
import kd.bos.coderule.opplugin.util.OrgUtil;
import kd.bos.common.enums.EnableEnum;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.dataentity.entity.DynamicObjectCollection;
import kd.bos.dataentity.entity.LocaleString;
import kd.bos.entity.EntityMetadataCache;
import kd.bos.entity.QueryEntityType;
import kd.bos.entity.operate.result.IOperateInfo;
import kd.bos.entity.operate.result.OperationResult;
import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
import kd.bos.entity.plugin.PreparePropertysEventArgs;
import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
import kd.bos.entity.plugin.args.EndOperationTransactionArgs;
import kd.bos.exception.ErrorCode;
import kd.bos.exception.KDBizException;
import kd.bos.logging.Log;
import kd.bos.logging.LogFactory;
import kd.bos.orm.ORM;
import kd.bos.orm.query.QCP;
import kd.bos.orm.query.QFilter;
import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
import kd.bos.servicehelper.operation.SaveServiceHelper;
import kd.bos.servicehelper.user.UserServiceHelper;
import kd.bos.servicehelper.workflow.MessageCenterServiceHelper;
import kd.bos.url.UrlService;
import kd.bos.util.JSONUtils;
import kd.bos.workflow.engine.msg.info.MessageInfo;
import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
import kd.hr.hbp.business.servicehelper.HRQueryEntityHelper;
import kd.hr.hbp.common.constants.history.HisModelDataStatusEnum;
import kd.hr.hbp.common.util.HRCollUtil;
import kd.hr.hbp.common.util.HRDateTimeUtils;
import kd.hr.hbp.common.util.HRObjectUtils;
import kd.sdk.hr.hrpi.business.helper.HRPIEmployeeServiceHelper;
import nckd.jxccl.base.common.utils.QueryFieldBuilder;
import nckd.jxccl.base.entity.helper.EntityHelper;
import nckd.jxccl.base.org.helper.OrgHelper;
import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
import nckd.jxccl.hr.sdm.business.SdmBusinessService;
import nckd.jxccl.hr.sdm.common.SanDingConstant;
import nckd.jxccl.hr.sdm.common.SanDingPlanEntryStatus;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
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.function.Function;
import java.util.stream.Collectors;

/**
 * 发起任务OP插件
 * @entity: nckd_sandingplan
 * @operat: sendtask
 * @author: jtd
 * @date: 2025/12/3 15:01
 */
public class SendTaskOpPlugin extends AbstractOperationServicePlugIn {
    private static final Log logger = LogFactory.getLog(SendTaskOpPlugin.class);

    /** 特殊借调变动操作编码 */
    private static final String TSJD_CHGACTION_NUMBER = "JTCC_1001";
    /** 跨公司调动 */
    private static final String KGSDD_CHGACTION_NUMBER = "101060_S";
    /** 获取岗位信息查询编码 */
    private static final String NCKD_SDM_GETPOSITIONQUERY = "nckd_sdm_getpositionquery";
    /** orm */
    private static final ORM ORM_IMPL = ORM.create();
    /** key->三定计划ID value->三定任务ID */
    private static final Map<Long, Long> SANDING_TASK_MAP = new HashMap<Long, Long>();

    @Override
    public void onPreparePropertys(PreparePropertysEventArgs e) {
        super.onPreparePropertys(e);

        e.getFieldKeys().addAll(billEntityType.getAllFields().keySet());
    }

    @Override
    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
        super.beginOperationTransaction(e);

        try {
            DynamicObject[] dyos = e.getDataEntities();
            // 构建三定任务
            for (DynamicObject dyo : dyos) {
                DynamicObjectCollection entryDyoColl = dyo.getDynamicObjectCollection(SanDingConstant.NCKD_ENTRYENTITY);
                sendTask(dyo, entryDyoColl);
            }
        } catch (Exception ex) {
            throw new KDBizException(ex, new ErrorCode("sendTaskError", ex.getMessage()));
        }

        try {
            DynamicObject[] dyos = e.getDataEntities();
            // 推送消息
            List<MessageInfo> messageInfos = new ArrayList<MessageInfo>();
            for (DynamicObject dyo : dyos) {
                DynamicObjectCollection entryDyoColl = dyo.getDynamicObjectCollection(SanDingConstant.NCKD_ENTRYENTITY);
                for (DynamicObject entryDyo : entryDyoColl) {
                    buildMessage(messageInfos, entryDyo);
                }
            }
            if (HRCollUtil.isNotEmpty(messageInfos)) {
                try {
                    Map<String, Object> sendMsgResult = MessageCenterServiceHelper.batchSendMessages(messageInfos);
                    boolean success = (Boolean)sendMsgResult.get("success");
                    String description = (String)sendMsgResult.get("description");
                    logger.info(String.format("successToSendMessageSanDingTaskSendTask: messageInfos.size: %s, success: %s, description: %s", messageInfos.size(), success, description));
                    Map<String, Object> data = (Map<String, Object>) sendMsgResult.getOrDefault("data", new HashMap());
                    if (!data.isEmpty()) {
                        for(Long successMsgId : (List<Long>) data.getOrDefault("successMsgIds", new ArrayList())) {
                            logger.info(String.format("successToSendMessageSanDingTaskSendTask-success: %s, messageID: %s", null, successMsgId));
                        }

                        Map<MessageInfo, String> failMsgs = (Map<MessageInfo, String>) data.getOrDefault("failMsgs", new HashMap());

                        for(Map.Entry<MessageInfo, String> failMsg : failMsgs.entrySet()) {
                            MessageInfo failMessageInfo = failMsg.getKey();
                            String failDescription = failMsg.getValue();
                            logger.info(String.format("successToSendMessageSanDingTaskSendTask-fail: %s, messageID: %s, description: %s", JSONUtils.toString(failMessageInfo), failMessageInfo.getId(), failDescription));
                        }
                    }
                } catch (Exception var45) {
                    try {
                        logger.error("FailedSendMessageSanDingTaskSendTask: " + JSONUtils.toString(messageInfos));
                    } catch (IOException var43) {
                        logger.error("Failed to parse message: " + messageInfos);
                    }
                }
            }
        } catch (Exception ex) {
            throw new KDBizException(ex, new ErrorCode("sendMessageError", ex.getMessage()));
        }
    }

    private static void sendTask(DynamicObject billDyo, DynamicObjectCollection entryDyoColl) throws Exception {
        // key 是否在职
        String isHiredKey = String.join(".", SanDingConstant.NCKD_LABORRELSTATUS_KEY, SanDingConstant.IS_HIRED);
        // Key 岗位BOID
        String positionBoIdKey = String.join(".", SanDingConstant.POSITION_KEY, SanDingConstant.BOID_KEY);
        // Key 员工BOID
        String employeeBoIdKey = String.join(".", SanDingConstant.EMPLOYEE_KEY, SanDingConstant.BOID_KEY);
        // Key 单位BOID
        String companyBoIdKey = String.join(".", SanDingConstant.NCKD_COMPANY_KEY, SanDingConstant.BOID_KEY);
        // 获取 所有单位BOID
        Set<Long> companyBoIds = entryDyoColl.stream().map(entryDyo -> entryDyo.getLong(companyBoIdKey)).collect(Collectors.toSet());
        // 获取 所有岗位数据
        QFilter qFilter = new QFilter(String.join(".", SanDingConstant.ADMINORG, SanDingConstant.BELONGCOMPANY_KEY, SanDingConstant.BOID_KEY),  QCP.in, companyBoIds)
                .and(QFilterCommonHelper.getCurrentVersionFilter())
                .and(SanDingConstant.DATA_STATUS, QCP.equals, HisModelDataStatusEnum.EFFECTING.getStatus())
                .and(SanDingConstant.ENABLE, QCP.equals, EnableEnum.YES.getCode())
                .and(SanDingConstant.ISSTANDARDPOS_KEY, QCP.equals, EnableEnum.NO.getCode());
        // 查询 BOID、行政组织、行政组织.所属公司
        String queryFields = QueryFieldBuilder.create()
                .add(SanDingConstant.BOID_KEY)
                .add(SanDingConstant.SOURCEVID_KEY)
                .add(SanDingConstant.HAOS_ADMINORGHRF7_BOID)
                .add(SanDingConstant.HAOS_ADMINORGHRF7_SOURCEVID)
                .add(SanDingConstant.HAOS_ADMINORGHRF7_BELONGCOMPANY_BOID)
                .buildSelect();
        String orderBys = QueryFieldBuilder.create()
                .addAsc(SanDingConstant.ADMINORG, SanDingConstant.LEVEL_KEY)
                .addAsc(SanDingConstant.ADMINORG, SanDingConstant.INDEX_KEY)
                .addDesc(SanDingConstant.ISLEADER_KEY)
                .addAsc(SanDingConstant.NUMBER_KEY)
                .buildOrder();
        DynamicObjectCollection positionDyoColl = HRQueryEntityHelper.getInstance().getQueryDyoColl((QueryEntityType) EntityMetadataCache.getDataEntityType(NCKD_SDM_GETPOSITIONQUERY), queryFields, new QFilter[]{qFilter}, orderBys);
        // 获取 所有岗位BOID
        Set<Long> positionBoIdSet = positionDyoColl.stream().map(positionDyo -> positionDyo.getLong(SanDingConstant.BOID_KEY)).collect(Collectors.toSet());
        // 处理成 key->公司BOID value->List<Map<String, Long>>
        Map<Long, List<Map<String, Long>>> companyPositionMap = positionDyoColl.stream()
            .collect(Collectors.groupingBy(
                positionDyo -> positionDyo.getLong(SanDingConstant.HAOS_ADMINORGHRF7_BELONGCOMPANY_BOID),
                Collectors.mapping(
                    positionDyo -> {
                        Map<String, Long> positionOrgMap = new HashMap<>();
                        positionOrgMap.put("positionBoId", positionDyo.getLong(SanDingConstant.BOID_KEY));
                        positionOrgMap.put("positionSourceVid", positionDyo.getLong(SanDingConstant.BOID_KEY));
                        positionOrgMap.put("adminOrgBoId", positionDyo.getLong(SanDingConstant.HAOS_ADMINORGHRF7_BOID));
                        positionOrgMap.put("adminOrgSourceVid", positionDyo.getLong(SanDingConstant.HAOS_ADMINORGHRF7_BOID));
                        return positionOrgMap;
                    },
                    Collectors.toList()
                )
            ));
        // 获取 所有岗位当前在职人数
        HRBaseServiceHelper empPosOrgRelServiceHelper = HRBaseServiceHelper.create(SanDingConstant.HRPI_EMPPOSORGREL);
        // 获取当前日期
        Date nowDate = HRDateTimeUtils.getNowDate();
        // 过滤 主任职 并且 开始日期<=当前日期 并且 结束日期>=当前日期 并且 用工关系状态.是否在职=1 并且 变动操作.编码<>特殊借调 并且 岗位BOID
        qFilter = new QFilter(SanDingConstant.IS_PRIMARY, QCP.equals, EnableEnum.YES.getCode())
                .and(new QFilter(SanDingConstant.STARTDATE, QCP.less_equals, nowDate))
                .and(new QFilter(SanDingConstant.ENDDATE, QCP.large_equals, nowDate))
                .and(new QFilter(isHiredKey, QCP.equals, EnableEnum.YES.getCode()))
                .and(new QFilter(String.join(".", SanDingConstant.CHGACTION, SanDingConstant.NUMBER_KEY), QCP.not_equals, TSJD_CHGACTION_NUMBER))
                .and(new QFilter(positionBoIdKey, QCP.in, positionBoIdSet));
        // 排序 行政组织.物理层级、行政组织.排序号、岗位.是否负责人岗位
        orderBys = QueryFieldBuilder.create()
                .addAsc(SanDingConstant.ADMINORG, SanDingConstant.LEVEL_KEY)
                .addAsc(SanDingConstant.ADMINORG, SanDingConstant.INDEX_KEY)
                .addDesc(SanDingConstant.POSITION_KEY, SanDingConstant.ISLEADER_KEY)
                .buildOrder();
        DynamicObject[] empPosOrgRelDyos = empPosOrgRelServiceHelper.queryOriginalArray(positionBoIdKey, new QFilter[]{qFilter}, orderBys);
        // 处理成 key->岗位BOID value->岗位当前在职人数
        Map<Long, Integer> positionInStaffEmployeeCountMap = Arrays.stream(empPosOrgRelDyos).map(empPosOrgRelDyo -> empPosOrgRelDyo.getLong(positionBoIdKey)).collect(Collectors.groupingBy(Function.identity(), Collectors.collectingAndThen(Collectors.counting(), Long::intValue)));
        // 获取上年
        String y1 = String.valueOf(HRDateTimeUtils.getYear(HRDateTimeUtils.getNowDate())-1);
        // 获取上上年
        String y2 = String.valueOf(HRDateTimeUtils.getYear(HRDateTimeUtils.getNowDate())-2);
        // 获取上上上年
        String y3 = String.valueOf(HRDateTimeUtils.getYear(HRDateTimeUtils.getNowDate())-3);
        // 获取上上上上年
        String y4 = String.valueOf(HRDateTimeUtils.getYear(HRDateTimeUtils.getNowDate())-4);
        // 过滤 主任职 并且 用工关系状态.是否在职<>1 并且 开始日期>=三年前年初 并且 开始日期<=去年年末 并且 岗位BOID
        qFilter = new QFilter(SanDingConstant.IS_PRIMARY, QCP.equals, EnableEnum.YES.getCode())
                .and(new QFilter(isHiredKey, QCP.not_equals, EnableEnum.YES.getCode()))
                .and(new QFilter(SanDingConstant.STARTDATE, QCP.large_equals, HRDateTimeUtils.parseDate(y3+"-01-01", HRDateTimeUtils.YYYY_MM_DD)))
                .and(new QFilter(SanDingConstant.STARTDATE, QCP.less_equals, HRDateTimeUtils.parseDate(y1+"-12-31", HRDateTimeUtils.YYYY_MM_DD)))
                .and(new QFilter(positionBoIdKey, QCP.in, positionBoIdSet));
        // 查询 岗位ID、开始日期
        String selectFields = QueryFieldBuilder.create().add(positionBoIdKey).add(SanDingConstant.STARTDATE).buildSelect();
        // 获取不在职人数
        empPosOrgRelDyos = empPosOrgRelServiceHelper.queryOriginalArray(selectFields, new QFilter[]{qFilter}, orderBys);
        // 处理成 key->岗位BOID@开始年 value->岗位不在职人数
        Map<String, Integer> yOutSysMap = Arrays.stream(empPosOrgRelDyos).map(empPosOrgRelDyo -> empPosOrgRelDyo.getLong(positionBoIdKey) + "@" + HRDateTimeUtils.getYear(empPosOrgRelDyo.getDate(SanDingConstant.STARTDATE))).collect(Collectors.groupingBy(Function.identity(), Collectors.collectingAndThen(Collectors.counting(), Long::intValue)));
        Map<String, Integer> y1OutSysMap = new HashMap<String, Integer>();
        Map<String, Integer> y2OutSysMap = new HashMap<String, Integer>();
        Map<String, Integer> y3OutSysMap = new HashMap<String, Integer>();
        for (Map.Entry<String, Integer> entry : yOutSysMap.entrySet()) {
            String[] keys = entry.getKey().split("@");
            String year = keys[1];
            if (y1.equals(year)) {
                y1OutSysMap.put(entry.getKey(), entry.getValue());
            }
            if (y2.equals(year)) {
                y2OutSysMap.put(entry.getKey(), entry.getValue());
            }
            if (y3.equals(year)) {
                y3OutSysMap.put(entry.getKey(), entry.getValue());
            }
        }
        // 过滤 主任职 并且 开始日期>=三年前年初 并且 结束日期<=去年年末 并且 变动操作.编码=跨公司调动 并且 岗位BOID
        qFilter = new QFilter(SanDingConstant.IS_PRIMARY, QCP.equals, EnableEnum.YES.getCode())
                .and(new QFilter(SanDingConstant.STARTDATE, QCP.large_equals, HRDateTimeUtils.parseDate(y3+"-01-01", HRDateTimeUtils.YYYY_MM_DD)))
                .and(new QFilter(SanDingConstant.ENDDATE, QCP.less_equals, HRDateTimeUtils.parseDate(y1+"-12-31", HRDateTimeUtils.YYYY_MM_DD)))
                .and(new QFilter(String.join(".", SanDingConstant.CHGACTION, SanDingConstant.NUMBER_KEY), QCP.equals, KGSDD_CHGACTION_NUMBER))
                .and(new QFilter(positionBoIdKey, QCP.in, positionBoIdSet));
        empPosOrgRelDyos = empPosOrgRelServiceHelper.queryOriginalArray(String.join(".", SanDingConstant.EMPLOYEE_KEY, SanDingConstant.BOID_KEY), new QFilter[]{qFilter}, orderBys);
        // 获取 员工BOID
        Set<Long> employeeBoIdSet = new HashSet<Long>();
        // 处理成 key->员工BOID value->List<跨公司调动任职经历id>
        Map<Long, List<Long>> employeeBoIdMap = new HashMap<Long, List<Long>>();
        for (DynamicObject empPosOrgRelDyo : empPosOrgRelDyos) {
            long employeeBoId = empPosOrgRelDyo.getLong(employeeBoIdKey);
            if (employeeBoIdMap.containsKey(employeeBoId)) {
                List<Long> empPosOrgRelIdList = employeeBoIdMap.get(employeeBoId);
                empPosOrgRelIdList.add(empPosOrgRelDyo.getLong(SanDingConstant.BOID_KEY));
            } else {
                List<Long> empPosOrgRelIdList = new ArrayList<Long>();
                empPosOrgRelIdList.add(empPosOrgRelDyo.getLong(SanDingConstant.BOID_KEY));
                employeeBoIdMap.put(employeeBoId, empPosOrgRelIdList);
            }
            employeeBoIdSet.add(employeeBoId);
        }
        // 过滤 主任职 并且 (结束日期>=四年前年末 并且 结束日期 <= 去年年末 或者 开始日期>=三年前年末 并且 结束日期<=去年年末) 并且 员工BOID
        qFilter = new QFilter(SanDingConstant.IS_PRIMARY, QCP.equals, EnableEnum.YES.getCode())
                .and(
                        new QFilter(SanDingConstant.ENDDATE, QCP.large_equals, HRDateTimeUtils.parseDate(y4+"-12-31", HRDateTimeUtils.YYYY_MM_DD))
                        .and(SanDingConstant.ENDDATE, QCP.less_equals, HRDateTimeUtils.parseDate(y1+"-12-31", HRDateTimeUtils.YYYY_MM_DD))
                        .or(SanDingConstant.STARTDATE, QCP.large_equals, HRDateTimeUtils.parseDate(y3+"-12-31", HRDateTimeUtils.YYYY_MM_DD))
                        .and(SanDingConstant.ENDDATE, QCP.less_equals, HRDateTimeUtils.parseDate(y1+"-12-31", HRDateTimeUtils.YYYY_MM_DD))
                )
                .and(new QFilter(SanDingConstant.STARTDATE, QCP.large_equals, HRDateTimeUtils.parseDate(y3+"-01-01", HRDateTimeUtils.YYYY_MM_DD)))
                .and(new QFilter(SanDingConstant.ENDDATE, QCP.less_equals, HRDateTimeUtils.parseDate(y1+"-12-31", HRDateTimeUtils.YYYY_MM_DD)))
                .and(new QFilter(SanDingConstant.EMPLOYEE_KEY, QCP.in, employeeBoIdSet));
        selectFields = QueryFieldBuilder.create().add(SanDingConstant.EMPLOYEE_KEY,  SanDingConstant.BOID_KEY).add(SanDingConstant.ID_KEY).add(SanDingConstant.POSITION_KEY, SanDingConstant.BOID_KEY).add(SanDingConstant.ENDDATE).buildSelect();
        // 获取 员工的所有主任职记录
        orderBys = QueryFieldBuilder.create().addAsc(SanDingConstant.EMPLOYEE_KEY, SanDingConstant.EMP_NUMBER_KEY).addAsc(SanDingConstant.STARTDATE).buildOrder();
        empPosOrgRelDyos = empPosOrgRelServiceHelper.queryOriginalArray(selectFields, new QFilter[]{qFilter}, orderBys);
        // 处理成 key->员工BOID value->List<员工所有主任职记录>
        Map<Long, List<DynamicObject>> empPosOrgRelDyoListMap = Arrays.stream(empPosOrgRelDyos).collect(Collectors.groupingBy(empPosOrgRelDyo -> empPosOrgRelDyo.getLong(SanDingConstant.EMPLOYEE_KEY)));
        // 处理成 key->岗位BOID@年 value->List<跨公司调动任职经历id>
        Map<String, List<Long>> kgsddEmpPosOrgRelIdListMap = new HashMap<String, List<Long>>();
        // 对于跨公司调动来说，这笔任职属于流入，需要根据人定位到这条任职经历的上一条主任职记录，才是流出的任职记录，流出任职经历的结束时间所在的年才是流出年
        for (Map.Entry<Long, List<Long>> entry : employeeBoIdMap.entrySet()) {
            Long employeeBoId = entry.getKey();
            List<Long> empPosOrgRelIdList = entry.getValue();
            List<DynamicObject> empPosOrgRelDyoList = empPosOrgRelDyoListMap.get(employeeBoId);
            for (Long empPosOrgRelId : empPosOrgRelIdList) {
                for (int i = 0; i < empPosOrgRelDyoList.size(); i++) {
                    DynamicObject empPosOrgRelDyo = empPosOrgRelDyoList.get(i);
                    if (empPosOrgRelId == empPosOrgRelDyo.getLong(SanDingConstant.ID_KEY)) {
                        DynamicObject lastEmpPosOrgRelDyo = empPosOrgRelDyoList.get(i - 1);
                        Long positionBoId = lastEmpPosOrgRelDyo.getLong(positionBoIdKey);
                        String year = String.valueOf(HRDateTimeUtils.getYear(lastEmpPosOrgRelDyo.getDate(SanDingConstant.ENDDATE)));
                        String key = positionBoId + "@" + year;
                        List<Long> kgsddEmpPosOrgRelIdList = kgsddEmpPosOrgRelIdListMap.get(key);
                        if (HRObjectUtils.isEmpty(kgsddEmpPosOrgRelIdList)) {
                            kgsddEmpPosOrgRelIdList = new ArrayList<Long>();
                            kgsddEmpPosOrgRelIdList.add(positionBoId);
                            kgsddEmpPosOrgRelIdListMap.put(key, kgsddEmpPosOrgRelIdList);
                        } else {
                            kgsddEmpPosOrgRelIdList.add(positionBoId);
                        }
                        break;
                    }
                }
            }
        }
        // 处理成 key->岗位BOID@年 value->数量
        Map<String, Integer> kgsddEmpPosOrgRelMap = kgsddEmpPosOrgRelIdListMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().size()));
        // key->单位BOID@岗位BOID value->定员数
        Map<String, Integer> lastSanDingTaskMap = new HashMap<String, Integer>();
        DynamicObject lastSanDingPlanDyo = SdmBusinessService.getLastSanDingPlanDyo(billDyo.getLong(SanDingConstant.ID_KEY));
        if (!HRObjectUtils.isEmpty(lastSanDingPlanDyo)) {
            long sanDingPlanId = lastSanDingPlanDyo.getLong(SanDingConstant.ID_KEY);
            // 查询 单据体.定员数、单据体.岗位.BOID、单位.BOID
            selectFields = QueryFieldBuilder.create()
                    .add(SanDingConstant.NCKD_ENTRYENTITY, SanDingConstant.NCKD_AUTHORIZEDSTRENGTH_KEY)
                    .add(SanDingConstant.NCKD_ENTRYENTITY, SanDingConstant.NCKD_POSITION_KEY, SanDingConstant.BOID_KEY)
                    .add(companyBoIdKey)
                    .buildSelect();
            // 获取三定任务ID
            DynamicObject[] lastSanDingTaskDyos = HRBaseServiceHelper.create(SanDingConstant.NCKD_SANDINGTASK_ENTITY).queryOriginalArray(selectFields, new QFilter[]{new QFilter(String.join(".", SanDingConstant.NCKD_SANDINGPLAN_KEY, SanDingConstant.ID_KEY), QCP.equals, sanDingPlanId)});
            for (DynamicObject lastSanDingTaskDyo : lastSanDingTaskDyos) {
                Long positionBoId = lastSanDingTaskDyo.getLong(String.join(".", SanDingConstant.NCKD_ENTRYENTITY, SanDingConstant.NCKD_POSITION_KEY, SanDingConstant.BOID_KEY));
                int authorizedStrength = lastSanDingTaskDyo.getInt(String.join(".", SanDingConstant.NCKD_ENTRYENTITY, SanDingConstant.NCKD_AUTHORIZEDSTRENGTH_KEY));
                Long companyBoId = lastSanDingTaskDyo.getLong(companyBoIdKey);
                lastSanDingTaskMap.put(companyBoId+"@"+positionBoId, authorizedStrength);
            }
        }
        // 构建三定任务
        HRBaseServiceHelper sanDingTaskServiceHelper = HRBaseServiceHelper.create(SanDingConstant.NCKD_SANDINGTASK_ENTITY);
        List<DynamicObject> sanDingTaskDyoList = new ArrayList<DynamicObject>();
        for (DynamicObject entryDyo : entryDyoColl) {
            // 设置 状态为未提交
            entryDyo.set(SanDingConstant.NCKD_STATUS_KEY, SanDingPlanEntryStatus.UNSUBMIT.getCode());
            // 构建 三定任务
            DynamicObject sanDingTaskDyo = sanDingTaskServiceHelper.generateEmptyDynamicObject();
            // 设置 ID
            sanDingTaskDyo.set(SanDingConstant.ID_KEY, ORM_IMPL.genLongId(sanDingTaskDyo.getDataEntityType()));
            // 设置 创建人
            sanDingTaskDyo.set(SanDingConstant.CREATOR_KEY, UserServiceHelper.getCurrentUserId());
            // 设置 主业务组织
            sanDingTaskDyo.set(SanDingConstant.ORG_KEY, OrgHelper.getCreateOrg(SanDingConstant.NCKD_SANDINGTASK_ENTITY));
            // 设置 单据状态
            sanDingTaskDyo.set(SanDingConstant.BILL_STATUS_KEY, "A");// 默认暂存
            // 设置 审批状态
            sanDingTaskDyo.set(SanDingConstant.AUDIT_STATUS, "A");// 默认暂存
            // 设置 单据编码
            CodeRuleInfo codeRuleInfo = CodeRuleServiceHelper.getCodeRule(SanDingConstant.NCKD_SANDINGTASK_ENTITY, sanDingTaskDyo, OrgUtil.getMainOrgId(sanDingTaskDyo));
            if (codeRuleInfo == null) {
                throw new KDBizException("三定任务没有可使用的编码规则");
            }
            String number = CodeRuleServiceHelper.readNumber(codeRuleInfo, sanDingTaskDyo);
            sanDingTaskDyo.set(SanDingConstant.BILL_NO_KEY,  number);
            // 设置 三定计划
            sanDingTaskDyo.set(SanDingConstant.NCKD_SANDINGPLAN_KEY, billDyo);
            // 设置 三定计划分录
            sanDingTaskDyo.set(SanDingConstant.NCKD_SDP_ENTRY_KEY, entryDyo);
            // 设置 单位
            sanDingTaskDyo.set(SanDingConstant.NCKD_COMPANY_KEY, entryDyo.getLong(String.join(".", SanDingConstant.NCKD_COMPANY_KEY, SanDingConstant.ID_KEY)));
            // 构建单据体
            DynamicObjectCollection sanDingTaskEntryDyoColl = sanDingTaskDyo.getDynamicObjectCollection(SanDingConstant.NCKD_ENTRYENTITY);
            List<Map<String, Long>> positionBoIdList = companyPositionMap.get(entryDyo.getLong(companyBoIdKey));
            if (HRCollUtil.isNotEmpty(positionBoIdList)) {
                for (Map<String, Long> positionOrgMap : positionBoIdList) {
                    DynamicObject sanDingTaskEntryDyo = sanDingTaskEntryDyoColl.addNew();
                    // 岗位BOID
                    Long positionBoId = positionOrgMap.get("positionBoId");
                    // 设置 组织
                    sanDingTaskEntryDyo.set(SanDingConstant.NCKD_ADMINORG, EntityHelper.newEntity(SanDingConstant.ADMINORG_ENTITYID, positionOrgMap.get("adminOrgSourceVid")));
                    // 设置 岗位
                    sanDingTaskEntryDyo.set(SanDingConstant.NCKD_POSITION_KEY, EntityHelper.newEntity(SanDingConstant.HBPM_POSITIONHR, positionOrgMap.get("positionSourceVid")));
                    // 设置 原定员数
                    sanDingTaskEntryDyo.set(SanDingConstant.NCKD_ORI_ASR_KEY, lastSanDingTaskMap.getOrDefault(entryDyo.getLong(companyBoIdKey)+"@"+positionBoId, 0));
                    // 设置 定员数
                    sanDingTaskEntryDyo.set(SanDingConstant.NCKD_AUTHORIZEDSTRENGTH_KEY, lastSanDingTaskMap.getOrDefault(entryDyo.getLong(companyBoIdKey)+"@"+positionBoId, 0));
                    // 设置 实际占编人数
                    sanDingTaskEntryDyo.set(SanDingConstant.NCKD_ACTUALSTAFFCOUNT_KEY, positionInStaffEmployeeCountMap.getOrDefault(positionBoId, 0));
                    // 设置 缺编人数
                    sanDingTaskEntryDyo.set(SanDingConstant.NCKD_STAFFINGSHORTFALL_KEY, 0);
                    // 设置 前一年流失人数（系统数）
                    sanDingTaskEntryDyo.set(SanDingConstant.NCKD_Y1OUT_SYS_KEY, y1OutSysMap.getOrDefault(positionBoId + "@" + y1, 0) + kgsddEmpPosOrgRelMap.getOrDefault(positionBoId + "@" + y1, 0));
                    // 设置 前两年流失人数（系统数）
                    sanDingTaskEntryDyo.set(SanDingConstant.NCKD_Y2OUT_SYS_KEY, y2OutSysMap.getOrDefault(positionBoId + "@" + y2, 0) + kgsddEmpPosOrgRelMap.getOrDefault(positionBoId + "@" + y2, 0));
                    // 设置 前三年流失人数（系统数）
                    sanDingTaskEntryDyo.set(SanDingConstant.NCKD_Y3OUT_SYS_KEY, y3OutSysMap.getOrDefault(positionBoId + "@" + y3, 0) + kgsddEmpPosOrgRelMap.getOrDefault(positionBoId + "@" + y3, 0));
                    // 设置 前一年流失人数（确认数）
                    sanDingTaskEntryDyo.set(SanDingConstant.NCKD_Y1OUT_CONF_KEY, sanDingTaskEntryDyo.get(SanDingConstant.NCKD_Y1OUT_SYS_KEY));
                    // 设置 前两年流失人数（确认数）
                    sanDingTaskEntryDyo.set(SanDingConstant.NCKD_Y2OUT_CONF_KEY, sanDingTaskEntryDyo.get(SanDingConstant.NCKD_Y2OUT_SYS_KEY));
                    // 设置 前三年流失人数（确认数）
                    sanDingTaskEntryDyo.set(SanDingConstant.NCKD_Y3OUT_CONF_KEY, sanDingTaskEntryDyo.get(SanDingConstant.NCKD_Y3OUT_SYS_KEY));
                }
            }
            sanDingTaskDyoList.add(sanDingTaskDyo);
            SANDING_TASK_MAP.put(entryDyo.getLong(SanDingConstant.ID_KEY), sanDingTaskDyo.getLong(SanDingConstant.ID_KEY));
        }
        // 保存
        OperationResult saveResult = SaveServiceHelper.saveOperate(SanDingConstant.NCKD_SANDINGTASK_ENTITY, sanDingTaskDyoList.toArray(new DynamicObject[0]));
        if (saveResult != null && !saveResult.isSuccess()) {
            List<String> errorMsgList = Lists.newArrayList();
            for(IOperateInfo operateInfo : saveResult.getAllErrorOrValidateInfo()) {
                errorMsgList.add(operateInfo.getMessage());
            }

            if (!errorMsgList.isEmpty()) {
                throw new KDBizException(String.join(System.lineSeparator(), errorMsgList));
            }
        }
    }

    @Override
    public void endOperationTransaction(EndOperationTransactionArgs e) {
        super.endOperationTransaction(e);

        DynamicObject[] dyos = e.getDataEntities();
        for (DynamicObject dyo : dyos) {
            // 设置为已发送
            dyo.set(SanDingConstant.NCKD_ISSEND_KEY, true);
            // 更新最近一笔三定计划的结束时间
            updateLastSanDingPlanEndDate(dyo);
        }
        SaveServiceHelper.save(dyos);
    }

    private void buildMessage(List<MessageInfo> messageInfos, DynamicObject entryDyo) {
        // 构建 消息
        MessageInfo messageInfo = new MessageInfo();
        // 设置 消息标题
        messageInfo.setMessageTitle(new LocaleString("三定任务填报提醒"));
        // 设置 文本消息内容
        messageInfo.setMessageContent(new LocaleString("您有一个三定任务待填报，请尽快处理，点击下方【快速处理】链接处理。"));
        // 设置 消息类型
        messageInfo.setType(MessageInfo.TYPE_MESSAGE);
        // 设置 实体编码
        messageInfo.setEntityNumber(SanDingConstant.NCKD_SANDINGTASK_ENTITY);
        // 设置 业务单据ID
        messageInfo.setBizDataId(SANDING_TASK_MAP.get(entryDyo.getLong(SanDingConstant.ID_KEY)));
        // 设置 标签
        messageInfo.setTag("三定管理");
        long managerBoId = entryDyo.getDynamicObject(SanDingConstant.NCKD_MANAGER_KEY).getLong(SanDingConstant.BOID_KEY);
        // 设置 消息接收人
        List<Long> userIds = getUserIds(Collections.singletonList(managerBoId));
        messageInfo.setUserIds(userIds);
        // 设置 消息Web端url
        messageInfo.setContentUrl(UrlService.getDomainContextUrl() + "/index.html?formId="+SanDingConstant.NCKD_SANDINGTASK_ENTITY+"&pkId=" + SANDING_TASK_MAP.get(entryDyo.getLong(SanDingConstant.ID_KEY)));
        // 设置发送人
        messageInfo.setSenderId(UserServiceHelper.getCurrentUserId());
        messageInfos.add(messageInfo);
    }

    private List<Long> getUserIds(List<Long> managerBoIdList) {
        // 获取 所有负责人userId
        Map<String, Object> result = HRPIEmployeeServiceHelper.queryUserIdsByEmployeeIds(managerBoIdList, null);
        if ((Boolean) result.get("success")) {
            Map<Long, Map<String, Object>> retMap = (Map<Long, Map<String, Object>>) result.get("data");
            // 从返回结果中提取用户ID列表
            List<Long> userIds = retMap.values().stream().map(reMap -> (Long) reMap.get("user")).collect(Collectors.toList());
            return userIds;
        } else {
            throw new KDBizException((String) result.get("message"));
        }
    }

    private void updateLastSanDingPlanEndDate(DynamicObject dyo) {
        Date startDate = dyo.getDate(SanDingConstant.NCKD_STARTDATE_KEY);
        DynamicObject lastSanDingPlanDyo = SdmBusinessService.getLastSanDingPlanDyo(dyo.getLong(SanDingConstant.ID_KEY));
        if (!HRObjectUtils.isEmpty(lastSanDingPlanDyo)) {
            lastSanDingPlanDyo.set(SanDingConstant.NCKD_ENDDATE, HRDateTimeUtils.addDay(startDate, -1));
            SaveServiceHelper.save(new DynamicObject[]{lastSanDingPlanDyo});
        }
    }

}
