package com.cpop.oam.business.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.cpop.api.tencent.wxWork.handler.WebHookSendHandler;
import com.cpop.common.utils.DateUtils;
import com.cpop.common.utils.StringUtils;
import com.cpop.common.utils.bean.BeanUtils;
import com.cpop.core.base.entity.PageDomain;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.base.exception.UtilException;
import com.cpop.core.service.RedisService;
import com.cpop.core.utils.QuartzUtils;
import com.cpop.core.utils.SecurityUtils;
import com.cpop.core.utils.SpringUtils;
import com.cpop.core.utils.sql.SqlUtils;
import com.cpop.oam.business.bo.*;
import com.cpop.oam.business.dto.TaskWorkOrderReceiveDealPauseDto;
import com.cpop.oam.business.entity.*;
import com.cpop.oam.business.mapper.TaskWorkOrderMapper;
import com.cpop.oam.business.service.*;
import com.cpop.oam.business.vo.*;
import com.cpop.oam.framework.constant.WebHookKeyConstant;
import com.cpop.oam.framework.enums.OamConfigEnum;
import com.cpop.oam.framework.enums.QuartzEnums;
import com.cpop.oam.framework.tasks.*;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.sql.Date;
import java.text.ParseException;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

import static com.cpop.core.base.table.table.SysUserTableDef.SYS_USER;
import static com.cpop.oam.business.entity.table.DutyTableDef.DUTY;
import static com.cpop.oam.business.entity.table.StaffTableDef.STAFF;
import static com.cpop.oam.business.entity.table.TaskStaffGroupTableDef.TASK_STAFF_GROUP;
import static com.cpop.oam.business.entity.table.TaskTableDef.TASK;
import static com.cpop.oam.business.entity.table.TaskWorkOrderRecordTableDef.TASK_WORK_ORDER_RECORD;
import static com.cpop.oam.business.entity.table.TaskWorkOrderTableDef.TASK_WORK_ORDER;
import static com.cpop.system.business.entity.table.BrandTableDef.BRAND;
import static com.cpop.system.business.entity.table.StoreTableDef.STORE;
import static com.mybatisflex.core.query.QueryMethods.dateFormat;

/**
 * 任务-工单表 服务层实现。
 *
 * @author DB
 * @since 2023-09-18
 */
@Slf4j
@Service("taskWorkOrderService")
public class TaskWorkOrderServiceImpl extends ServiceImpl<TaskWorkOrderMapper, TaskWorkOrder> implements TaskWorkOrderService {

    @Autowired
    private Scheduler scheduler;

    /**
     * 工单模块-工单提交-接收/处理/暂停中
     *
     * @return com.cpop.core.base.R<com.mybatisflex.core.paginate.Page < com.cpop.oam.business.vo.TaskWorkOrderPageVo>>
     * @author DB
     * @since 2023/09/18 17:18
     */
    @Override
    public TaskWorkOrderReceiveDealPauseVo receiveDealPause() {
        // 获取工单
        List<TaskWorkOrderReceiveDealPauseDto> dtoList = this.mapper.selectListByQueryAs(QueryWrapper.create()
                        // 工单
                        .select(TASK_WORK_ORDER.ID,
                                TASK_WORK_ORDER.PHONE_NUMBER,
                                TASK_WORK_ORDER.CREATE_TIME.as("createTime"),
                                TASK_WORK_ORDER.RECEIVING_TIME,
                                TASK_WORK_ORDER.PAUSE_STAFF_ID,
                                TASK_WORK_ORDER.PAUSE_RECORD_TIME,
                                TASK_WORK_ORDER.PAUSE_EXPIRE_TIME)
                        // 任务
                        .select(TASK.TASK_CONTENT, TASK.TASK_STATUS, TASK.RESPONSIBLE_STAFF_ID, TASK.TASK_RECEIPT_TIME, TASK.RECORD_STAFF_ID)
                        // 品牌
                        .select(BRAND.BRAND_NAME)
                        // 校区
                        .select(STORE.STORE_NAME)
                        .from(TASK_WORK_ORDER)
                        .leftJoin(TASK)
                        .on(TASK.ID.eq(TASK_WORK_ORDER.TASK_ID))
                        .leftJoin(BRAND)
                        .on(BRAND.ID.eq(TASK_WORK_ORDER.BRAND_ID))
                        .leftJoin(STORE)
                        .on(STORE.ID.eq(TASK_WORK_ORDER.STORE_ID))
                        // 工单
                        .where(TASK.TASK_TYPE.eq(2))
                        .and(TASK.TASK_STATUS.in(1, 2, 4, 5, 6, 7)),
                TaskWorkOrderReceiveDealPauseDto.class,
                // 提交人
                item -> item.field(TaskWorkOrderReceiveDealPauseDto::getRecordStaffName)
                        .queryWrapper(recordStaff -> queryChain().select(STAFF.NAME.as("recordStaffName"))
                                .from(STAFF)
                                .where(STAFF.ID.eq(recordStaff.getRecordStaffId()))),
                // 负责人
                item -> item.field(TaskWorkOrderReceiveDealPauseDto::getResponsibleStaffName)
                        .queryWrapper(responsibleStaff -> queryChain().select(STAFF.NAME.as("responsibleStaffName"))
                                .from(STAFF)
                                .where(STAFF.ID.eq(StringUtils.isNotBlank(responsibleStaff.getResponsibleStaffId())
                                        ? responsibleStaff.getResponsibleStaffId()
                                        : "-1"))),
                // 暂停人
                item -> item.field(TaskWorkOrderReceiveDealPauseDto::getPauseStaffName)
                        .queryWrapper(pauseStaff -> queryChain().select(STAFF.NAME.as("pauseStaffName"))
                                .from(STAFF)
                                .where(STAFF.ID.eq(
                                        StringUtils.isNotBlank(pauseStaff.getPauseStaffId()) ? pauseStaff.getPauseStaffId() : "-1"))));
        // 分组过滤
        Map<Integer, List<TaskWorkOrderReceiveDealPauseDto>> taskStatusMap =
                dtoList.stream().collect(Collectors.groupingBy(TaskWorkOrderReceiveDealPauseDto::getTaskStatus));
        TaskWorkOrderReceiveDealPauseVo taskWorkOrderReceiveDealPauseVo = new TaskWorkOrderReceiveDealPauseVo();
        taskStatusMap.forEach((key, value) -> {
            switch (key) {
                // 待接收
                case 1:
                    // 接收逾期
                case 7:
                    List<TaskWorkOrderReceiveDealPauseVo.Receive> receiveList =
                            BeanUtils.mapToList(taskStatusMap.get(key), TaskWorkOrderReceiveDealPauseVo.Receive.class);
                    if (taskWorkOrderReceiveDealPauseVo.getReceiveList() == null) {
                        taskWorkOrderReceiveDealPauseVo.setReceiveList(receiveList);
                    } else {
                        taskWorkOrderReceiveDealPauseVo.getReceiveList().addAll(receiveList);
                    }
                    break;
                case 6:
                // 进行中
                case 2:
                    // 逾期
                case 4:
                    List<TaskWorkOrderReceiveDealPauseVo.Deal> dealList =
                            BeanUtils.mapToList(taskStatusMap.get(key), TaskWorkOrderReceiveDealPauseVo.Deal.class);
                    if (taskWorkOrderReceiveDealPauseVo.getDealList() == null) {
                        taskWorkOrderReceiveDealPauseVo.setDealList(dealList);
                    } else {
                        taskWorkOrderReceiveDealPauseVo.getDealList().addAll(dealList);
                    }
                    break;
                default:
                    // 暂停中
                    List<TaskWorkOrderReceiveDealPauseVo.Pause> pauseList =
                            BeanUtils.mapToList(taskStatusMap.get(key), TaskWorkOrderReceiveDealPauseVo.Pause.class);
                    taskWorkOrderReceiveDealPauseVo.setPauseList(pauseList);
            }
        });
        return taskWorkOrderReceiveDealPauseVo;
    }

