17个文件已修改
11个文件已添加
1417 ■■■■■ 已修改文件
business/src/main/java/com/ycl/constant/TaskTypeConstant.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/controller/CodingRulerController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/domain/entity/ProjectOvertimeTimes.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/domain/form/ProjectOvertimeTimesForm.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/domain/query/ProjectInfoQuery.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/domain/query/ProjectOvertimeTimesQuery.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/domain/vo/ProjectOvertimeTimesVO.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/domain/vo/ProjectProcessDetailVO.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/mapper/CodingRulerMapper.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/mapper/ProjectOvertimeTimesMapper.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/service/CodingRulerService.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/service/ProjectOvertimeTimesService.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/service/impl/CodingRulerServiceImpl.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/service/impl/ProjectCodingStatusServiceImpl.java 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/service/impl/ProjectInfoServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/service/impl/ProjectProcessServiceImpl.java 546 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/task/CodingTask.java 216 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/task/FlowableTask.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/resources/mapper/CodingRulerMapper.xml 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/resources/mapper/ProjectInfoMapper.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/resources/mapper/ProjectOvertimeTimesMapper.xml 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/src/main/java/com/ycl/common/enums/business/CodingRulerCodeTypeEnum.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/src/main/java/com/ycl/common/enums/business/CodingRulerSymbolTypeEnum.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/src/main/java/com/ycl/common/enums/business/TaskStatusEnum.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
common/src/main/java/com/ycl/common/utils/DateUtils.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
flowable/src/main/java/com/ycl/domain/vo/ProcessOvertimeTimesVO.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
flowable/src/main/java/com/ycl/mapper/ProcessCodingMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
flowable/src/main/resources/mapper/ProcessCodingMapper.xml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
business/src/main/java/com/ycl/constant/TaskTypeConstant.java
@@ -11,6 +11,7 @@
    public final static String ALL = "all"; // 全部任务
    public final static String TODO = "todo"; // 待办任务
    public final static String WAIT = "wait"; // 容缺
    public final static String JUMP = "jump"; // 容缺
    public final static String REMAINING = "remaining"; // 剩余任务
    public final static String TIMELY = "timely"; // 及时完成的任务
    public final static String OVERTIME = "overtime"; // 超时完成的任务
business/src/main/java/com/ycl/controller/CodingRulerController.java
@@ -25,7 +25,7 @@
 */
@Validated
@RequiredArgsConstructor
@Api(value = "项目编码类型管理", tags = "管理")
@Api(value = "赋码规则类型管理", tags = "管理")
@RestController
@RequestMapping("/coding-ruler")
public class CodingRulerController {
business/src/main/java/com/ycl/domain/entity/ProjectOvertimeTimes.java
New file
@@ -0,0 +1,39 @@
package com.ycl.domain.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import com.ycl.system.domain.base.AbsEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 *
 *
 * @author zxl
 * @since 2025-04-01
 */
@Data
@TableName("t_project_overtime_times")
public class ProjectOvertimeTimes extends AbsEntity {
    private static final long serialVersionUID = 1L;
    @TableField("task_overtime_num")
    /**  超时任务数*/
    private Long taskOvertimeNum;
    @TableField("process_ins_id")
    /**  流程实例id*/
    private String processInsId;
    @TableField("project_id")
    /**  项目id*/
    private String projectId;
    /**  项目类型*/
    @TableField("project_type")
    private String projectType;
}
business/src/main/java/com/ycl/domain/form/ProjectOvertimeTimesForm.java
New file
@@ -0,0 +1,49 @@
package com.ycl.domain.form;
import com.ycl.common.group.Update;
import com.ycl.common.group.Add;
import com.ycl.domain.entity.ProjectOvertimeTimes;
import com.ycl.system.domain.base.AbsForm;
import org.springframework.beans.BeanUtils;
import javax.validation.constraints.NotNull;
import org.springframework.lang.NonNull;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * 表单
 *
 * @author zxl
 * @since 2025-04-01
 */
@Data
@ApiModel(value = "ProjectCodingStatus表单", description = "表单")
public class ProjectOvertimeTimesForm extends AbsForm {
    @NotNull(message = "不能为空", groups = {Add.class, Update.class})
    @ApiModelProperty("超时任务数")
    private Integer taskOvertimeNum;
    @NotNull(message = "不能为空", groups = {Add.class, Update.class})
    @ApiModelProperty("流程实例id")
    private String processInsId;
    @NotNull(message = "不能为空", groups = {Add.class, Update.class})
    @ApiModelProperty("项目id")
    private String projectId;
    @NotNull(message = "不能为空", groups = {Add.class, Update.class})
    @ApiModelProperty("项目类型")
    private String projectType;
    public static ProjectOvertimeTimes getEntityByForm(@NonNull ProjectOvertimeTimesForm form, ProjectOvertimeTimes entity) {
        if(entity == null) {
          entity = new ProjectOvertimeTimes();
        }
        BeanUtils.copyProperties(form, entity);
        return entity;
    }
}
business/src/main/java/com/ycl/domain/query/ProjectInfoQuery.java
@@ -46,19 +46,29 @@
    private String investType;
    //行政区划
    private String area;
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date projectStartTime;
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date projectEndTime;
    //从有没有流程判断是存储还是早期
    private String reserveOrPrevious;
    private Integer role;// 业主:0,审核:1
    //查询条件业主单位
    private Long projectOwnerUnit;
    private Integer usedStatus;
    //查询条件主管部门
    private Long competentDepartment;
    @ApiModelProperty("项目码")
    private String coding;
    @ApiModelProperty("是否是异常项目查询:0 不是  1是")
    private Integer exe;
business/src/main/java/com/ycl/domain/query/ProjectOvertimeTimesQuery.java
New file
@@ -0,0 +1,22 @@
package com.ycl.domain.query;
import com.ycl.system.domain.base.AbsQuery;
import java.util.List;
import org.springframework.lang.NonNull;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * 查询
 *
 * @author zxl
 * @since 2025-04-01
 */
@Data
@ApiModel(value = "ProjectOvertimeTimesQuery查询参数", description = "查询参数")
public class ProjectOvertimeTimesQuery extends AbsQuery {
}
business/src/main/java/com/ycl/domain/vo/ProjectOvertimeTimesVO.java
New file
@@ -0,0 +1,46 @@
package com.ycl.domain.vo;
import com.ycl.domain.entity.ProjectOvertimeTimes;
import com.ycl.system.domain.base.AbsVo;
import org.springframework.lang.NonNull;
import org.springframework.beans.BeanUtils;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * 展示
 *
 * @author zxl
 * @since 2025-04-01
 */
@Data
@ApiModel(value = "响应数据", description = "响应数据")
public class ProjectOvertimeTimesVO extends AbsVo {
    /**  任务超时数*/
    @ApiModelProperty("任务超时数")
    private Integer taskOvertimeNum;
    /**  流程实例id*/
    @ApiModelProperty("流程实例id")
    private String processInsId;
    /**  项目id*/
    @ApiModelProperty("项目id")
    private String projectId;
    /**  项目类型*/
    @ApiModelProperty("项目类型")
    private String projectType;
    public static ProjectOvertimeTimesVO getVoByEntity(@NonNull ProjectOvertimeTimes entity, ProjectOvertimeTimesVO vo) {
        if(vo == null) {
            vo = new ProjectOvertimeTimesVO();
        }
        BeanUtils.copyProperties(entity, vo);
        return vo;
    }
}
business/src/main/java/com/ycl/domain/vo/ProjectProcessDetailVO.java
@@ -90,6 +90,11 @@
        /**
         * 容缺任务数
         */
        private Long toleranceNum = 0L;
        private Long waitTaskNum = 0L;
        /**
         * 跳过任务数
         */
        private Long jumpTaskNum = 0L;
    }
}
business/src/main/java/com/ycl/mapper/CodingRulerMapper.java
@@ -4,7 +4,6 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ycl.domain.vo.CodingRulerVO;
import com.ycl.domain.form.CodingRulerForm;
import com.ycl.domain.query.CodingRulerQuery;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
@@ -31,4 +30,6 @@
    */
    IPage getPage(IPage page, @Param("query") CodingRulerQuery query);
    List<CodingRulerVO> getStartRuler();
}
business/src/main/java/com/ycl/mapper/ProjectOvertimeTimesMapper.java
New file
@@ -0,0 +1,36 @@
package com.ycl.mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ycl.domain.entity.ProjectOvertimeTimes;
import com.ycl.domain.query.ProjectOvertimeTimesQuery;
import com.ycl.domain.vo.ProjectOvertimeTimesVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
 *  Mapper 接口
 *
 * @author zxl
 * @since 2025-04-01
 */