    /**
     * 工单模块-工单提交-获取当天值班人员
     *
     * @author DB
     * @since 2023-11-29 14:01:03
     */
    @Override
    public WorkOrderDutyVo getWorkOrderDutyStaff() {
        return SpringUtils.getBean(DutyService.class)
                .getMapper()
                .selectOneWithRelationsByQueryAs(
                        QueryWrapper.create()
                                .select(DUTY.SERVICE_STAFF_ID, DUTY.TECHNOLOGY_STAFF_ID, DUTY.DUTY_DATE)
                                .from(DUTY)
                                .where(DUTY.DUTY_DATE.eq(DateUtils.getDate())),
                        WorkOrderDutyVo.class);
    }

    /**
     * 工单模块-工单提交-已办结分页列表
     *
     * @author DB
     * @since 2023-11-29 14:35:08
     */
    @Override
    public Page<TaskWorkOrderPageVo> finishWorkOrderPage() {
        PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
        return this.mapper.paginateAs(Page.of(pageDomain.getPageNum(), pageDomain.getPageSize()),
                QueryWrapper.create()
                        .select(TASK_WORK_ORDER.ID, TASK_WORK_ORDER.FINISH_STAFF_ID, TASK_WORK_ORDER.FINISH_TIME)
                        .select(TASK.TASK_CONTENT)
                        .from(TASK_WORK_ORDER)
                        .leftJoin(TASK)
                        .on(TASK.ID.eq(TASK_WORK_ORDER.TASK_ID))
                        // 工单
                        .where(TASK.TASK_TYPE.eq(2))
                        .and(TASK.TASK_STATUS.eq(9))
                        .orderBy(TASK_WORK_ORDER.FINISH_TIME.desc()),
                TaskWorkOrderPageVo.class,
                item -> item.field(TaskWorkOrderPageVo::getFinishStaffName)
                        .queryWrapper(finishStaff -> queryChain().select(STAFF.NAME.as("finishStaffName"))
                                .from(STAFF)
                                .where(STAFF.ID.eq(finishStaff.getFinishStaffId()))));
    }

    /**
     * 工单模块-工单提交-新增工单
     *
     * @param bo 请求参数
     * @author DB
     * @since 2023/5/30 16:53
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertWorkOrder(TaskWorkOrderBo bo) {
        //获取当前时间
        int hour = LocalDateTime.now().getHour();
        if (hour < 9 || hour > 21){
            throw new ServiceException("工单提交时间为:9:00-21:00,请明天上午九点后再提交");
        }
        // 获取值班员工
        DutyService dutyService = SpringUtils.getBean(DutyService.class);
        Duty duty = dutyService.getOne(QueryWrapper.create().where(DUTY.DUTY_DATE.eq(DateUtils.getDate())));
        if (duty == null) {
            throw new ServiceException("当天没有值班员工,请联系相关人员添加值班信息!");
        }
        // 添加任务
        Task task = new Task();
        BeanUtils.copyBeanProp(task, bo);
        // 读取员工信息
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        // 默认待接受
        task.setTaskStatus(1)
                // 默认开发中
                .setTaskItem(0)
                .setTaskWeight(0)
                // 工单不参与评级
                .setTaskRating("N")
                .setRecordStaffId(loginUserInfo.getString("id"))
                .setResponsibleStaffId(duty.getTechnologyStaffId());
        TaskService taskService = SpringUtils.getBean(TaskService.class);
        taskService.save(task);
        // 添加工单
        TaskWorkOrder entity = new TaskWorkOrder();
        BeanUtils.copyBeanProp(entity, bo);
        entity.setTaskId(task.getId());
        this.save(entity);
        // 添加一个0绩点
        TaskStaffGroupService taskStaffGroupService = SpringUtils.getBean(TaskStaffGroupService.class);
        TaskStaffGroup taskStaffGroup = new TaskStaffGroup();
        taskStaffGroup.setTaskId(task.getId()).setStaffId(duty.getTechnologyStaffId()).setGradePoint(0);
        taskStaffGroupService.save(taskStaffGroup);
        // 添加工单记录
        TaskWorkOrderRecord workOrderRecord = new TaskWorkOrderRecord();
        workOrderRecord.setTaskWorkOrderId(entity.getId())
                .setRecordText(bo.getTaskContent())
                .setRecordStaffId(loginUserInfo.getString("id"))
                .setAttachmentUrl(bo.getAttachmentUrl());
        SpringUtils.getBean(TaskWorkOrderRecordService.class).save(workOrderRecord);
        // 通知值班员工
        StaffService staffService = SpringUtils.getBean(StaffService.class);
        StaffInfoVo technologyStaffInfo = staffService.getStaffInfo(duty.getTechnologyStaffId());
        if (technologyStaffInfo != null) {
            // 企微通知值班人员
            List<String> phoneList = new ArrayList<String>();
            phoneList.add(loginUserInfo.getString("phoneNumber"));
            phoneList.add(technologyStaffInfo.getPhoneNumber());
            try {
                SpringUtils.getBean(WebHookSendHandler.class)
                        .webHookSendText(WebHookKeyConstant.ORDER_INFO_BOT,
                                phoneList,
                                "==========您有一条新的工单需要处理==========\n" + task.getTaskContent(),
                                false);
            } catch (IOException e) {
                throw new ServiceException("发送消息通知失败!");
            }
        }
        // 添加未接收任务
        LocalDateTime now = LocalDateTime.now();
        startOrRemoveWorkOrderAcceptTask(entity.getId(), true, now);
        // 添加未接收工单超时提醒任务
        startOrRemoveWorkOrderAcceptOverTimeRemindTask(entity.getId(), true, now);
    }

    /**
     * 工单接受超时提醒任务
     * @author DB
     * @since 2024/2/4
     * @param workOrderId 工单id
     * @param isStart 是否是开始标识
     * @param dateTime 时间
     */
    private void startOrRemoveWorkOrderAcceptOverTimeRemindTask(String workOrderId, Boolean isStart, LocalDateTime dateTime) {
        // 基于表达式构建触发器
        QuartzUtils quartzUtils = SpringUtils.getBean(QuartzUtils.class);
        try {
            QuartzEnums acceptEnums = QuartzEnums.WORK_ORDER_ACCEPT_OVERTIME_REMIND_TASK;
            // 开始
            if (isStart) {
                // 通过JobBuilder构建JobDetail实例，JobDetail规定其job只能是实现Job接口的实例
                JobDetail jobDetail = JobBuilder.newJob(WorkOrderAcceptOverTimeRemindTask.class)
                        .withIdentity(acceptEnums.getName() + workOrderId, acceptEnums.getGroup())
                        .usingJobData("WorkOrderId", workOrderId)
                        .build();
                LocalDateTime localDateTime = dateTime.plusMinutes(25);
                ZoneId zoneId = ZoneId.systemDefault();
                String cron = quartzUtils.convertToCron(Date.from(localDateTime.atZone(zoneId).toInstant()));
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
                // CronTrigger表达式触发器 继承于Trigger。TriggerBuilder 用于构建触发器实例
                CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                        .withIdentity(acceptEnums.getName() + workOrderId, acceptEnums.getGroup())
                        .withSchedule(cronScheduleBuilder)
                        .build();
                scheduler.scheduleJob(jobDetail, cronTrigger);
            } else {
                // 删除
                quartzUtils.deleteJob(acceptEnums.getName() + workOrderId, acceptEnums.getGroup());
            }
        } catch (SchedulerException | ParseException e) {
            throw new ServiceException("生成工单接收任务失败!");
        }
    }