@Mapper
public interface ProjectOvertimeTimesMapper extends BaseMapper<ProjectOvertimeTimes> {
    /**
     * id查找
     * @param id
     * @return
     */
    ProjectOvertimeTimesVO getById(Integer id);
    /**
    *  分页
    */
    IPage getPage(IPage page, @Param("query") ProjectOvertimeTimesQuery query);
    void delAll();
}
business/src/main/java/com/ycl/service/CodingRulerService.java
@@ -8,7 +8,7 @@
import java.util.List;
/**
 *  服务类
 *  赋码规则服务类
 *
 * @author zxl
 * @since 2025-03-18
@@ -68,4 +68,10 @@
     * @return
     */
    Result changeCodingRulerStatus(Integer id);
    /**
     * 获得启动的赋码规则
     * @return 存放的字段 yellowRuler启用的黄码规则,redRuler启用的红码规则
     */
    Result getStartRuler();
}
business/src/main/java/com/ycl/service/ProjectOvertimeTimesService.java
New file
@@ -0,0 +1,66 @@
package com.ycl.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ycl.common.base.Result;
import com.ycl.domain.entity.ProjectOvertimeTimes;
import com.ycl.domain.form.ProjectOvertimeTimesForm;
import com.ycl.domain.query.ProjectOvertimeTimesQuery;
import java.util.List;
/**
 *  服务类
 *
 * @author zxl
 * @since 2025-04-01
 */
public interface ProjectOvertimeTimesService extends IService<ProjectOvertimeTimes> {
    /**
     * 添加
     * @param form
     * @return
     */
    Result add(ProjectOvertimeTimesForm form);
    /**
     * 修改
     * @param form
     * @return
     */
    Result update(ProjectOvertimeTimesForm form);
    /**
     * 批量删除
     * @param ids
     * @return
     */
    Result remove(List<String> ids);
    /**
     * id删除
     * @param id
     * @return
     */
    Result removeById(String id);
    /**
     * 分页查询
     * @param query
     * @return
     */
    Result page(ProjectOvertimeTimesQuery query);
    /**
     * 根据id查找
     * @param id
     * @return
     */
    Result detail(Integer id);
    /**
     * 列表
     * @return
     */
    Result all();
}
business/src/main/java/com/ycl/service/impl/CodingRulerServiceImpl.java
@@ -17,6 +17,7 @@
import com.ycl.framework.utils.PageUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.stream.Collectors;
@@ -150,4 +151,30 @@
        baseMapper.updateById(codingRuler);
        return Result.ok(msg);
    }
    /**
     * 获得启动的赋码规则
     * @return 存放的字段 yellowRuler启用的黄码规则,redRuler启用的红码规则
     */
    @Override
    public Result getStartRuler() {
        Result result = new Result();
        //获得启用的赋码规则
        List<CodingRulerVO> codingRulerVOS = codingRulerMapper.getStartRuler();
        if (CollectionUtils.isEmpty(codingRulerVOS)){
            log.error("未启用赋码规则");
            return result;
        }
        CodingRulerVO yellowRuler = null;
        CodingRulerVO redRuler = null;
        for (CodingRulerVO codingRulerVO : codingRulerVOS){
            if (CodingRulerCodeTypeEnum.YELLOW.getValue().equals(codingRulerVO.getProjectCodeType())){
                yellowRuler = codingRulerVO;
            }else if (CodingRulerCodeTypeEnum.RED.getValue().equals(codingRulerVO.getProjectCodeType())){
                redRuler = codingRulerVO;
            }
        }
        result.put("yellowRuler",yellowRuler);
        result.put("redRuler",redRuler);
        return result;
    }
}
business/src/main/java/com/ycl/service/impl/ProjectCodingStatusServiceImpl.java
New file
@@ -0,0 +1,121 @@
package com.ycl.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ycl.domain.entity.ProjectOvertimeTimes;
import com.ycl.domain.query.ProjectOvertimeTimesQuery;
import com.ycl.common.base.Result;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ycl.domain.form.ProjectOvertimeTimesForm;
import com.ycl.domain.vo.ProjectOvertimeTimesVO;
import com.ycl.mapper.ProjectOvertimeTimesMapper;
import com.ycl.service.ProjectOvertimeTimesService;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import com.ycl.framework.utils.PageUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;
import java.util.List;
import java.util.stream.Collectors;
/**
 *  服务实现类
 *
 * @author zxl
 * @since 2025-04-01
 */
@Service
@RequiredArgsConstructor
public class ProjectCodingStatusServiceImpl extends ServiceImpl<ProjectOvertimeTimesMapper, ProjectOvertimeTimes> implements ProjectOvertimeTimesService {
    private final ProjectOvertimeTimesMapper projectCodingStatusMapper;
    /**
     * 添加
     * @param form
     * @return
     */
    @Override
    public Result add(ProjectOvertimeTimesForm form) {
        ProjectOvertimeTimes entity = ProjectOvertimeTimesForm.getEntityByForm(form, null);
        baseMapper.insert(entity);
        return Result.ok("添加成功");
    }
    /**
     * 修改
     * @param form
     * @return
     */
    @Override
    public Result update(ProjectOvertimeTimesForm form) {
        ProjectOvertimeTimes entity = baseMapper.selectById(form.getId());
        // 为空抛IllegalArgumentException,做全局异常处理
        Assert.notNull(entity, "记录不存在");
        BeanUtils.copyProperties(form, entity);
        baseMapper.updateById(entity);
        return Result.ok("修改成功");
    }
    /**
     * 批量删除
     * @param ids
     * @return
     */
    @Override
    public Result remove(List<String> ids) {
        baseMapper.deleteBatchIds(ids);
        return Result.ok("删除成功");
    }
    /**
     * id删除
     * @param id
     * @return
     */
    @Override
    public Result removeById(String id) {
        baseMapper.deleteById(id);
        return Result.ok("删除成功");
    }
    /**
     * 分页查询
     * @param query
     * @return
     */
    @Override
    public Result page(ProjectOvertimeTimesQuery query) {
        IPage<ProjectOvertimeTimesVO> page = PageUtil.getPage(query, ProjectOvertimeTimesVO.class);
        baseMapper.getPage(page, query);
        return Result.ok().data(page.getRecords()).total(page.getTotal());
    }
    /**
     * 根据id查找
     * @param id
     * @return
     */
    @Override
    public Result detail(Integer id) {
        ProjectOvertimeTimesVO vo = baseMapper.getById(id);
        Assert.notNull(vo, "记录不存在");
        return Result.ok().data(vo);
    }
    /**
     * 列表
     * @return
     */
    @Override
    public Result all() {
        List<ProjectOvertimeTimes> entities = baseMapper.selectList(null);
        List<ProjectOvertimeTimesVO> vos = entities.stream()
                .map(entity -> ProjectOvertimeTimesVO.getVoByEntity(entity, null))
                .collect(Collectors.toList());
        return Result.ok().data(vos);
    }
}
business/src/main/java/com/ycl/service/impl/ProjectInfoServiceImpl.java
@@ -20,10 +20,7 @@
import com.ycl.common.core.domain.BaseEntity;
import com.ycl.common.core.domain.entity.SysDictData;
import com.ycl.common.enums.YesOrNo;
import com.ycl.common.enums.business.FileTypeEnum;
import com.ycl.common.enums.business.ImportanceTypeEnum;
import com.ycl.common.enums.business.ProjectCategoryEnum;
import com.ycl.common.enums.business.ProjectStatusEnum;
import com.ycl.common.enums.business.*;
import com.ycl.common.exception.base.BaseException;
import com.ycl.common.utils.CopyUtils;
import com.ycl.common.utils.DateUtils;
@@ -100,6 +97,7 @@
    private final ProjectUnitRegistrationInfoServiceImpl projectUnitRegistrationInfoServiceImpl;
    private final SysDeptMapper sysDeptMapper;
    private final ISysDictDataService dictDataService;
    private final ProcessLogService processLogService;
    /**
@@ -547,6 +545,12 @@
    public Map<String, Integer> countExceptionProject(IndexDTO indexDTO) {
        Map<String, Integer> map = new HashMap<>();
        map.put("processExceptionProject", 0);
        // 查询发生过容缺的项目数
        List<String> projectIds = new LambdaQueryChainWrapper<>(processLogService.getBaseMapper())
                .select(ProcessLog::getProjectId)
                .eq(ProcessLog::getEventType, ProcessLogEventTypeEnum.WAIT)
                .list().stream().map(ProcessLog::getProjectId).distinct().collect(Collectors.toList());
        map.put("hasWaitProjectNum", projectIds.size());
        return map;
    }
business/src/main/java/com/ycl/service/impl/ProjectProcessServiceImpl.java
@@ -13,6 +13,7 @@
import com.ycl.common.core.domain.entity.SysRole;
import com.ycl.common.core.domain.entity.SysUser;
import com.ycl.common.enums.business.*;
import com.ycl.common.utils.DateUtils;
import com.ycl.common.utils.SecurityUtils;
import com.ycl.constant.TaskTypeConstant;
import com.ycl.domain.entity.*;
@@ -305,8 +306,8 @@
        taskStatistics.setTimelyFinishedTaskNum(this.getTimelyTaskNum(projectProcess.getProcessInsId()));
        taskStatistics.setOvertimeTaskNum(this.getOvertimeTaskNum(projectProcess.getProcessInsId()));
        taskStatistics.setWillOvertimeTaskNum(this.getWillOvertimeTaskNum(projectProcess.getProcessInsId()));
        taskStatistics.setToleranceNum(this.getToleranceTask(projectProcess.getProcessInsId()));
//        taskStatistics.setCurrentTask(this.getCurrentNodeTaskList(projectProcess.getProcessInstanceId()));
        taskStatistics.setWaitTaskNum(this.getWaitTaskNum(projectProcess.getProcessInsId()));
        taskStatistics.setJumpTaskNum(this.getJumpTaskNum(projectProcess.getProcessInsId()));
        detail.setStatistics(taskStatistics);
        Result result = Result.ok();
@@ -321,7 +322,7 @@
     * @param processInsId
     * @return
     */
    private Long getToleranceTask(String processInsId){
    private Long getWaitTaskNum(String processInsId){
        // 查出容缺过的任务
        List<ProcessLog> allWaitTaskList = new LambdaQueryChainWrapper<>(processLogService.getBaseMapper())
                .eq(ProcessLog::getProcessInsId, processInsId)
@@ -331,7 +332,7 @@
        // 排除容缺后又完成的任务
        List<ProcessLog> finishedTaskList = new LambdaQueryChainWrapper<>(processLogService.getBaseMapper())
                .eq(ProcessLog::getProcessInsId, processInsId)
                .eq(ProcessLog::getEventType, ProcessLogEventTypeEnum.FINISHED)
                .in(ProcessLog::getEventType, ProcessLogEventTypeEnum.FINISHED, ProcessLogEventTypeEnum.REJECT)
                .list();
        List<String> finishedTaskIds = finishedTaskList.stream().map(ProcessLog::getTaskId).distinct().collect(Collectors.toList());
        // 得到未完成的容缺任务
@@ -339,7 +340,19 @@
        if (CollectionUtils.isEmpty(waitTaskIds)) {
            return 0L;
        }
        return Long.valueOf(waitTaskIds.size());
        // 容缺的任务都属于历史任务,只是需要补表单数据
        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInsId)
                .taskIds(waitTaskIds)
                .includeIdentityLinks()
                .orderByHistoricTaskInstanceStartTime()
                .desc()
                .list();
        hisTaskList = this.distinctHisTask(hisTaskList);
        // 排除运行时任务
        List<String> runTaskKeys = taskService.createTaskQuery().processInstanceId(processInsId).list().stream().map(Task::getTaskDefinitionKey).collect(Collectors.toList());
        hisTaskList = hisTaskList.stream().filter(his -> !runTaskKeys.contains(his.getTaskDefinitionKey())).collect(Collectors.toList());
        return Long.valueOf(hisTaskList.size());
    }
    @Override