    /**
     * 工单完结超时提醒
     * @author DB
     * @since 2024/2/4
     * @param workOrderId 工单id
     * @param status 命令状态 0：新增；1：更新；2：删除
     * @param dateTime 时间
     */
    private void workOrderFinishOverTimeRemindTask(String workOrderId, Integer status, LocalDateTime dateTime) {
        // 基于表达式构建触发器
        QuartzUtils quartzUtils = SpringUtils.getBean(QuartzUtils.class);
        try {
            QuartzEnums acceptEnums = QuartzEnums.WORK_ORDER_FINISH_OVERTIME_REMIND_TASK;
            // 开始
            if (status == 0) {
                LocalDateTime localDateTime = dateTime.plusMinutes(150);
                ZoneId zoneId = ZoneId.systemDefault();
                String cron = quartzUtils.convertToCron(Date.from(localDateTime.atZone(zoneId).toInstant()));
                // 通过JobBuilder构建JobDetail实例，JobDetail规定其job只能是实现Job接口的实例
                JobDetail jobDetail = JobBuilder.newJob(WorkOrderFinishOverTimeRemindTask.class)
                        .withIdentity(acceptEnums.getName() + workOrderId, acceptEnums.getGroup())
                        .usingJobData("WorkOrderId", workOrderId)
                        .build();
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
                // CronTrigger表达式触发器 继承于Trigger。TriggerBuilder 用于构建触发器实例
                CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                        .withIdentity(acceptEnums.getName() + workOrderId, acceptEnums.getGroup())
                        .withSchedule(cronScheduleBuilder)
                        .build();
                scheduler.scheduleJob(jobDetail, cronTrigger);
            } else if (status == 1) {
                LocalDateTime localDateTime = dateTime.plusMinutes(150);
                ZoneId zoneId = ZoneId.systemDefault();
                String cron = quartzUtils.convertToCron(Date.from(localDateTime.atZone(zoneId).toInstant()));
                //修改
                quartzUtils.modifyJob(acceptEnums.getName() + workOrderId, QuartzEnums.WORK_ORDER_OVERTIME_TASK.getGroup(), cron);
            } else {
                // 删除
                quartzUtils.deleteJob(acceptEnums.getName() + workOrderId, acceptEnums.getGroup());
            }
        } catch (SchedulerException | ParseException e) {
            throw new ServiceException("生成工单接收任务失败!");
        }
    }

    /**
     * 开始或删除工单接收任务
     *
     * @param workOrderId 工单id
     * @param isStart     是否是开始标识
     * @param dateTime    时间
     * @author DB
     * @since 2023-11-29 10:21:02
     */
    private void startOrRemoveWorkOrderAcceptTask(String workOrderId, Boolean isStart, LocalDateTime dateTime) {
        // 基于表达式构建触发器
        QuartzUtils quartzUtils = SpringUtils.getBean(QuartzUtils.class);
        try {
            QuartzEnums acceptEnums = QuartzEnums.WORK_ORDER_ACCEPT_OVERTIME_TASK;
            // 开始
            if (isStart) {
                // 通过JobBuilder构建JobDetail实例，JobDetail规定其job只能是实现Job接口的实例
                JobDetail jobDetail = JobBuilder.newJob(WorkOrderAcceptOverTimeTask.class)
                        .withIdentity(acceptEnums.getName() + workOrderId, acceptEnums.getGroup())
                        .usingJobData("WorkOrderId", workOrderId)
                        .build();
                LocalDateTime localDateTime = dateTime.plusMinutes(30);
                ZoneId zoneId = ZoneId.systemDefault();
                String cron = quartzUtils.convertToCron(Date.from(localDateTime.atZone(zoneId).toInstant()));
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
                // CronTrigger表达式触发器 继承于Trigger。TriggerBuilder 用于构建触发器实例
                CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                        .withIdentity(acceptEnums.getName() + workOrderId, acceptEnums.getGroup())
                        .withSchedule(cronScheduleBuilder)
                        .build();
                scheduler.scheduleJob(jobDetail, cronTrigger);
            } else {
                // 删除
                quartzUtils.deleteJob(acceptEnums.getName() + workOrderId, acceptEnums.getGroup());
            }
        } catch (SchedulerException | ParseException e) {
            throw new ServiceException("生成工单接收任务失败!");
        }
    }

    /**
     * 生成工单超时任务
     *
     * @param workOrderId 工单id
     * @param isUpdate    是否更新
     * @param dateTime    开始时间
     * @param minute    延迟多少分钟
     * @author DB
     * @since 2023-11-29 16:09:02
     */
    private void startOrUpdateWorkOrderOvertimeTask(String workOrderId, Boolean isUpdate, LocalDateTime dateTime,Integer minute) {
        // 基于表达式构建触发器
        QuartzUtils quartzUtils = SpringUtils.getBean(QuartzUtils.class);
        try {
            //实际三小时
            LocalDateTime localDateTime = dateTime.plusMinutes(minute);
            ZoneId zoneId = ZoneId.systemDefault();
            String cron = quartzUtils.convertToCron(Date.from(localDateTime.atZone(zoneId).toInstant()));
            if (isUpdate) {
                quartzUtils.modifyJob(QuartzEnums.WORK_ORDER_OVERTIME_TASK.getName() + workOrderId,
                        QuartzEnums.WORK_ORDER_OVERTIME_TASK.getGroup(),
                        cron);
            } else {
                // 通过JobBuilder构建JobDetail实例，JobDetail规定其job只能是实现Job接口的实例
                JobDetail jobDetail = JobBuilder.newJob(WorkOrderOvertimeTask.class)
                        .withIdentity(QuartzEnums.WORK_ORDER_OVERTIME_TASK.getName() + workOrderId,
                                QuartzEnums.WORK_ORDER_OVERTIME_TASK.getGroup())
                        .usingJobData("WorkOrderId", workOrderId)
                        .build();
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
                // CronTrigger表达式触发器 继承于Trigger。TriggerBuilder 用于构建触发器实例
                CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                        .withIdentity(QuartzEnums.WORK_ORDER_OVERTIME_TASK.getName() + workOrderId,
                                QuartzEnums.WORK_ORDER_OVERTIME_TASK.getGroup())
                        .withSchedule(cronScheduleBuilder)
                        .build();
                scheduler.scheduleJob(jobDetail, cronTrigger);
            }
        } catch (SchedulerException | ParseException e) {
            log.error(e.getMessage());
            throw new ServiceException("生成工单超时任务失败!");
        }
    }

    /**
     * 生成工单超时任务
     *
     * @param workOrderId 工单id
     * @param isUpdate    是否更新
     * @param dateTime    开始时间
     * @author DB
     * @since 2023-11-29 16:09:02
     */
    private void startOrUpdateWorkOrderOvertimeTask(String workOrderId, Boolean isUpdate, LocalDateTime dateTime) {
        startOrUpdateWorkOrderOvertimeTask(workOrderId, isUpdate, dateTime, 180);
    }

    /**
     * 工单模块-工单提交-工单记录列表
     *
     * @param workOrderId 工单id
     * @author DB
     * @since 2023-11-29 11:25:28
     */
    @Override
    public List<TaskWorkOrderRecordListVo> getWorkOrderRecordList(String workOrderId) {
        // 根据任务id获取列表
        return SpringUtils.getBean(TaskWorkOrderRecordService.class)
                .listAs(
                        QueryWrapper.create()
                                .select(TASK_WORK_ORDER_RECORD.ALL_COLUMNS)
                                .select(STAFF.NAME.as(TaskWorkOrderRecordListVo::getRecordStaffName),
                                        STAFF.STAFF_TYPE.as(TaskWorkOrderRecordListVo::getRecordStaffType))
                                .select(SYS_USER.AVATAR.as(TaskWorkOrderRecordListVo::getAvatar))
                                .from(TASK_WORK_ORDER_RECORD)
                                // 员工表
                                .leftJoin(STAFF)
                                .on(STAFF.ID.eq(TASK_WORK_ORDER_RECORD.RECORD_STAFF_ID))
                                // 系统用户表
                                .leftJoin(SYS_USER)
                                .on(SYS_USER.ID.eq(STAFF.USER_ID))
                                .where(TASK_WORK_ORDER_RECORD.TASK_WORK_ORDER_ID.eq(workOrderId))
                                .orderBy(TASK_WORK_ORDER_RECORD.CREATE_TIME.asc()),
                        TaskWorkOrderRecordListVo.class);
    }

    /**
     * 工单模块-工单提交-新增工单记录
     *
     * @param bo 请求参数
     * @author DB
     * @since 2023-11-29 11:33:35
     */
    @Override
    public void insertWorkOrderRecord(TaskWorkOrderRecordBo bo) {
        // 获取工单信息
        TaskWorkOrder taskWorkOrder = this.getById(bo.getTaskWorkOrderId());
        // 获取任务信息
        TaskService taskService = SpringUtils.getBean(TaskService.class);
        Task task = taskService.getOne(QueryWrapper.create().where(TASK.ID.eq(taskWorkOrder.getTaskId())));
        // 插入工单记录
        TaskWorkOrderRecord taskWorkOrderRecord = BeanUtils.mapToClass(bo, TaskWorkOrderRecord.class);
        // 读取员工信息
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        taskWorkOrderRecord.setRecordStaffId(loginUserInfo.getString("id"));
        // 插入记录
        SpringUtils.getBean(TaskWorkOrderRecordService.class).save(taskWorkOrderRecord);
        //判断插入记录用户类型
        List<String> phoneList = new ArrayList<>();
        StaffService staffService = SpringUtils.getBean(StaffService.class);
        if (loginUserInfo.getInteger("staffType") == 1) {
            // 负责人手机号
            StaffInfoVo responsibleStaff = staffService.getStaffInfo(task.getResponsibleStaffId());
            phoneList.add(responsibleStaff.getPhoneNumber());
        } else {
            //读取记录人信息
            StaffInfoVo recordStaff = SpringUtils.getBean(StaffService.class).getStaffInfo(task.getRecordStaffId());
            phoneList.add(recordStaff.getPhoneNumber());
        }
        phoneList.add(loginUserInfo.getString("phoneNumber"));
        // 通知记录
        try {
            SpringUtils.getBean(WebHookSendHandler.class).webHookSendText(WebHookKeyConstant.ORDER_INFO_BOT, phoneList,
                    task.getTaskContent() + "\n工单有新回复:\n" + bo.getRecordText(),
                    false);
        } catch (IOException e) {
            throw new ServiceException("发送消息通知失败!");
        }
    }