@@ -363,6 +376,9 @@
                break;
            case TaskTypeConstant.WAIT:
                this.getWaitTask(query.getProjectId(), query.getProcessDefId(), projectProcess.getProcessInsId(), query.getTaskName(), (int) query.getCurrentPage(), (int) query.getPageSize(), ok);
                break;
            case TaskTypeConstant.JUMP:
                this.getJumpTask(query.getProjectId(), query.getProcessDefId(), projectProcess.getProcessInsId(), query.getTaskName(), (int) query.getCurrentPage(), (int) query.getPageSize(), ok);
                break;
            case TaskTypeConstant.REMAINING:
                this.getRemainingTask(query.getProjectId(), query.getProcessDefId(), projectProcess.getProcessInsId(), query.getTaskName(), (int) query.getCurrentPage(), (int) query.getPageSize(), ok);
@@ -420,7 +436,7 @@
                    Long overtime = getTime(processCoding.getRedTime());
                    long durationTime = 0l;
                    if (Objects.nonNull(processCoding.getStartTaskTime())) {
                        durationTime = ((new Date()).getTime() - processCoding.getStartTaskTime().getTime()) / 1000;
                        durationTime = DateUtils.getWorkingSed(processCoding.getStartTaskTime(), new Date());
                    }
                    if (overtime > durationTime) {
                        order.setNum((overtime - durationTime) / 3600);
@@ -1400,16 +1416,30 @@
     */
    private Long getTimelyTaskNum(String processInsId) {
        // 查出已完成的任务key
        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery()
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInsId)
                .finished()
                .list();
        hisTaskList = this.distinctHisTask(hisTaskList);
                .includeIdentityLinks()
                .orderByTaskCreateTime()
                .desc();
        List<HistoricTaskInstance> hisTaskList = query.list();
        if (CollectionUtils.isEmpty(hisTaskList)) {
            return 0L;
        }
        hisTaskList = this.distinctHisTask(hisTaskList);
        // 排除跳过、容缺的任务,因为这两个操作都会完成任务而产生历史任务
        List<String> jumpAndWaitTaskKeys = new LambdaQueryChainWrapper<>(processLogService.getBaseMapper())
                .eq(ProcessLog::getProcessInsId, processInsId)
                .in(ProcessLog::getEventType, ProcessLogEventTypeEnum.JUMP, ProcessLogEventTypeEnum.WAIT) // TODO 如果运行时任务正好是被驳回的,需要想办法排除调被驳回的任务,不能直接在这加 ProcessLogEventTypeEnum.REJECT
                .list().stream().map(ProcessLog::getTaskDefKey).collect(Collectors.toList());
        List<String> runtimeTaskKey = taskService.createTaskQuery().processInstanceId(processInsId).list().stream().map(Task::getTaskDefinitionKey).collect(Collectors.toList());
        jumpAndWaitTaskKeys.addAll(runtimeTaskKey);
        hisTaskList = hisTaskList.stream().filter(hisTask -> jumpAndWaitTaskKeys.indexOf(hisTask.getTaskDefinitionKey()) == -1).collect(Collectors.toList());
        List<String> hisTaskKeys = hisTaskList.stream().map(HistoricTaskInstance::getTaskDefinitionKey).distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(hisTaskKeys)) {
            return 0L;
        }
        Map<String, HistoricTaskInstance> hisTaskMap = hisTaskList.stream().collect(Collectors.toMap(HistoricTaskInstance::getTaskDefinitionKey, his -> his));
        // 查出时间正常的任务key
        List<ProcessCoding> codeList = new LambdaQueryChainWrapper<>(processCodingService.getBaseMapper())
@@ -1451,21 +1481,37 @@
        int endNum = startNum + pageSize;
        // 查出已完成的任务key
        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery()
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInsId)
                .finished()
                .taskNameLike(taskName)
                .includeIdentityLinks()
                .orderByTaskCreateTime()
                .desc()
                .list();
                .desc();
        if (StringUtils.isNotBlank(taskName)) {
            query.taskNameLike(taskName);
        }
        List<HistoricTaskInstance> hisTaskList = query.list();
        if (CollectionUtils.isEmpty(hisTaskList)) {
            result.total(0);
            return new ArrayList<>();
        }
        hisTaskList = this.distinctHisTask(hisTaskList);
        // 排除跳过、容缺的任务,因为这两个操作都会完成任务而产生历史任务
        List<String> jumpAndWaitTaskKeys = new LambdaQueryChainWrapper<>(processLogService.getBaseMapper())
                .eq(ProcessLog::getProcessInsId, processInsId)
                .eq(ProcessLog::getProjectId, projectId)
                .in(ProcessLog::getEventType, ProcessLogEventTypeEnum.JUMP, ProcessLogEventTypeEnum.WAIT)
                .list().stream().map(ProcessLog::getTaskDefKey).collect(Collectors.toList());
        List<String> runtimeTaskKey = taskService.createTaskQuery().processInstanceId(processInsId).list().stream().map(Task::getTaskDefinitionKey).collect(Collectors.toList());
        jumpAndWaitTaskKeys.addAll(runtimeTaskKey);
        hisTaskList = hisTaskList.stream().filter(hisTask -> jumpAndWaitTaskKeys.indexOf(hisTask.getTaskDefinitionKey()) == -1).collect(Collectors.toList());
        List<String> hisTaskKeys = hisTaskList.stream().map(HistoricTaskInstance::getTaskDefinitionKey).distinct().collect(Collectors.toList());
        Map<String, HistoricTaskInstance> hisTaskMap = this.distinctHisTask(hisTaskList).stream().collect(Collectors.toMap(HistoricTaskInstance::getTaskDefinitionKey, his -> his));
        if (CollectionUtils.isEmpty(hisTaskKeys)) {
            result.total(0);
            return new ArrayList<>();
        }
        Map<String, HistoricTaskInstance> hisTaskMap = hisTaskList.stream().collect(Collectors.toMap(HistoricTaskInstance::getTaskDefinitionKey, his -> his));
        // 查出时间正常的任务key
        List<ProcessCoding> codeList = new LambdaQueryChainWrapper<>(processCodingService.getBaseMapper())
                .eq(ProcessCoding::getProcessInsId, processInsId)