    /**
     * 工单模块-工单提交-工单提醒
     *
     * @param workOrderId 工单id
     * @author DB
     * @since 2023-11-29 11:41:46
     */
    @Override
    public void workOrderRemind(String workOrderId) {
        Task task = SpringUtils.getBean(TaskService.class)
                .queryChain()
                .select(TASK.ALL_COLUMNS)
                .leftJoin(TASK_WORK_ORDER)
                .on(TASK_WORK_ORDER.TASK_ID.eq(TASK.ID))
                .where(TASK_WORK_ORDER.ID.eq(workOrderId))
                .one();
        // 获取任务状态
        List<String> phoneList = new ArrayList<>();
        try {
            // 负责人手机号
            StaffInfoVo responsibleStaff =
                    SpringUtils.getBean(StaffService.class).getStaffInfo(task.getResponsibleStaffId());
            phoneList.add(responsibleStaff.getPhoneNumber());
            switch (task.getTaskStatus()) {
                // 待接收与接收超时
                case 1:
                case 7:
                    SpringUtils.getBean(WebHookSendHandler.class)
                            .webHookSendText(WebHookKeyConstant.ORDER_INFO_BOT,
                                    phoneList,
                                    task.getTaskContent() + "\n您有一条工单需要接收\n",
                                    false);
                    break;
                default:
                    SpringUtils.getBean(WebHookSendHandler.class)
                            .webHookSendText(WebHookKeyConstant.ORDER_INFO_BOT,
                                    phoneList,
                                    task.getTaskContent() + "\n您有一条工单需要尽快处理\n",
                                    false);
            }
        } catch (IOException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 工单模块-工单提交-工单转需求
     *
     * @param workOrderId 工单id
     * @author DB
     * @since 2023-11-29 12:30:21
     */
    @Override
    public void workOrderToDemand(String workOrderId) {
        // 对比当前用户信息
        TaskWorkOrder workOrder = this.getById(workOrderId);
        // 获取任务
        Task task = SpringUtils.getBean(TaskService.class).getById(workOrder.getTaskId());
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        if (!StringUtils.equals(task.getRecordStaffId(), loginUserInfo.getString("id"))) {
            throw new ServiceException("当前操作员工不是当前工单提交人,请联系提交人操作");
        }
        TaskDemandBo taskDemandBo = new TaskDemandBo();
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        CommonService commonService = SpringUtils.getBean(CommonService.class);
        String brandId = redisService.getCacheObject(commonService.getCacheKey(OamConfigEnum.DEMAND_TO_TASK_BRAND.getKey()));
        String storeId = redisService.getCacheObject(commonService.getCacheKey(OamConfigEnum.DEMAND_TO_TASK_STORE.getKey()));
        taskDemandBo.setAttachmentUrl(task.getAttachmentUrl())
                .setBrandId(brandId)
                .setStoreId(storeId)
                .setTaskContent(task.getTaskContent());
        SpringUtils.getBean(TaskDemandService.class).insertTaskDemand(taskDemandBo);
        //工单办结
        workOrderFinish(workOrderId);
    }

    /**
     * 工单模块-工单提交-工单暂停
     *
     * @param bo 请求参数
     * @author DB
     * @since 2023-11-29 16:00:18
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pauseWorkOrder(PauseWorkOrderBo bo) {
        if (bo.getPauseExpireTime().isBefore(LocalDateTime.now())){
            throw new ServiceException("暂停时间小于当前时间,暂停失败");
        }
        // 获取工单信息
        TaskWorkOrder workOrder = this.getById(bo.getWorkOrderId());
        TaskService taskService = SpringUtils.getBean(TaskService.class);
        Task task = taskService.getById(workOrder.getTaskId());
        // 如果任务是在进行中没有逾期，重设定时任务
        if (task.getTaskStatus() == 2) {
            startOrUpdateWorkOrderOvertimeTask(bo.getWorkOrderId(), true, bo.getPauseExpireTime());
        }
        // 工单暂停
        startOrRemoveWorkOrderPauseRecover(bo.getWorkOrderId(), true, bo.getPauseExpireTime());
        // 工单完结超时
        workOrderFinishOverTimeRemindTask(bo.getWorkOrderId(), 1, bo.getPauseExpireTime());
        // 设定任务状态暂停
        task.setTaskStatus(5);
        taskService.updateById(task);
        // 插入工单暂停记录
        TaskWorkOrderRecord taskWorkOrderRecord = new TaskWorkOrderRecord();
        //暂停时间
        String pauseExpireTime = "工单已暂停到:" + bo.getPauseExpireTime().format(DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS));
        // 读取员工信息
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        taskWorkOrderRecord.setRecordStaffId(loginUserInfo.getString("id"))
                .setTaskWorkOrderId(bo.getWorkOrderId())
                .setRecordText(pauseExpireTime);
        // 插入记录
        SpringUtils.getBean(TaskWorkOrderRecordService.class).save(taskWorkOrderRecord);
        // 负责人手机号
        StaffInfoVo responsibleStaff = SpringUtils.getBean(StaffService.class).getStaffInfo(task.getResponsibleStaffId());
        List<String> phoneList = new ArrayList<>();
        phoneList.add(responsibleStaff.getPhoneNumber());
        phoneList.add(loginUserInfo.getString("phoneNumber"));
        // 通知记录
        try {
            SpringUtils.getBean(WebHookSendHandler.class)
                    .webHookSendText(WebHookKeyConstant.ORDER_INFO_BOT,
                            phoneList,
                            task.getTaskContent() + "\n" + pauseExpireTime,
                            false);
        } catch (IOException e) {
            throw new ServiceException("发送消息通知失败!");
        }
        // 更新工单信息
        this.updateChain()
                .set(TASK_WORK_ORDER.PAUSE_STAFF_ID, loginUserInfo.getString("id"))
                .set(TASK_WORK_ORDER.PAUSE_EXPIRE_TIME, bo.getPauseExpireTime())
                .set(TASK_WORK_ORDER.PAUSE_RECORD_TIME, LocalDateTime.now())
                .where(TASK_WORK_ORDER.ID.eq(bo.getWorkOrderId()))
                .update();
    }

    /**
     * 开始与删除工单暂停恢复任务
     * @author DB
     * @since 2023/12/21
     * @param workOrderId 工单di
     * @param isStart 是否开始
     * @param pauseExpireTime 暂停到期时间
     */
    private void startOrRemoveWorkOrderPauseRecover(String workOrderId, Boolean isStart, LocalDateTime pauseExpireTime) {
        // 基于表达式构建触发器
        QuartzUtils quartzUtils = SpringUtils.getBean(QuartzUtils.class);
        try {
            if (isStart) {
                ZoneId zoneId = ZoneId.systemDefault();
                String cron = quartzUtils.convertToCron(Date.from(pauseExpireTime.atZone(zoneId).toInstant()));
                // 通过JobBuilder构建JobDetail实例，JobDetail规定其job只能是实现Job接口的实例
                JobDetail jobDetail = JobBuilder.newJob(WorkOrderPauseRecoverTask.class)
                        .withIdentity(QuartzEnums.WORK_ORDER_PAUSE_RECOVER_TASK.getName() + workOrderId,
                                QuartzEnums.WORK_ORDER_PAUSE_RECOVER_TASK.getGroup())
                        .usingJobData("WorkOrderId", workOrderId)
                        .build();
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
                // CronTrigger表达式触发器 继承于Trigger。TriggerBuilder 用于构建触发器实例
                CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                        .withIdentity(QuartzEnums.WORK_ORDER_PAUSE_RECOVER_TASK.getName() + workOrderId,
                                QuartzEnums.WORK_ORDER_PAUSE_RECOVER_TASK.getGroup())
                        .withSchedule(cronScheduleBuilder)
                        .build();
                scheduler.scheduleJob(jobDetail, cronTrigger);
            } else {
                quartzUtils.deleteJob(QuartzEnums.WORK_ORDER_PAUSE_RECOVER_TASK.getName() + workOrderId,
                        QuartzEnums.WORK_ORDER_PAUSE_RECOVER_TASK.getGroup());
            }
        } catch (SchedulerException | ParseException e) {
            log.error(e.getMessage());
            throw new ServiceException("生成工单超时任务失败!");
        }
    }

    /**
     * 工单模块-工单提交-工单办结
     *
     * @param workOrderId 工单id
     * @author DB
     * @since 2023-11-29 15:44:08
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void workOrderFinish(String workOrderId) {
        // 获取工单信息
        TaskWorkOrder taskWorkOrder = this.getById(workOrderId);
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        taskWorkOrder.setFinishStaffId(loginUserInfo.getString("id"));
        LocalDateTime finishTime = LocalDateTime.now();
        taskWorkOrder.setFinishTime(finishTime);
        // 设置工单完成
        TaskService taskService = SpringUtils.getBean(TaskService.class);
        Task task = taskService.getOne(QueryWrapper.create().where(TASK.ID.eq(taskWorkOrder.getTaskId())));
        //直接设置完成
        task.setTaskItem(3).setTaskStatus(9);
        task.setCompletionDate(LocalDate.now());
        taskService.updateById(task);
        // 更新工单
        this.updateById(taskWorkOrder);
        // 插入工单暂停记录
        TaskWorkOrderRecord taskWorkOrderRecord = new TaskWorkOrderRecord();
        //暂停时间
        String finishTimeString = "\n工单办结,时间为:" + finishTime.format(DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS));
        // 读取员工信息
        taskWorkOrderRecord.setRecordStaffId(loginUserInfo.getString("id"))
                .setTaskWorkOrderId(workOrderId)
                .setRecordText(finishTimeString);
        // 插入记录
        SpringUtils.getBean(TaskWorkOrderRecordService.class).save(taskWorkOrderRecord);
        // 负责人手机号
        StaffService staffService = SpringUtils.getBean(StaffService.class);
        Set<String> staffIds = new HashSet<>();
        staffIds.add(task.getResponsibleStaffId());
        staffIds.add(task.getRecordStaffId());
        Map<String, StaffInfoVo> staffMap = staffService.listAs(QueryWrapper.create()
                                .select(STAFF.ID, STAFF.NAME)
                                .select(SYS_USER.PHONE_NUMBER.as(StaffInfoVo::getPhoneNumber))
                                .from(STAFF)
                                .leftJoin(SYS_USER)
                                .on(SYS_USER.ID.eq(STAFF.USER_ID))
                                .where(STAFF.ID.in(staffIds)),
                        StaffInfoVo.class)
                .stream().collect(Collectors.toMap(StaffInfoVo::getId, item -> item));
        List<String> phoneList = new ArrayList<>();
        phoneList.add(staffMap.get(task.getResponsibleStaffId()).getPhoneNumber());
        // 记录人手机号
        phoneList.add(staffMap.get(task.getRecordStaffId()).getPhoneNumber());
        // 通知记录
        try {
            SpringUtils.getBean(WebHookSendHandler.class).webHookSendText(WebHookKeyConstant.ORDER_INFO_BOT, phoneList, task.getTaskContent() + "\n" + finishTimeString, false);
        } catch (IOException e) {
            throw new ServiceException("发送消息通知失败!");
        }
        // 删除工单完结超时任务
        try {
            SpringUtils.getBean(QuartzUtils.class).deleteJob(QuartzEnums.WORK_ORDER_OVERTIME_TASK.getName() + workOrderId, QuartzEnums.WORK_ORDER_OVERTIME_TASK.getGroup());
            // 删除工单暂停恢复任务
            startOrRemoveWorkOrderPauseRecover(workOrderId, false, null);
            // 删除工单完结超时任务
            workOrderFinishOverTimeRemindTask(workOrderId, 2, null);
        } catch (SchedulerException e) {
            throw new ServiceException("删除工单完结超时任务失败");
        }
    }

    /**
     * 技术模块-技术-获取个人工单列表
     *
     * @author DB
     * @since 2023-11-30 19:41:32
     */
    @Override
    public List<TaskWorkOrderPersonVo> getPersonWorkOrder() {
        // 获取值班员工
        DutyService dutyService = SpringUtils.getBean(DutyService.class);
        Duty duty = dutyService.getOne(QueryWrapper.create().where(DUTY.DUTY_DATE.eq(DateUtils.getDate())));
        if (duty == null) {
            throw new ServiceException("当天没有值班员工,请联系相关人员添加值班信息!");
        }
        return this.mapper.selectListByQueryAs(QueryWrapper.create()
                        .select(TASK_WORK_ORDER.ID,
                                TASK_WORK_ORDER.PHONE_NUMBER,
                                TASK_WORK_ORDER.CREATE_TIME.as("createTime"),
                                TASK_WORK_ORDER.RECEIVING_TIME)
                        // 任务
                        .select(TASK.TASK_CONTENT, TASK.TASK_STATUS, TASK.TASK_RECEIPT_TIME, TASK.RECORD_STAFF_ID)
                        // 品牌
                        .select(BRAND.BRAND_NAME)
                        // 校区
                        .select(STORE.STORE_NAME)
                        .leftJoin(TASK)
                        .on(TASK.ID.eq(TASK_WORK_ORDER.TASK_ID))
                        .leftJoin(BRAND)
                        .on(BRAND.ID.eq(TASK_WORK_ORDER.BRAND_ID))
                        .leftJoin(STORE)
                        .on(STORE.ID.eq(TASK_WORK_ORDER.STORE_ID))
                        .where(TASK.TASK_STATUS.in(1, 2, 4, 5, 7)),
                TaskWorkOrderPersonVo.class,
                // 提交人
                item -> item.field(TaskWorkOrderPersonVo::getRecordStaffName)
                        .queryWrapper(recordStaff -> queryChain().select(STAFF.NAME.as("recordStaffName"))
                                .from(STAFF)
                                .where(STAFF.ID.eq(recordStaff.getRecordStaffId()))));
    }

    /**
     * 是否是第一个接收的技术员工
     *
     * @param taskWorkOrderId 工单记录id
     * @author DB
     * @since 2023/12/04
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void whetherReceive(String taskWorkOrderId) {
        Task task = SpringUtils.getBean(TaskService.class).queryChain()
                .leftJoin(TASK_WORK_ORDER).on(TASK_WORK_ORDER.TASK_ID.eq(TASK.ID))
                .where(TASK_WORK_ORDER.ID.eq(taskWorkOrderId)).one();
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        //判断当前登陆用户是否是当前工单的负责员工
        if (StringUtils.equals(loginUserInfo.getString("id"), task.getResponsibleStaffId())) {
            //查询负责员工的记录数
            long count = SpringUtils.getBean(TaskWorkOrderRecordService.class).queryChain()
                    .where(TASK_WORK_ORDER_RECORD.TASK_WORK_ORDER_ID.eq(taskWorkOrderId))
                    .and(TASK_WORK_ORDER_RECORD.RECORD_STAFF_ID.eq(task.getResponsibleStaffId()))
                    .count();
            //首次记录修改状态
            if (count == 0) {
                //获取工单
                TaskWorkOrder workOrder = this.getById(taskWorkOrderId);
                LocalDateTime now = LocalDateTime.now();
                SpringUtils.getBean(TaskService.class).updateChain()
                        .set(TASK.TASK_STATUS, 2)
                        .set(TASK.TASK_RECEIPT_TIME, now)
                        .where(TASK.ID.eq(task.getId()))
                        .update();
                Duration duration = Duration.between(workOrder.getCreateTime(), now);
                workOrder.setReceivingTime((int) duration.toMinutes());
                this.updateById(workOrder);
                //删除工单接收超时任务
                startOrRemoveWorkOrderAcceptTask(taskWorkOrderId, false, now);
                //删除工单接收超时提醒任务
                startOrRemoveWorkOrderAcceptOverTimeRemindTask(taskWorkOrderId, false, now);
                //生成工单处理超时任务
                startOrUpdateWorkOrderOvertimeTask(taskWorkOrderId, false, now);
                //生成工单完结超时任务提醒
                workOrderFinishOverTimeRemindTask(taskWorkOrderId, 0, now);
            }
        }
    }

    /**
     * 工单统计-获取工单统计分页
     * @author DB
     * @since 2024/1/31
     * @param staffId 员工id
     * @param startDate 开始日期
     * @param endDate 结束日期
     * @return R<Page<TaskWorkOrderPageVo>>
     */
    @Override
    public Page<TaskWorkOrderStatPageVo> getWorkOrderStatPage(String staffId,LocalDate startDate,LocalDate endDate) {
        PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
        QueryWrapper queryWrapper = QueryWrapper.create();
        if (startDate == null && endDate == null) {
            LocalDate now = LocalDate.now();
            // 获取目标月的第一天
            LocalDate firstDay = now.with(TemporalAdjusters.firstDayOfMonth());
            // 获取目标月的最后一天
            LocalDate lastDay = now.with(TemporalAdjusters.lastDayOfMonth());
            //获取当月绩点
            queryWrapper.where(dateFormat(TASK_WORK_ORDER.CREATE_TIME, "%Y-%m-%d").between(firstDay, lastDay));
        } else {
            queryWrapper.where(dateFormat(TASK_WORK_ORDER.CREATE_TIME, "%Y-%m-%d").between(startDate, endDate));
        }
        return this.mapper.paginateAs(Page.of(pageDomain.getPageNum(), pageDomain.getPageSize()), queryWrapper
                        .select(TASK_WORK_ORDER.ID,BRAND.BRAND_NAME,STORE.STORE_NAME,TASK_WORK_ORDER.RECEIVING_TIME,TASK.TASK_CONTENT,TASK_WORK_ORDER.PAUSE_STAFF_ID,TASK_WORK_ORDER.PAUSE_EXPIRE_TIME,
                                TASK_WORK_ORDER.PAUSE_RECORD_TIME,TASK_WORK_ORDER.FINISH_STAFF_ID,TASK_WORK_ORDER.FINISH_TIME,TASK.RECORD_STAFF_ID,TASK.RESPONSIBLE_STAFF_ID,TASK.TASK_RECEIPT_TIME,
                                TASK_STAFF_GROUP.GRADE_POINT,TASK_STAFF_GROUP.REMARK,TASK.TASK_STATUS)
                        .leftJoin(BRAND).on(BRAND.ID.eq(TASK_WORK_ORDER.BRAND_ID))
                        .leftJoin(STORE).on(STORE.ID.eq(TASK_WORK_ORDER.STORE_ID))
                        .leftJoin(TASK).on(TASK.ID.eq(TASK_WORK_ORDER.TASK_ID))
                        .leftJoin(TASK_STAFF_GROUP).on(TASK_STAFF_GROUP.TASK_ID.eq(TASK.ID))
                        .and(TASK.RESPONSIBLE_STAFF_ID.eq(staffId))
                        .orderBy(TASK_WORK_ORDER.CREATE_TIME.desc()),
                TaskWorkOrderStatPageVo.class,
                // 暂停人
                item -> item.field(TaskWorkOrderStatPageVo::getPauseStaffName)
                        .queryWrapper(pauseStaffName -> {
                            if (StringUtils.isNotBlank(pauseStaffName.getPauseStaffId())) {
                                return queryChain().select(STAFF.NAME.as(TaskWorkOrderStatPageVo::getPauseStaffName))
                                        .from(STAFF)
                                        .where(STAFF.ID.eq(pauseStaffName.getPauseStaffId()));
                            } else {
                                return null;
                            }
                        }),
                // 办结人
                item -> item.field(TaskWorkOrderStatPageVo::getFinishStaffName)
                        .queryWrapper(finishStaffName -> {
                            if (StringUtils.isNotBlank(finishStaffName.getFinishStaffId())) {
                                return queryChain().select(STAFF.NAME.as(TaskWorkOrderStatPageVo::getFinishStaffName))
                                        .from(STAFF)
                                        .where(STAFF.ID.eq(finishStaffName.getFinishStaffId()));
                            } else {
                                return null;
                            }
                        }),
                // 记录人
                item -> item.field(TaskWorkOrderStatPageVo::getRecordStaffName)
                        .queryWrapper(recordStaffName -> {
                            if (StringUtils.isNotBlank(recordStaffName.getRecordStaffId())) {
                                return queryChain().select(STAFF.NAME.as(TaskWorkOrderStatPageVo::getRecordStaffName))
                                        .from(STAFF)
                                        .where(STAFF.ID.eq(recordStaffName.getRecordStaffId()));
                            } else {
                                return null;
                            }
                        }),
                // 负责人
                item -> item.field(TaskWorkOrderStatPageVo::getResponsibleStaffName)
                        .queryWrapper(responsibleStaffName -> {
                            if (StringUtils.isNotBlank(responsibleStaffName.getResponsibleStaffId())){
                               return queryChain().select(STAFF.NAME.as(TaskWorkOrderStatPageVo::getResponsibleStaffName))
                                        .from(STAFF)
                                        .where(STAFF.ID.eq(responsibleStaffName.getResponsibleStaffId()));
                            } else {
                                return null;
                            }
                        }));
    }

    /**
     * 强制办结工单
     * @author DB
     * @since 2024/2/4
     * @param bo 请求参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void enforceFinishWorkOrder(EnforceFinishWorkOrderBo bo) {
        // 获取工单信息
        TaskWorkOrder taskWorkOrder = this.getById(bo.getId());
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        taskWorkOrder.setEnforceFinishStaffId(loginUserInfo.getString("id"));
        LocalDateTime finishTime = LocalDateTime.now();
        taskWorkOrder.setEnforceFinishTime(finishTime);
        taskWorkOrder.setEnforceFinishReason(bo.getEnforceFinishReason());
        // 读取工单办结定时任务
        QuartzUtils quartzUtils = SpringUtils.getBean(QuartzUtils.class);
        try {
            LocalDateTime jobExecuteTime = quartzUtils.getJobExecuteTime(QuartzEnums.WORK_ORDER_OVERTIME_TASK.getName() + bo.getId(), QuartzEnums.WORK_ORDER_OVERTIME_TASK.getGroup());
            Duration duration = Duration.between(finishTime, jobExecuteTime);
            Long minutes = duration.toMinutes();
            taskWorkOrder.setEnforceFinishSurplusTime(minutes.intValue());
        } catch (SchedulerException e) {
            throw new UtilException(e);
        }
        // 设置工单强制办结
        TaskService taskService = SpringUtils.getBean(TaskService.class);
        Task task = taskService.getOne(QueryWrapper.create().where(TASK.ID.eq(taskWorkOrder.getTaskId())));
        //检查工单接受时间距离现在是否大于120分钟
        Duration between = Duration.between(task.getTaskReceiptTime(), finishTime);
        long minutes = between.toMinutes();
        if (minutes - 120 <= 0) {
            throw new ServiceException("强制办结工单的接受时间距离现在必须大于120分钟");
        }
        //设置强制办结
        task.setTaskItem(1).setTaskStatus(6);
        taskService.updateById(task);
        // 更新工单
        this.updateById(taskWorkOrder);
        // 插入工单暂停记录
        TaskWorkOrderRecord taskWorkOrderRecord = new TaskWorkOrderRecord();
        String finishTimeString = "提交工单办结申请,时间为:" + finishTime.format(DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS));
        // 读取员工信息
        taskWorkOrderRecord.setRecordStaffId(loginUserInfo.getString("id"))
                .setTaskWorkOrderId(bo.getId())
                .setRecordText(finishTimeString);
        // 插入记录
        SpringUtils.getBean(TaskWorkOrderRecordService.class).save(taskWorkOrderRecord);
        // 负责人手机号
        StaffService staffService = SpringUtils.getBean(StaffService.class);
        Set<String> staffIds = new HashSet<>();
        staffIds.add(task.getResponsibleStaffId());
        staffIds.add(task.getRecordStaffId());
        Map<String, StaffInfoVo> staffMap = staffService.listAs(QueryWrapper.create()
                                .select(STAFF.ID, STAFF.NAME)
                                .select(SYS_USER.PHONE_NUMBER.as(StaffInfoVo::getPhoneNumber))
                                .from(STAFF)
                                .leftJoin(SYS_USER)
                                .on(SYS_USER.ID.eq(STAFF.USER_ID))
                                .where(STAFF.ID.in(staffIds)),
                        StaffInfoVo.class)
                .stream().collect(Collectors.toMap(StaffInfoVo::getId, item -> item));
        List<String> phoneList = new ArrayList<>();
        phoneList.add(staffMap.get(task.getResponsibleStaffId()).getPhoneNumber());
        // 记录人手机号
        phoneList.add(staffMap.get(task.getRecordStaffId()).getPhoneNumber());
        // 通知记录
        try {
            SpringUtils.getBean(WebHookSendHandler.class).webHookSendText(WebHookKeyConstant.ORDER_INFO_BOT, phoneList, task.getTaskContent() + "\n" + finishTimeString, false);
        } catch (IOException e) {
            throw new ServiceException("发送消息通知失败!");
        }
        // 删除工单完结超时任务
        try {
            SpringUtils.getBean(QuartzUtils.class).deleteJob(QuartzEnums.WORK_ORDER_OVERTIME_TASK.getName() + bo.getId(), QuartzEnums.WORK_ORDER_OVERTIME_TASK.getGroup());
            // 删除工单暂停恢复任务
            startOrRemoveWorkOrderPauseRecover(bo.getId(), false, null);
            // 删除工单完结超时任务
            workOrderFinishOverTimeRemindTask(bo.getId(), 2, null);
        } catch (SchedulerException e) {
            throw new ServiceException("删除工单完结超时任务失败");
        }
    }

    /**
     * 工单模块-工单提交-同意与拒绝强制办结
     * @author DB
     * @since 2024/2/21
     * @param bo 请求参数
     */
    @Override
    public void agreeOrRejectEnforceFinish(AgreeOrRejectEnforceFinishBo bo) {
        TaskWorkOrder workOrder = this.getById(bo.getId());
        TaskService taskService = SpringUtils.getBean(TaskService.class);
        Task task = taskService.getById(workOrder.getTaskId());
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        String finishString;
        //同意
        if (bo.getAgreeOrReject()){
            task.setTaskStatus(9);
            task.setTaskItem(3);
            taskService.updateById(task);
            workOrder.setFinishStaffId(loginUserInfo.getString("id"));
            workOrder.setFinishTime(workOrder.getEnforceFinishTime());
            this.updateById(workOrder);
            finishString = "\n工单办结申请通过,办结时间为:" + workOrder.getFinishTime().format(DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS));
        } else {
            //拒绝继续计时
            task.setTaskStatus(2);
            task.setTaskItem(0);
            taskService.updateById(task);
            LocalDateTime now = LocalDateTime.now();
            finishString = "\n工单办结申请未通过,工单到期时间为:" + now.plusMinutes(workOrder.getEnforceFinishSurplusTime().longValue()).format(DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS)) +
                    "\n工单到期剩余时间为:" + workOrder.getEnforceFinishSurplusTime() + "分钟";
            //重新发起工单超时任务
            startOrUpdateWorkOrderOvertimeTask(workOrder.getId(), false, now, workOrder.getEnforceFinishSurplusTime());
        }
        // 插入工单暂停记录
        TaskWorkOrderRecord taskWorkOrderRecord = new TaskWorkOrderRecord();
        // 读取员工信息
        taskWorkOrderRecord.setRecordStaffId(loginUserInfo.getString("id"))
                .setTaskWorkOrderId(workOrder.getId())
                .setRecordText(finishString);
        // 插入记录
        SpringUtils.getBean(TaskWorkOrderRecordService.class).save(taskWorkOrderRecord);
        // 负责人手机号
        StaffService staffService = SpringUtils.getBean(StaffService.class);
        Set<String> staffIds = new HashSet<>();
        staffIds.add(task.getResponsibleStaffId());
        staffIds.add(task.getRecordStaffId());
        Map<String, StaffInfoVo> staffMap = staffService.listAs(QueryWrapper.create()
                                .select(STAFF.ID, STAFF.NAME)
                                .select(SYS_USER.PHONE_NUMBER.as(StaffInfoVo::getPhoneNumber))
                                .from(STAFF)
                                .leftJoin(SYS_USER)
                                .on(SYS_USER.ID.eq(STAFF.USER_ID))
                                .where(STAFF.ID.in(staffIds)),
                        StaffInfoVo.class)
                .stream().collect(Collectors.toMap(StaffInfoVo::getId, item -> item));
        List<String> phoneList = new ArrayList<>();
        phoneList.add(staffMap.get(task.getResponsibleStaffId()).getPhoneNumber());
        // 记录人手机号
        phoneList.add(staffMap.get(task.getRecordStaffId()).getPhoneNumber());
        // 通知记录
        try {
            SpringUtils.getBean(WebHookSendHandler.class).webHookSendText(WebHookKeyConstant.ORDER_INFO_BOT, phoneList, task.getTaskContent() + "\n" + finishString, false);
        } catch (IOException e) {
            throw new ServiceException("发送消息通知失败!");
        }
    }
}