@@ -1571,6 +1617,81 @@
    }
    /**
     * 设置历史任务信息
     *
     * @param hisTask
     * @param vo
     * @param projectId
     * @param processInsId
     */
    private void setHisTaskInfo(HistoricTaskInstance hisTask, CustomerTaskVO vo, String projectId, String processInsId) {
        // 查出流程
        ProcessInstance process = runtimeService.createProcessInstanceQuery().processInstanceId(processInsId).singleResult();
        String deployId = "";
        String processName = "";
        String processDefinitionId = "";
        if (Objects.nonNull(process)) {
            deployId = process.getDeploymentId();
            processName = process.getProcessDefinitionName();
            processDefinitionId = process.getProcessDefinitionId();
        } else {
            HistoricProcessInstance hisProcess = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInsId).singleResult();
            deployId = hisProcess.getDeploymentId();
            processName = hisProcess.getProcessDefinitionName();
            processDefinitionId = hisProcess.getProcessDefinitionId();
        }
        vo.setProcessInsId(processInsId);
        vo.setProcessDefId(processDefinitionId);
        vo.setDeployId(deployId);
        vo.setTaskName(hisTask.getName());
        vo.setProcessName(processName);
        // 一个任务可能有多个候选人/组,所以需要使用list
        List<Long> handlerIds = new ArrayList<>(2);
        List<String> handlerNames = new ArrayList<>(2);
        List<Long> handlerUnitIds = new ArrayList<>(2);
        List<String> handlerUnitNames = new ArrayList<>(2);
        List<String> promoterNames = new ArrayList<>(2);
        List<String> promoterUnitNames = new ArrayList<>(2);
        vo.setHandlerId(handlerIds);
        vo.setHandlerName(handlerNames);
        vo.setHandlerUnitId(handlerUnitIds);
        vo.setHandlerUnitName(handlerUnitNames);
        vo.setPromoterName(promoterNames);
        vo.setPromoterUnitName(promoterUnitNames);
        List<UserTask> targetUserTasks = this.getAllUserTaskElement(processDefinitionId).stream().filter(userTask -> hisTask.getTaskDefinitionKey().equals(userTask.getId())).collect(Collectors.toList());
        UserTask userTask = null;
        if (! CollectionUtils.isEmpty(targetUserTasks)) {
            userTask = targetUserTasks.get(0);
        }
        this.setCandidateInfo(userTask, vo, projectId, processInsId);
        vo.setTaskStatus(TaskStatusEnum.FINISHED);
        // 如果是已完成的,信息需要单独赋值
        vo.setTaskId(hisTask.getId());
        vo.setExecutionId(hisTask.getExecutionId());
        vo.setCreateTime(hisTask.getStartTime());
        // 查询实际处理人
        if (StringUtils.isNotBlank(hisTask.getAssignee())) {
            long handlerUserId = Long.parseLong(hisTask.getAssignee());
            SysUser handlerUser = sysUserService.selectUserById(handlerUserId);
            if (Objects.nonNull(handlerUser)) {
                vo.getHandlerId().add(handlerUserId);
                vo.getHandlerName().add(this.getUserShowName(handlerUser));
                if (Objects.nonNull(handlerUser.getDept())) {
                    vo.getHandlerUnitId().add(handlerUser.getDept().getDeptId());
                    vo.getHandlerUnitName().add(handlerUser.getDept().getDeptName());
                }
            }
        }
        vo.setTaskDefinitionKey(hisTask.getTaskDefinitionKey());
        this.distinctVo(vo);
    }
    /**
     * 用户名称后面跟电话号码
     *
     * @param user
@@ -1619,36 +1740,16 @@
     * @return
     */
    private Long getOvertimeTaskNum(String processInsId) {
        // 查出运行在的任务key
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInsId).list();
        if (CollectionUtils.isEmpty(taskList)) {
        // 查出已超时的任务id
        List<ProcessCoding> overtimeRecords = new LambdaQueryChainWrapper<>(processCodingService.getBaseMapper())
                .eq(ProcessCoding::getProcessInsId, processInsId)
                .eq(ProcessCoding::getOvertimeStatus, ProcessOverTimeConstants.OVERTIME)
                .orderByDesc(ProcessCoding::getGmtCreate)
                .list();
        if (CollectionUtils.isEmpty(overtimeRecords)) {
            return 0L;
        }
        List<String> taskKeys = taskList.stream().map(Task::getTaskDefinitionKey).distinct().collect(Collectors.toList());
        Map<String, Task> taskMap = taskList.stream().collect(Collectors.toMap(Task::getTaskDefinitionKey, his -> his));
        // 查出数据库的任务key
        List<ProcessCoding> codeList = new LambdaQueryChainWrapper<>(processCodingService.getBaseMapper())
                .eq(ProcessCoding::getProcessInsId, processInsId)
                .in(ProcessCoding::getTaskDefKey, taskKeys)
                .list();
        List<Task> tList = new ArrayList<>();
        // 判断
        for (String key : taskMap.keySet()) {
            List<ProcessCoding> targetProcessCodings = codeList.stream().filter(code -> key.equals(code.getTaskDefKey())).collect(Collectors.toList());
            // 如果已完成的任务没从数据库查找出来,说明该任务没配置赋码等时间,直接设置为按时完成
            if (CollectionUtils.isEmpty(targetProcessCodings)) {
                tList.add(taskMap.get(key));
            } else {
                // 按照时间降序排列
                targetProcessCodings.sort(Comparator.comparing(ProcessCoding::getGmtCreate).reversed());
                ProcessCoding latestProjectProcess = targetProcessCodings.get(0);
                if (Objects.nonNull(latestProjectProcess) && ProcessOverTimeConstants.OVERTIME.equals(latestProjectProcess.getOvertimeStatus())) {
                    tList.add(taskMap.get(key));
                }
            }
        }
        return Long.valueOf(taskList.size());
        return Long.valueOf(overtimeRecords.size());
    }
    /**
@@ -1667,63 +1768,108 @@
        int startNum = pageSize * (pageNum - 1);
        int endNum = startNum + pageSize;
        // 查出运行在的任务key
        List<Task> taskList = new ArrayList<>(12);
        if (StringUtils.isNotBlank(taskName)) {
            taskList = taskService.createTaskQuery().processInstanceId(processInsId).taskNameLike(taskName).orderByTaskCreateTime().desc().list();
        } else {
            taskList = taskService.createTaskQuery().processInstanceId(processInsId).orderByTaskCreateTime().desc().list();
        }
        if (CollectionUtils.isEmpty(taskList)) {
        // 查出已超时的任务id
        List<ProcessCoding> overtimeRecords = new LambdaQueryChainWrapper<>(processCodingService.getBaseMapper())
                .eq(ProcessCoding::getProcessInsId, processInsId)
                .eq(ProcessCoding::getOvertimeStatus, ProcessOverTimeConstants.OVERTIME)
                .orderByDesc(ProcessCoding::getGmtCreate)
                .list();
        if (CollectionUtils.isEmpty(overtimeRecords)) {
            result.total(0);
            return new ArrayList<>();
        }
        List<String> taskKeys = taskList.stream().map(Task::getTaskDefinitionKey).distinct().collect(Collectors.toList());
        Map<String, Task> taskMap = taskList.stream().collect(Collectors.toMap(Task::getTaskDefinitionKey, his -> his));
        // 查出数据库的任务key
        List<ProcessCoding> codeList = new LambdaQueryChainWrapper<>(processCodingService.getBaseMapper())
                .eq(ProcessCoding::getProcessInsId, processInsId)
                .in(ProcessCoding::getTaskDefKey, taskKeys)
                .list();
        List<Task> tList = new ArrayList<>();
        // 判断
        for (String key : taskMap.keySet()) {
            List<ProcessCoding> targetProcessCodings = codeList.stream().filter(code -> key.equals(code.getTaskDefKey())).collect(Collectors.toList());
            // 如果已完成的任务没从数据库查找出来,说明该任务没配置赋码等时间,直接设置为按时完成
            if (CollectionUtils.isEmpty(targetProcessCodings)) {
                tList.add(taskMap.get(key));
            } else {
                // 按照时间降序排列
                targetProcessCodings.sort(Comparator.comparing(ProcessCoding::getGmtCreate).reversed());
                ProcessCoding latestProjectProcess = targetProcessCodings.get(0);
                if (Objects.nonNull(latestProjectProcess) && ProcessOverTimeConstants.OVERTIME.equals(latestProjectProcess.getOvertimeStatus())) {
                    tList.add(taskMap.get(key));
                }
            }
        }
        if (startNum >= tList.size()) {
        if (startNum >= overtimeRecords.size()) {
            // 如果起始索引超出了列表的大小,返回一个空列表
            return new ArrayList<>();
        }
        result.total(tList.size());
        int end = Math.min(endNum, tList.size());
        List<Task> pageTaskList = tList.subList(startNum, end);
        List<String> taskDefs = pageTaskList.stream().map(Task::getTaskDefinitionKey).collect(Collectors.toList());
        Map<String, Task> keyMap = pageTaskList.stream().collect(Collectors.toMap(Task::getTaskDefinitionKey, his -> his));
        result.total(overtimeRecords.size());
        int end = Math.min(endNum, overtimeRecords.size());
        List<ProcessCoding> pageOvertimeRecords = overtimeRecords.subList(startNum, end);
        // 得到目标任务对应的定义
        List<UserTask> finishedUserTaskElement = this.getAllUserTaskElement(processDefinitionId).stream().filter(el -> taskDefs.contains(el.getId())).collect(Collectors.toList());
        // 查询任务相关信息
        List<CustomerTaskVO> vos = finishedUserTaskElement.stream().map(userTask -> {
            Task task = keyMap.get(userTask.getId());
        List<CustomerTaskVO> vos = pageOvertimeRecords.stream().map(record -> {
            CustomerTaskVO vo = new CustomerTaskVO();
            this.setRuntimeTaskInfo(task, vo, projectId);
            return vo;
        }).collect(Collectors.toList());
            Task task = taskService.createTaskQuery().processInstanceId(processInsId).taskId(record.getTaskId()).singleResult();
            if (Objects.nonNull(task)) {
                this.setRuntimeTaskInfo(task, vo, projectId);
                vo.setTaskStatus(TaskStatusEnum.OVER_TIME);
                return vo;
            } else {
                List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
                        .finished()
                        .processInstanceId(processInsId)
                        .taskId(record.getTaskId())
                        .orderByHistoricTaskInstanceStartTime()
                        .desc()
                        .list();
                if (CollectionUtils.isEmpty(list)) {
                    return null;
                }
                List<HistoricTaskInstance> hisTask = this.distinctHisTask(list);
                this.setHisTaskInfo(hisTask.get(0), vo, projectId, processInsId);
                vo.setTaskStatus(TaskStatusEnum.OVER_TIME_FINISHED);
                return vo;
            }
        }).filter(Objects::nonNull).collect(Collectors.toList());
        result.data(vos);
        return vos;
//        // 查出运行在的任务key
//        List<Task> taskList = new ArrayList<>(12);
//        if (StringUtils.isNotBlank(taskName)) {
//            taskList = taskService.createTaskQuery().processInstanceId(processInsId).taskNameLike(taskName).orderByTaskCreateTime().desc().list();
//        } else {
//            taskList = taskService.createTaskQuery().processInstanceId(processInsId).orderByTaskCreateTime().desc().list();
//        }
//        if (CollectionUtils.isEmpty(taskList)) {
//            result.total(0);
//            return new ArrayList<>();
//        }
//        List<String> taskKeys = taskList.stream().map(Task::getTaskDefinitionKey).distinct().collect(Collectors.toList());
//        Map<String, Task> taskMap = taskList.stream().collect(Collectors.toMap(Task::getTaskDefinitionKey, his -> his));
//        // 查出数据库的任务key
//        List<ProcessCoding> codeList = new LambdaQueryChainWrapper<>(processCodingService.getBaseMapper())
//                .eq(ProcessCoding::getProcessInsId, processInsId)
//                .in(ProcessCoding::getTaskDefKey, taskKeys)
//                .list();
//        List<Task> tList = new ArrayList<>();
//        // 判断
//        for (String key : taskMap.keySet()) {
//            List<ProcessCoding> targetProcessCodings = codeList.stream().filter(code -> key.equals(code.getTaskDefKey())).collect(Collectors.toList());
//            // 如果已完成的任务没从数据库查找出来,说明该任务没配置赋码等时间,直接设置为按时完成
//            if (CollectionUtils.isEmpty(targetProcessCodings)) {
//                tList.add(taskMap.get(key));
//            } else {
//                // 按照时间降序排列
//                targetProcessCodings.sort(Comparator.comparing(ProcessCoding::getGmtCreate).reversed());
//                ProcessCoding latestProjectProcess = targetProcessCodings.get(0);
//                if (Objects.nonNull(latestProjectProcess) && ProcessOverTimeConstants.OVERTIME.equals(latestProjectProcess.getOvertimeStatus())) {
//                    tList.add(taskMap.get(key));
//                }
//            }
//        }
//
//        if (startNum >= tList.size()) {
//            // 如果起始索引超出了列表的大小,返回一个空列表
//            return new ArrayList<>();
//        }
//        result.total(tList.size());
//        int end = Math.min(endNum, tList.size());
//        List<Task> pageTaskList = tList.subList(startNum, end);
//        List<String> taskDefs = pageTaskList.stream().map(Task::getTaskDefinitionKey).collect(Collectors.toList());
//        Map<String, Task> keyMap = pageTaskList.stream().collect(Collectors.toMap(Task::getTaskDefinitionKey, his -> his));
//
//        // 得到目标任务对应的定义
//        List<UserTask> finishedUserTaskElement = this.getAllUserTaskElement(processDefinitionId).stream().filter(el -> taskDefs.contains(el.getId())).collect(Collectors.toList());
//
//        // 查询任务相关信息
//        List<CustomerTaskVO> vos = finishedUserTaskElement.stream().map(userTask -> {
//            Task task = keyMap.get(userTask.getId());
//            CustomerTaskVO vo = new CustomerTaskVO();
//            this.setRuntimeTaskInfo(task, vo, projectId);
//            return vo;
//        }).collect(Collectors.toList());
//        result.data(vos);
//        return vos;
    }
    /**
@@ -1991,6 +2137,190 @@
    }
    /**
     * 获取跳过任务
     *
     * @param processInsId
     * @return
     */
    private Long getJumpTaskNum(String processInsId) {
        // 查出跳过的任务
        List<ProcessLog> allJumpTaskList = new LambdaQueryChainWrapper<>(processLogService.getBaseMapper())
                .eq(ProcessLog::getProcessInsId, processInsId)
                .eq(ProcessLog::getEventType, ProcessLogEventTypeEnum.JUMP)
                .orderByDesc(ProcessLog::getGmtCreate)
                .list();
        // 排除驳回的
        List<String> rejectTaskIds = new LambdaQueryChainWrapper<>(processLogService.getBaseMapper())
                .select(ProcessLog::getTaskId)
                .eq(ProcessLog::getProcessInsId, processInsId)
                .eq(ProcessLog::getEventType, ProcessLogEventTypeEnum.REJECT)
                .orderByDesc(ProcessLog::getGmtCreate)
                .list().stream().map(ProcessLog::getTaskId).collect(Collectors.toList());
        List<String> jumpTaskIds = allJumpTaskList.stream().map(ProcessLog::getTaskId).collect(Collectors.toList());
        jumpTaskIds.removeAll(rejectTaskIds);
        if(CollectionUtils.isEmpty(jumpTaskIds)) {
            return 0L;
        }
        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInsId)
                .finished()
                .taskIds(jumpTaskIds)
                .includeIdentityLinks()
                .orderByHistoricTaskInstanceStartTime()
                .desc()
                .list();
        hisTaskList = this.distinctHisTask(hisTaskList);
        // 排除运行时任务
        List<String> runTaskKeys = taskService.createTaskQuery().processInstanceId(processInsId).list().stream().map(Task::getTaskDefinitionKey).collect(Collectors.toList());
        hisTaskList = hisTaskList.stream().filter(his -> !runTaskKeys.contains(his.getTaskDefinitionKey())).collect(Collectors.toList());
        return Long.valueOf(hisTaskList.size());
    }
    /**
     * 获取跳过任务
     *
     * @param projectId
     * @param processDefinitionId
     * @param processInsId
     * @param taskName
     * @param pageNum
     * @param pageSize
     * @param result
     * @return
     */
    private List<CustomerTaskVO> getJumpTask(String projectId,
                                             String processDefinitionId,
                                             String processInsId,
                                             String taskName,
                                             Integer pageNum,
                                             Integer pageSize,
                                             Result result) {
        // 查出跳过的任务
        List<ProcessLog> allJumpTaskList = new LambdaQueryChainWrapper<>(processLogService.getBaseMapper())
                .eq(ProcessLog::getProcessInsId, processInsId)
                .eq(ProcessLog::getEventType, ProcessLogEventTypeEnum.JUMP)
                .like(StringUtils.isNotBlank(taskName), ProcessLog::getTaskName, taskName)
                .orderByDesc(ProcessLog::getGmtCreate)
                .list();
        // 排除驳回的
        List<String> rejectTaskIds = new LambdaQueryChainWrapper<>(processLogService.getBaseMapper())
                .select(ProcessLog::getTaskId)
                .eq(ProcessLog::getProcessInsId, processInsId)
                .eq(ProcessLog::getEventType, ProcessLogEventTypeEnum.REJECT)
                .like(StringUtils.isNotBlank(taskName), ProcessLog::getTaskName, taskName)
                .orderByDesc(ProcessLog::getGmtCreate)
                .list().stream().map(ProcessLog::getTaskId).collect(Collectors.toList());
        List<String> jumpTaskIds = allJumpTaskList.stream().map(ProcessLog::getTaskId).collect(Collectors.toList());
        jumpTaskIds.removeAll(rejectTaskIds);
        if(CollectionUtils.isEmpty(jumpTaskIds)) {
            result.total(0l);
            return new ArrayList<>();
        }
        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInsId)
                .finished()
                .taskIds(jumpTaskIds)
                .includeIdentityLinks()
                .orderByHistoricTaskInstanceStartTime()
                .desc()
                .list();
        hisTaskList = this.distinctHisTask(hisTaskList);
        // 排除运行时任务
        List<String> runTaskKeys = taskService.createTaskQuery().processInstanceId(processInsId).list().stream().map(Task::getTaskDefinitionKey).collect(Collectors.toList());
        hisTaskList = hisTaskList.stream().filter(his -> !runTaskKeys.contains(his.getTaskDefinitionKey())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(hisTaskList)) {
            result.total(0l);
            return new ArrayList<>();
        }
        int startNum = pageSize * (pageNum - 1);
        int endNum = startNum + pageSize;
        if (startNum >= hisTaskList.size()) {
            result.total(0l);
            // 如果起始索引超出了列表的大小,返回一个空列表
            return new ArrayList<>();
        }
        result.total(hisTaskList.size());
        int end = Math.min(endNum, hisTaskList.size());
        List<HistoricTaskInstance> targetTaskList = hisTaskList.subList(startNum, end);
        // 转换成VO
        // 得到目标任务对应的定义
        List<String> taskDefs = targetTaskList.stream().map(HistoricTaskInstance::getTaskDefinitionKey).collect(Collectors.toList());
        Map<String, HistoricTaskInstance> keyMap = targetTaskList.stream().collect(Collectors.toMap(HistoricTaskInstance::getTaskDefinitionKey, his -> his));
        List<UserTask> finishedUserTaskElement = this.getAllUserTaskElement(processDefinitionId).stream().filter(el -> taskDefs.contains(el.getId())).collect(Collectors.toList());
        // 查出流程
        ProcessInstance process = runtimeService.createProcessInstanceQuery().processInstanceId(processInsId).singleResult();
        String deployId = "";
        String processName = "";
        if (Objects.nonNull(process)) {
            deployId = process.getDeploymentId();
            processName = process.getProcessDefinitionName();
        } else {
            HistoricProcessInstance hisProcess = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInsId).singleResult();
            deployId = hisProcess.getDeploymentId();
            processName = hisProcess.getProcessDefinitionName();
        }
        String finalDeployId = deployId;
        String finalProcessName = processName;
        List<CustomerTaskVO> vos = finishedUserTaskElement.stream().map(userTask -> {
            CustomerTaskVO vo = new CustomerTaskVO();
            vo.setProcessInsId(processInsId);
            vo.setProcessDefId(processDefinitionId);
            vo.setDeployId(finalDeployId);
            vo.setTaskName(userTask.getName());
            vo.setProcessName(finalProcessName);
            // 一个任务可能有多个候选人/组,所以需要使用list
            List<Long> handlerIds = new ArrayList<>(2);
            List<String> handlerNames = new ArrayList<>(2);
            List<Long> handlerUnitIds = new ArrayList<>(2);
            List<String> handlerUnitNames = new ArrayList<>(2);
            List<String> promoterNames = new ArrayList<>(2);
            List<String> promoterUnitNames = new ArrayList<>(2);
            vo.setHandlerId(handlerIds);
            vo.setHandlerName(handlerNames);
            vo.setHandlerUnitId(handlerUnitIds);
            vo.setHandlerUnitName(handlerUnitNames);
            vo.setPromoterName(promoterNames);
            vo.setPromoterUnitName(promoterUnitNames);
            this.setCandidateInfo(userTask, vo, projectId, processInsId);
            HistoricTaskInstance hisTask = keyMap.get(userTask.getId());
            if (Objects.nonNull(hisTask)) {
                vo.setTaskStatus(TaskStatusEnum.JUMP);
                // 如果是已完成的,信息需要单独赋值
                vo.setTaskId(hisTask.getId());
                vo.setExecutionId(hisTask.getExecutionId());
                vo.setCreateTime(hisTask.getStartTime());
                // 查询实际处理人
                if (StringUtils.isNotBlank(hisTask.getAssignee())) {
                    long handlerUserId = Long.parseLong(hisTask.getAssignee());
                    SysUser handlerUser = sysUserService.selectUserById(handlerUserId);
                    if (Objects.nonNull(handlerUser)) {
                        vo.getHandlerId().add(handlerUserId);
                        vo.getHandlerName().add(this.getUserShowName(handlerUser));
                        if (Objects.nonNull(handlerUser.getDept())) {
                            vo.getHandlerUnitId().add(handlerUser.getDept().getDeptId());
                            vo.getHandlerUnitName().add(handlerUser.getDept().getDeptName());
                        }
                    }
                }
                this.setHandler(vo, hisTask.getIdentityLinks());
                vo.setTaskDefinitionKey(hisTask.getTaskDefinitionKey());
            }
            this.distinctVo(vo);
            return vo;
        }).collect(Collectors.toList());
        result.data(vos);
        return vos;
    }
    /**
     * 容缺任务分页
     *
     * @param projectId
@@ -2020,7 +2350,7 @@
        // 排除容缺后又完成的任务
        List<ProcessLog> finishedTaskList = new LambdaQueryChainWrapper<>(processLogService.getBaseMapper())
                .eq(ProcessLog::getProcessInsId, processInsId)
                .eq(ProcessLog::getEventType, ProcessLogEventTypeEnum.FINISHED)
                .in(ProcessLog::getEventType, ProcessLogEventTypeEnum.FINISHED, ProcessLogEventTypeEnum.REJECT)
                .list();
        List<String> finishedTaskIds = finishedTaskList.stream().map(ProcessLog::getTaskId).distinct().collect(Collectors.toList());
        // 得到未完成的容缺任务
@@ -2038,7 +2368,9 @@
                .desc()
                .list();
        hisTaskList = this.distinctHisTask(hisTaskList);
        // 排除运行时任务
        List<String> runTaskKeys = taskService.createTaskQuery().processInstanceId(processInsId).list().stream().map(Task::getTaskDefinitionKey).collect(Collectors.toList());
        hisTaskList = hisTaskList.stream().filter(his -> !runTaskKeys.contains(his.getTaskDefinitionKey())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(hisTaskList)) {
            result.total(0l);
            return new ArrayList<>();
@@ -2160,8 +2492,14 @@
        // 排除进行中的任务和已完成的任务
        List<String> runTaskKeyList = taskService.createTaskQuery().processInstanceId(processInsId).list().stream().map(Task::getTaskDefinitionKey).collect(Collectors.toList());
        List<String> finishedTaskKeyList = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInsId).finished().list().stream().map(HistoricTaskInstance::getTaskDefinitionKey).distinct().collect(Collectors.toList());
        allUserTaskElement = allUserTaskElement.stream().filter(el -> !runTaskKeyList.contains(el.getId()) && !finishedTaskKeyList.contains(el.getId())).collect(Collectors.toList());
        List<String> finishedTaskKeyList = new LambdaQueryChainWrapper<>(processLogService.getBaseMapper())
                .select(ProcessLog::getTaskDefKey)
                .eq(ProcessLog::getProcessInsId, processInsId)
                .in(ProcessLog::getEventType, ProcessLogEventTypeEnum.FINISHED, ProcessLogEventTypeEnum.JUMP, ProcessLogEventTypeEnum.WAIT)
                .list().stream().map(ProcessLog::getTaskDefKey).collect(Collectors.toList());
        finishedTaskKeyList.addAll(runTaskKeyList);
//        List<String> finishedTaskKeyList = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInsId).finished().list().stream().map(HistoricTaskInstance::getTaskDefinitionKey).distinct().collect(Collectors.toList());
        allUserTaskElement = allUserTaskElement.stream().filter(el -> finishedTaskKeyList.indexOf(el.getId()) == -1).collect(Collectors.toList());
        // 模拟模糊查询
        if (StringUtils.isNotBlank(taskName)) {
            allUserTaskElement = allUserTaskElement.stream().filter(taskEl -> taskEl.getName().contains(taskName)).collect(Collectors.toList());
@@ -2318,14 +2656,18 @@
            // 运行时未找到流程,说明流程已经结束了
            return 0L;
        }
        // 查出已完成的任务,用总任务数-已完成的就得到剩余事项
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(process.getProcessInstanceId())
                .finished()
                .list();
        long num = list.stream().map(HistoricTaskInstance::getTaskDefinitionKey).distinct().count();
        List<UserTask> allUserTaskElement = this.getAllUserTaskElement(processDefinitionId);
        return totalNum - num;
        // 排除进行中的任务和已完成的任务
        List<String> runTaskKeyList = taskService.createTaskQuery().processInstanceId(processInsId).list().stream().map(Task::getTaskDefinitionKey).collect(Collectors.toList());
        List<String> finishedTaskKeyList = new LambdaQueryChainWrapper<>(processLogService.getBaseMapper())
                .select(ProcessLog::getTaskDefKey)
                .eq(ProcessLog::getProcessInsId, processInsId)
                .in(ProcessLog::getEventType, ProcessLogEventTypeEnum.FINISHED, ProcessLogEventTypeEnum.JUMP, ProcessLogEventTypeEnum.WAIT)
                .list().stream().map(ProcessLog::getTaskDefKey).collect(Collectors.toList());
        finishedTaskKeyList.addAll(runTaskKeyList);
        allUserTaskElement = allUserTaskElement.stream().filter(el -> finishedTaskKeyList.indexOf(el.getId()) == -1).collect(Collectors.toList());
        return Long.valueOf(allUserTaskElement.size());
    }
    /**
business/src/main/java/com/ycl/task/CodingTask.java
New file
@@ -0,0 +1,216 @@
package com.ycl.task;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.ycl.common.base.Result;
import com.ycl.common.enums.business.CodingRulerIntervalTypeEnum;
import com.ycl.common.enums.business.ProjectProcessTypeEnum;
import com.ycl.domain.entity.*;
import com.ycl.domain.vo.CodingRulerVO;
import com.ycl.domain.vo.ProcessCodingVO;
import com.ycl.domain.vo.ProcessOvertimeTimesVO;
import com.ycl.factory.FlowServiceFactory;
import com.ycl.mapper.*;
import com.ycl.service.CodingRulerService;
import com.ycl.service.ProjectOvertimeTimesService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
@Slf4j
@RequiredArgsConstructor
@Component("codingTask")
public class CodingTask extends FlowServiceFactory {
    private final ProjectOvertimeTimesMapper projectOvertimeTimesMapper;
    private final ProjectOvertimeTimesService projectOvertimeTimesService;
    private final ProjectProcessMapper projectProcessMapper;
    private final ProjectInfoMapper projectInfoMapper;
    private final CodingRulerService codingRulerService;
    private final ProcessCodingMapper processCodingMapper;
    //计数项目超时任务数并更具赋码规则进行赋码
    public void codingTask(){
        log.info("开始计算项目并且赋码");
        //获得启用的赋码规则
        Result result = codingRulerService.getStartRuler();
        CodingRulerVO yellowRuler = (CodingRulerVO) result.get("yellowRuler");
        CodingRulerVO redRuler = (CodingRulerVO) result.get("redRuler");
        //没启用直接结束
        if (Objects.isNull(yellowRuler) && Objects.isNull(redRuler)) {
            log.error("未启用赋码规则");
            return;
        }
        //获得所有 超时任务数与流程实例id
        List<ProcessOvertimeTimesVO> processOvertimeTimesVOS = processCodingMapper.countProjectOvertimeTimes();
        if (!CollectionUtils.isEmpty(processOvertimeTimesVOS)){
            //将流程实例id转换为list
            List<String> processIds = processOvertimeTimesVOS.stream().map(ProcessOvertimeTimesVO::getProcessInsId).toList();
            //获得所有 更具流程定义id,流程相关项目id, key为流程id
            Map<String, ProjectProcess> projectMap = new LambdaQueryChainWrapper<>(projectProcessMapper)
                    .in(ProjectProcess::getProcessInsId, processIds)
                    .eq(ProjectProcess::getProjectType, ProjectProcessTypeEnum.PROJECT)
                    .list()
                    .stream()
                    .collect(Collectors.toMap(ProjectProcess::getProcessInsId, Function.identity()));
            //修改为黄码的项目 id
            List<String> yellowProjectCodingIds = new ArrayList<>();
            //修改为红码的项目 id
            List<String> redProjectCodingIds = new ArrayList<>();
            //新增的记录集合
            List<ProjectOvertimeTimes> addList = new ArrayList<>();
            for (ProcessOvertimeTimesVO processOvertimeTimesVO : processOvertimeTimesVOS) {
                //判断 项目类型为 ProjectProcessTypeEnum.PROJECT 并且超时任务数大于0
                if (processOvertimeTimesVO.getTaskOvertimeNum() != 0 && projectMap.containsKey(processOvertimeTimesVO.getProcessInsId())) {
                    ProjectOvertimeTimes projectOvertimeTimes = new ProjectOvertimeTimes();
                    projectOvertimeTimes.setProjectType(ProjectProcessTypeEnum.PROJECT.getValue());
                    projectOvertimeTimes.setProjectId(projectMap.get(processOvertimeTimesVO.getProcessInsId()).getProjectId());
                    projectOvertimeTimes.setProcessInsId(processOvertimeTimesVO.getProcessInsId());
                    projectOvertimeTimes.setTaskOvertimeNum(processOvertimeTimesVO.getTaskOvertimeNum());
                    //添加到新增集合内
                    addList.add(projectOvertimeTimes);
                    //检验赋码规则
                    checkCodingType(projectOvertimeTimes.getTaskOvertimeNum(),
                            projectOvertimeTimes.getProjectId(),
                            yellowProjectCodingIds,redProjectCodingIds,
                            yellowRuler,redRuler);
                }
            }
            if(!CollectionUtils.isEmpty(addList)){
                projectOvertimeTimesMapper.delAll();
                projectOvertimeTimesService.saveBatch(addList);
            }
            //修改项目赋码
            updateProjectCoding(redProjectCodingIds,"red");
            log.info("打印赋值为红码的项目id");
            for (String s : redProjectCodingIds){
                System.out.println(s);
            }
            log.info("打印赋值为红码的项目id完毕");
            updateProjectCoding(yellowProjectCodingIds,"yellow");
            log.info("打印赋值为黄码的项目id");
            for (String s : yellowProjectCodingIds){
                System.out.println(s);
            }
            log.info("打印赋值为黄码的项目id完毕");
        }
        log.info("结束计算项目并且赋码");
    }
    /**
     * 赋码
     *
     * @param projectIds 流程实例ID列表
     * @param coding             赋码值
     */
    private void updateProjectCoding(List<String> projectIds, String coding) {
        if (!CollectionUtils.isEmpty(projectIds)) {
            //将id 类型转换为Long
            List<Long> longList = projectIds.stream()
                    .map(Long::parseLong)
                    .collect(Collectors.toList());
            new LambdaUpdateChainWrapper<>(projectInfoMapper)
                    .in(ProjectInfo::getId, longList)
                    .set(ProjectInfo::getCoding, coding)
                    .update();
        }
    }
    /**
     *
     * @param overTimeNum 超时任务数
     * @param projectId 项目id
     * @param yellowProjectCodingIds 修改项目赋码为黄码的id集合
     * @param redProjectCodingIds 修改项目赋码为红码的id集合
     * @param yellowRuler 黄码规则
     * @param redRuler 红码规则
     */
    private void checkCodingType(Long overTimeNum,String projectId,
                                 List<String> yellowProjectCodingIds, List<String> redProjectCodingIds,
                                 CodingRulerVO yellowRuler,CodingRulerVO redRuler){
        //先判断红码,规则如果满足,则不执行黄码规则
        if (judgeState(overTimeNum, projectId, redProjectCodingIds, redRuler)) return;
        //判断是否满足黄码规则
        judgeState(overTimeNum, projectId, yellowProjectCodingIds, yellowRuler);
    }
    private boolean judgeState(Long overTimeNum, String projectId, List<String> projectCodingIds, CodingRulerVO ruler) {
        if (ruler != null){
            if (CodingRulerIntervalTypeEnum.Interval.getValue().equals(ruler.getIntervalType())){
                //区间情况
                boolean result = false;
                //判断满足左区间
                if (ruler.getLeftSymbol().equals("0")){
                    result = (overTimeNum > ruler.getLeftValue());
                }else if (ruler.getLeftSymbol().equals("1")){
                    result = (overTimeNum >= ruler.getLeftValue());
                }else if (ruler.getLeftSymbol().equals("2")){
                    result = (Objects.equals(overTimeNum, ruler.getLeftValue()));
                }
                //满足则判断满足右区间
                if (result){
                    if (ruler.getRightSymbol().equals("2")){
                        result = (Objects.equals(overTimeNum, ruler.getRightValue()));
                    }else if (ruler.getRightSymbol().equals("3")){
                        result = (overTimeNum < ruler.getRightValue());
                    }else if (ruler.getRightSymbol().equals("4")){
                        result = (overTimeNum <= ruler.getRightValue());
                    }
                }
                if (result){
                    //符合该赋码条件
                    projectCodingIds.add(projectId);
                    return true;
                }
            }
            else{
                //单区间情况
                boolean result = false;
                if (ruler.getLeftSymbol().equals("0")){
                    result = (overTimeNum > ruler.getLeftValue());
                }else if (ruler.getLeftSymbol().equals("1")){
                    result = (overTimeNum >= ruler.getLeftValue());
                }else if (ruler.getLeftSymbol().equals("2")){
                    result = (Objects.equals(overTimeNum, ruler.getLeftValue()));
                }else if (ruler.getLeftSymbol().equals("3")){
                    result = (overTimeNum < ruler.getLeftValue());
                }else if (ruler.getLeftSymbol().equals("4")){
                    result = (overTimeNum <= ruler.getLeftValue());
                }
                if (result){
                    //符合该赋码条件
                    projectCodingIds.add(projectId);
                    return true;
                }
            }
        }
        return false;
    }
}
business/src/main/java/com/ycl/task/FlowableTask.java
@@ -5,6 +5,7 @@
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.ycl.common.enums.business.ProcessLogEventTypeEnum;
import com.ycl.common.enums.business.ProjectProcessTypeEnum;
import com.ycl.common.utils.DateUtils;
import com.ycl.domain.entity.ProcessCoding;
import com.ycl.domain.entity.ProcessLog;
import com.ycl.domain.entity.ProjectInfo;
@@ -111,8 +112,10 @@
//                Long yellowTime = getTime(processCoding.getYellowTime());
//                Long overtime = getTime(processCoding.getOvertime());
                if (startTime == null) continue;
                //节点处理时间
                long durationTime = (now.getTime() - startTime.getTime()) / 1000;
                //节点处理时间,需排除节假日
                long durationTime = DateUtils.getWorkingSed(startTime, now);
                //TODO:减去流程挂起时长
                //减去节点挂起时长
business/src/main/resources/mapper/CodingRulerMapper.xml
@@ -13,11 +13,21 @@
        <result column="status" property="status"/>
    </resultMap>
    <select id="getStartRuler" resultMap="BaseResultMap">
        select
            TCR.project_code_type,
            TCR.interval_type,
            TCR.left_symbol,
            TCR.left_value,
            TCR.right_symbol,
            TCR.right_value,
            TCR.id,
            TCR.status
        FROM
            t_coding_ruler TCR
        WHERE
            TCR.status = 1 AND TCR.deleted = 0;
    </select>
    <select id="getById" resultMap="BaseResultMap">
        SELECT
business/src/main/resources/mapper/ProjectInfoMapper.xml
@@ -116,6 +116,9 @@
        LEFT JOIN sys_dept d ON d.dept_id = TPI.project_owner_unit
        <where>
            TPI.deleted = 0
            <if test="query.coding !=null and query.coding!=''">
                and TPI.coding = #{query.coding}
            </if>
            <if test="query.projectName !=null and query.projectName!=''">
                and TPI.project_name like concat('%',#{query.projectName},'%')
            </if>
business/src/main/resources/mapper/ProjectOvertimeTimesMapper.xml
New file
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ycl.mapper.ProjectOvertimeTimesMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.ycl.domain.vo.ProjectOvertimeTimesVO">
        <result column="task_overtime_num" property="taskOvertimeNum" />
        <result column="process_ins_id" property="processInsId" />
        <result column="project_id" property="projectId" />
        <result column="project_type" property="projectType" />
    </resultMap>
    <delete id="delAll">
        DELETE FROM t_project_overtime_times
    </delete>
    <select id="getById" resultMap="BaseResultMap">
        SELECT
            TPOT.task_overtime_num,
            TPOT.process_ins_id,
            TPOT.project_id,
            TPOT.id
        FROM
            t_project_overtime_times TPOT
        WHERE
            TPOT.id = #{id} AND TPOT.deleted = 0
    </select>
    <select id="getPage" resultMap="BaseResultMap">
        SELECT
            TPOT.task_overtime_num,
            TPOT.process_ins_id,
            TPOT.project_id,
            TPOT.id
        FROM
            t_project_overtime_times TPOT
        WHERE
            TPOT.deleted = 0
    </select>
</mapper>
common/src/main/java/com/ycl/common/enums/business/CodingRulerCodeTypeEnum.java
@@ -11,7 +11,7 @@
@Getter
public enum CodingRulerCodeTypeEnum {
    RED("red", "红码"),
    GREEN("green","绿码");
    YELLOW("yellow","黄码");
    @EnumValue
    @JsonValue
common/src/main/java/com/ycl/common/enums/business/CodingRulerSymbolTypeEnum.java
New file
@@ -0,0 +1,36 @@
package com.ycl.common.enums.business;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
@Getter
public enum CodingRulerSymbolTypeEnum {
    GT("0","大于"),
    GE("1","大于等于"),
    EQ("2","等于"),
    LT("3","小于"),
    LE("4","小于等于");
    @EnumValue
    @JsonValue
    private final String value;
    private final String desc;
    CodingRulerSymbolTypeEnum(String value, String desc) {
        this.value = value;
        this.desc = desc;
    }
    // 将枚举转换为 Map
    public static Map<String, String> getMap() {
        return Arrays.stream(CodingRulerSymbolTypeEnum.values())
                .collect(Collectors.toMap(CodingRulerSymbolTypeEnum::getValue, CodingRulerSymbolTypeEnum::getDesc));
    }
}
common/src/main/java/com/ycl/common/enums/business/TaskStatusEnum.java
@@ -17,6 +17,9 @@
    FINISHED("finished", "已完成"),
    WAIT("wait", "容缺"),
    HANGUP("HANGUP", "挂起"),
    JUMP("JUMP", "跳过"),
    OVER_TIME("OVER_TIME", "超时未完成"),
    OVER_TIME_FINISHED("OVER_TIME_FINISHED", "超时已完成"),
    ;
    private final String value;
common/src/main/java/com/ycl/common/utils/DateUtils.java
@@ -4,11 +4,7 @@
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.*;
import java.util.Date;
import java.util.HashSet;
import java.util.Objects;
@@ -80,6 +76,52 @@
        return workingHours;
    }
    /**
     * 获取两个时间内,除去周末周日以及法定节假日的 秒总数。
     * @param startDate
     * @param endDate
     * @return
     */
    public static long getWorkingSed(Date startDate,Date endDate){
        long workingHours = 0;
        long workingMill = 0;
        //转换日期格式
        LocalDateTime startTime = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        LocalDateTime endTime = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        if(startTime.isAfter(endTime)){
            throw new RuntimeException("开始时间不能晚于结束时间");
        }
        LocalDateTime varTime = startTime;
        //节假日日期(2025为列)
        Set<LocalDate> holidays = new HashSet<>();
        String[] holiday = isHoliday.split(",");
        for (String str : holiday) {
            String[] md = str.split("-");
            int month = Integer.parseInt(md[0]);
            int day = Integer.parseInt(md[1]);
            holidays.add(LocalDate.of(2025,month,day));
        }
        while (varTime.isBefore(endTime)){
            boolean isWorkDay = varTime.getDayOfWeek().getValue() < 6
                    && !holidays.contains(varTime.toLocalDate());
            long diffMill = endTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - varTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            // 判断是否是工作日(非周末 + 非节假日)
            if (isWorkDay){
                if (diffMill < 3600000) {
                    // 如果两个时间不到一小时差距
                    workingMill += diffMill;
                    break;
                }
                workingHours++;
                workingMill += 3600000;
            }
            varTime = varTime.plusHours(1);
        }
        return workingMill / 1000;
    }
    /**
     * 获取当前Date型日期
flowable/src/main/java/com/ycl/domain/vo/ProcessOvertimeTimesVO.java
New file
@@ -0,0 +1,18 @@
package com.ycl.domain.vo;
import com.ycl.system.domain.base.AbsVo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "响应数据", description = "响应数据")
public class ProcessOvertimeTimesVO extends AbsVo {
    /** 流程实例id */
    @ApiModelProperty("流程实例id")
    private String processInsId;
    /** 超时任务数 */
    @ApiModelProperty("超时任务数")
    private Long taskOvertimeNum;
}
flowable/src/main/java/com/ycl/mapper/ProcessCodingMapper.java
@@ -5,6 +5,7 @@
import com.ycl.domain.entity.ProcessCoding;
import com.ycl.domain.query.ProcessCodingQuery;
import com.ycl.domain.vo.ProcessCodingVO;
import com.ycl.domain.vo.ProcessOvertimeTimesVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -38,4 +39,10 @@
    void updateBatch(@Param("list") List<ProcessCoding> list);
    List<ProcessCoding> findByTaskId(@Param("taskId") String taskId);
    /**
     * 查询出每个流程实例id中超时的任务数,流程实例id
     * @return
     */
    List<ProcessOvertimeTimesVO> countProjectOvertimeTimes();
}
flowable/src/main/resources/mapper/ProcessCodingMapper.xml
@@ -14,6 +14,21 @@
        <result column="status" property="status" />
        <result column="overtime_status" property="overtimeStatus" />
    </resultMap>
    <resultMap id="processOvertimeTimesVO" type="com.ycl.domain.vo.ProcessOvertimeTimesVO">
        <id column="id" property="id"/>
        <result column="task_overtime_num" property="taskOvertimeNum"/>
        <result column="process_ins_id" property="processInsId"/>
    </resultMap>
    <select id="countProjectOvertimeTimes" resultMap="processOvertimeTimesVO">
        select TPC.id,
               TPC.process_ins_id,
               COUNT(TPC.overtime_status) as task_overtime_num
        FROM t_process_coding TPC
        where
                TPC.overtime_status = 'overtime'
        GROUP BY TPC.process_ins_id
    </select>
    <select id="getById" resultMap="BaseResultMap">