business/src/main/java/com/ycl/controller/WorkStationScheduleController.java
@@ -2,10 +2,18 @@ import com.ycl.common.group.Update; import com.ycl.common.group.Add; import com.ycl.domain.form.ProjectInfoForm; import com.ycl.domain.query.AuditHistoryQuery; import com.ycl.domain.query.ProjectInfoQuery; import com.ycl.domain.query.WaitTodoQuery; import com.ycl.service.AuditHistoryService; import com.ycl.service.IndexHomeService; import com.ycl.system.domain.base.AbsQuery; import org.springframework.validation.annotation.Validated; import org.springframework.security.access.prepost.PreAuthorize; import lombok.RequiredArgsConstructor; import java.util.List; import java.util.Map; import javax.validation.constraints.NotEmpty; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -29,6 +37,15 @@ public class WorkStationScheduleController { private final WorkStationScheduleService workStationScheduleService; private final IndexHomeService indexHomeService; private final AuditHistoryService auditHistoryService; @GetMapping("/auditHistoryPage") public Result page(AuditHistoryQuery query){ return auditHistoryService.page(query); } @PostMapping @ApiOperation(value = "添加", notes = "添加") @@ -54,10 +71,10 @@ return workStationScheduleService.remove(ids); } @GetMapping("/page") @GetMapping("/listByDate") @ApiOperation(value = "分页", notes = "分页") public Result page(WorkStationScheduleQuery query) { return workStationScheduleService.page(query); public Result getListByDate(WorkStationScheduleQuery query) { return workStationScheduleService.listByDate(query); } @GetMapping("/{id}") @@ -81,4 +98,23 @@ public Result countAchievements(){ return workStationScheduleService.countAchievements(); } /** * 获得个人项目列表 * @return */ @GetMapping("/getProjectList") public Result projectList(){ return indexHomeService.getProjectList(); } @GetMapping("/getWaitTaskList") public Result getWaitTaskList(WaitTodoQuery query){ return indexHomeService.getWaitTaskList(query); } @GetMapping("/countTodayTask") Result countTodayTask(){ return workStationScheduleService.countTodayTask(); } } business/src/main/java/com/ycl/domain/entity/WorkStationSchedule.java
@@ -36,4 +36,7 @@ @TableField("completed_time") private Date completedTime; @TableField("project_id") private Integer projectId; } business/src/main/java/com/ycl/domain/form/AuditHistoryForm.java
New file @@ -0,0 +1,65 @@ package com.ycl.domain.form; import com.ycl.domain.entity.AuditHistory; import com.ycl.domain.entity.WorkStationSchedule; import com.ycl.system.domain.base.AbsForm; import lombok.Data; import org.springframework.beans.BeanUtils; import org.springframework.lang.NonNull; /** * nongtou-project-java * * @author : zxl * @date : 2025-12-11 10:19 **/ @Data public class AuditHistoryForm extends AbsForm { /** * 任务id */ private String taskId; /** * 任务key */ private String taskDefinitionKey; /** * 查看人id */ private String viewer; /** * 提交人部门 */ private String commitDept; /** * 1:提交,2:审核,3:驳回,4:转交 * @see com.ycl.common.enums.business.AuditTypeEnum */ private String auditType; /** * 是否阅读(0:未读,1:已读) */ private String isRead; /** * 业务id */ private String businessKey; /** * 业务表 */ private String businessTable; /** * 内容(节点名称) */ private String content; public static AuditHistory getEntityByForm(@NonNull AuditHistoryForm form, AuditHistory entity) { if(entity == null) { entity = new AuditHistory(); } BeanUtils.copyProperties(form, entity); return entity; } } business/src/main/java/com/ycl/domain/form/WorkStationScheduleForm.java
@@ -1,5 +1,6 @@ package com.ycl.domain.form; import com.fasterxml.jackson.annotation.JsonFormat; import com.ycl.common.group.Update; import com.ycl.common.group.Add; import com.ycl.system.domain.base.AbsForm; @@ -23,25 +24,15 @@ @ApiModel(value = "WorkStationSchedule表单", description = "表单") public class WorkStationScheduleForm extends AbsForm { @NotNull(message = "所属用户id不能为空", groups = {Add.class, Update.class}) @ApiModelProperty("所属用户id") private Long userId; @NotBlank(message = "工作内容不能为空", groups = {Add.class, Update.class}) @ApiModelProperty("工作内容") private String content; @NotBlank(message = "状态不能为空", groups = {Add.class, Update.class}) @ApiModelProperty("状态") private String status; @NotNull(message = "不能为空", groups = {Add.class, Update.class}) @ApiModelProperty("") private Date gmtcreate; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date completedTime; @NotNull(message = "不能为空", groups = {Add.class, Update.class}) @ApiModelProperty("") private Date gmtupdate; private Integer projectId; public static WorkStationSchedule getEntityByForm(@NonNull WorkStationScheduleForm form, WorkStationSchedule entity) { if(entity == null) { business/src/main/java/com/ycl/domain/query/AuditHistoryQuery.java
New file @@ -0,0 +1,24 @@ package com.ycl.domain.query; import com.ycl.system.domain.base.AbsQuery; import lombok.Data; import java.util.List; /** * nongtou-project-java * * @author : zxl * @date : 2025-12-11 11:59 **/ @Data public class AuditHistoryQuery extends AbsQuery { private String projectId; private Long deptId; private List<Long> projectIds; } business/src/main/java/com/ycl/domain/query/WaitTodoQuery.java
New file @@ -0,0 +1,16 @@ package com.ycl.domain.query; import com.ycl.system.domain.base.AbsQuery; import lombok.Data; /** * nongtou-project-java * * @author : zxl * @date : 2025-12-09 10:15 **/ @Data public class WaitTodoQuery extends AbsQuery { private String id; //all查询全部 } business/src/main/java/com/ycl/domain/query/WorkStationScheduleQuery.java
@@ -1,8 +1,12 @@ package com.ycl.domain.query; import com.fasterxml.jackson.annotation.JsonFormat; import com.ycl.system.domain.base.AbsQuery; import io.swagger.annotations.ApiModel; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.util.Date; /** * 查询 @@ -13,5 +17,10 @@ @Data @ApiModel(value = "WorkStationSchedule查询参数", description = "查询参数") public class WorkStationScheduleQuery extends AbsQuery { @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date completedTime; private String projectId; //查询 全部 传入的字符串 "all" } business/src/main/java/com/ycl/domain/vo/AuditHistoryVO.java
New file @@ -0,0 +1,88 @@ package com.ycl.domain.vo; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * nongtou-project-java * * @author : zxl * @date : 2025-12-11 14:00 **/ @Data public class AuditHistoryVO { /** * 流程任务ID */ @ApiModelProperty(value = "流程任务ID") private String taskId; /** * 任务key */ @ApiModelProperty(value = "任务key") private String taskDefinitionKey; /** * 查看人ID */ @ApiModelProperty(value = "查看人ID") private String viewer; /** * 提交人部门 */ @ApiModelProperty(value = "提交人部门") private String commitDept; /** * 审核类型(1:提交,2:审核,3:驳回,4:转交) */ @ApiModelProperty(value = "审核类型(1:提交,2:审核,3:驳回,4:转交)") private String auditType; /** * 是否阅读(0:未读,1:已读) */ @ApiModelProperty(value = "是否阅读(0:未读,1:已读)") private String isRead; /** * 业务ID(关联项目ID) */ @ApiModelProperty(value = "业务ID(关联项目ID)") private String businessKey; /** * 业务表名称 */ @ApiModelProperty(value = "业务表名称") private String businessTable; /** * 内容(节点名称) */ @ApiModelProperty(value = "内容(节点名称)") private String content; /** * 关联的项目名称(从t_project_info表关联) */ @ApiModelProperty(value = "项目名称") private String projectName; /** * 审核类型中文描述(前端展示用,如:提交/审核/驳回/转交) */ @ApiModelProperty(value = "审核类型中文描述") private String auditTypeDesc; /** * 是否阅读中文描述(前端展示用,如:未读/已读) */ @ApiModelProperty(value = "是否阅读中文描述") private String isReadDesc; } business/src/main/java/com/ycl/domain/vo/DailyStatVO.java
New file @@ -0,0 +1,27 @@ package com.ycl.domain.vo; import lombok.Data; import java.util.Date; import java.util.List; /** * nongtou-project-java * 每日统计结果封装类 * @author : zxl * @date : 2025-12-10 13:57 **/ @Data public class DailyStatVO { // 当日数据列表 private Long id; private String date; private String time; private String title; private String project; private Date completedTime; private Integer projectId; } business/src/main/java/com/ycl/domain/vo/TaskInfoVo.java
@@ -22,5 +22,7 @@ private Long overTimeCount;//超时次数 private CheckPointVO checkPointInfo; private String status; private CustomerTaskVO customerTaskInfo; } business/src/main/java/com/ycl/domain/vo/WorkStationScheduleVO.java
@@ -31,13 +31,11 @@ @ApiModelProperty("状态") private String status; /** */ @ApiModelProperty("") private Date gmtcreate; private Date completedTime; /** */ @ApiModelProperty("") private Date gmtupdate; private Integer projectId; private String projectName; public static WorkStationScheduleVO getVoByEntity(@NonNull WorkStationSchedule entity, WorkStationScheduleVO vo) { if(vo == null) { business/src/main/java/com/ycl/mapper/AuditHistoryMapper.java
@@ -1,9 +1,13 @@ package com.ycl.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.ycl.domain.entity.AuditHistory; import com.ycl.domain.entity.File; import com.ycl.domain.query.AuditHistoryQuery; import com.ycl.domain.query.WorkStationScheduleQuery; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; /** * 审核记录 Mapper 接口 @@ -12,5 +16,9 @@ @Mapper public interface AuditHistoryMapper extends BaseMapper<AuditHistory> { /** * 分页 */ IPage getPage(IPage page, @Param("query") AuditHistoryQuery query); } business/src/main/java/com/ycl/mapper/WorkStationScheduleMapper.java
@@ -5,8 +5,13 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ycl.domain.vo.WorkStationScheduleVO; import com.ycl.domain.query.WorkStationScheduleQuery; import lombok.Data; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.Date; import java.util.List; import java.util.Map; /** * Mapper 接口 @@ -29,4 +34,9 @@ */ IPage getPage(IPage page, @Param("query") WorkStationScheduleQuery query); List<WorkStationScheduleVO> groupByProjectAndDate( @Param("userId") Long userId, @Param("projectId") Integer projectId, @Param("startTime") Date startTime, @Param("endTime") Date endTime); } business/src/main/java/com/ycl/service/AuditHistoryService.java
@@ -1,12 +1,18 @@ package com.ycl.service; import com.baomidou.mybatisplus.extension.service.IService; import com.ycl.common.base.Result; import com.ycl.domain.entity.AuditHistory; import com.ycl.domain.form.AuditHistoryForm; import com.ycl.domain.query.AuditHistoryQuery; /** * 审核记录 服务类 */ public interface AuditHistoryService extends IService<AuditHistory> { public Result add(AuditHistoryForm auditHistory); public Result page(AuditHistoryQuery query); } business/src/main/java/com/ycl/service/IndexHomeService.java
@@ -2,7 +2,12 @@ import com.ycl.common.base.Result; import com.ycl.domain.entity.ProjectInfo; import com.ycl.domain.form.ProjectProgressStatisticsForm; import com.ycl.domain.query.WaitTodoQuery; import com.ycl.system.domain.base.AbsQuery; import java.util.List; /** * 首页 服务类 @@ -21,6 +26,8 @@ */ Result projectStageCount(); List<ProjectInfo> getLoginUserOwnProjectInfo(); /** * 项目进度统计 * @return @@ -33,6 +40,11 @@ */ Result projectFundingStatus(); /** * 获得待处理任务 * @return */ int countWaitTask(); /** * 项目推进卡点 @@ -40,5 +52,15 @@ */ Result projectAdvanceCheckPoint(); Result getWaitTaskList(WaitTodoQuery query); /** * 获得项目列表 * @return */ Result getProjectList(); Result getProjectSelectList(); } business/src/main/java/com/ycl/service/WorkStationScheduleService.java
@@ -51,6 +51,12 @@ Result page(WorkStationScheduleQuery query); /** * 查询传入时间月份内每天日程 * @param query * @return */ Result listByDate(WorkStationScheduleQuery query); /** * 根据id查找 * @param id * @return @@ -64,6 +70,15 @@ Result all(); Result completedAchievements(Integer id); /** * 获得今成就统计 * @return */ Result countAchievements(); /** * 获得今日任务统计 * @return */ Result countTodayTask(); } business/src/main/java/com/ycl/service/impl/AuditHistoryServiceImpl.java
@@ -1,15 +1,30 @@ package com.ycl.service.impl; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ycl.common.base.Result; import com.ycl.common.utils.SecurityUtils; import com.ycl.domain.entity.AuditHistory; import com.ycl.domain.entity.ProjectInfo; import com.ycl.domain.form.AuditHistoryForm; import com.ycl.domain.query.AuditHistoryQuery; import com.ycl.domain.vo.AuditHistoryVO; import com.ycl.framework.utils.PageUtil; import com.ycl.mapper.AuditHistoryMapper; import com.ycl.mapper.ProjectInfoMapper; import com.ycl.service.AuditHistoryService; import com.ycl.service.IndexHomeService; import com.ycl.system.mapper.SysDeptMapper; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * 审核记录 服务实现类 @@ -19,4 +34,61 @@ @RequiredArgsConstructor public class AuditHistoryServiceImpl extends ServiceImpl<AuditHistoryMapper, AuditHistory> implements AuditHistoryService { private final AuditHistoryMapper auditHistoryMapper; private final ProjectInfoMapper projectInfoMapper; private final SysDeptMapper sysDeptMapper; @Override public Result add(AuditHistoryForm auditHistoryForm) { AuditHistory entity = AuditHistoryForm.getEntityByForm(auditHistoryForm, null); baseMapper.insert(entity); return Result.ok("添加成功"); } public List<ProjectInfo> getLoginUserOwnProjectInfo(){ //权限控制 Long userId = SecurityUtils.getUserId(); List<ProjectInfo> list; if (SecurityUtils.isAdmin(userId)){ //查询全部 list = new LambdaQueryChainWrapper<>(projectInfoMapper) .eq(ProjectInfo::getDeleted, Boolean.FALSE) .eq(ProjectInfo::getUsedStatus, 2)//审核通过 .orderBy(true, true, ProjectInfo::getId) .list(); }else{ String ancestors = sysDeptMapper.selectAncestors(userId); String[] ancestorArr = ancestors.split(","); List<String> ancestorList = Arrays.stream(ancestorArr).collect(Collectors.toList()); ancestorList.add(SecurityUtils.getDeptId() + ""); //获得本单位以及其子单位deptId; list = new LambdaQueryChainWrapper<>(projectInfoMapper) .eq(ProjectInfo::getDeleted, Boolean.FALSE) .eq(ProjectInfo::getUsedStatus, 2) //审核通过 .in(ProjectInfo::getProjectOwnerUnit, ancestorList) .orderBy(true, true, ProjectInfo::getId) .list(); } return list; } @Override public Result page(AuditHistoryQuery query) { //需要查询到指定项目的日志 List<Long> ids = new ArrayList<>(); if ("all".equals(query.getProjectId())){ List<ProjectInfo> loginUserOwnProjectInfo = getLoginUserOwnProjectInfo(); if (CollectionUtils.isEmpty(loginUserOwnProjectInfo)) { //返回默认值 return Result.ok().data(null).total(0); } ids = loginUserOwnProjectInfo.stream().map(ProjectInfo::getId).collect(Collectors.toList()); }else{ ids.add(Long.valueOf(query.getProjectId())); } query.setProjectIds(ids); query.setDeptId(SecurityUtils.getDeptId()); IPage<AuditHistoryVO> page = PageUtil.getPage(query, AuditHistoryVO.class); baseMapper.getPage(page, query); return Result.ok().data(page.getRecords()).total(page.getTotal()); } } business/src/main/java/com/ycl/service/impl/FlowTaskServiceImpl.java
@@ -16,6 +16,8 @@ import com.ycl.common.core.domain.entity.SysRole; import com.ycl.common.core.domain.entity.SysUser; import com.ycl.common.enums.FlowComment; import com.ycl.common.enums.business.AuditHistoryEnum; import com.ycl.common.enums.business.AuditTypeEnum; import com.ycl.common.enums.business.ProcessLogEventTypeEnum; import com.ycl.common.enums.business.TaskStatusEnum; import com.ycl.common.exception.CustomException; @@ -29,6 +31,7 @@ import com.ycl.domain.entity.ProcessLog; import com.ycl.domain.entity.ProjectProcess; import com.ycl.domain.entity.SysForm; import com.ycl.domain.form.AuditHistoryForm; import com.ycl.domain.form.EditFinishedTaskForm; import com.ycl.domain.json.RejectData; import com.ycl.domain.query.ProcessLogQuery; @@ -111,7 +114,7 @@ private final ProcessLogService processLogService; private final ApplicationEventPublisher publisher; private final ProjectProcessMapper projectProcessMapper; private final AuditHistoryService auditHistoryService; @Value("${targetIp}") private String targetIp; @@ -136,6 +139,21 @@ taskService.setAssignee(taskVo.getTaskId(), userId.toString()); taskService.complete(taskVo.getTaskId(), taskVo.getVariables()); } ProjectProcess projectProcess = new LambdaQueryChainWrapper<>(projectProcessMapper) .eq(ProjectProcess::getProcessInsId, task.getProcessInstanceId()) .eq(ProjectProcess::getProcessDefId, task.getProcessDefinitionId()) .one(); AuditHistoryForm auditHistoryForm = buildForm(task.getId(), task.getTaskDefinitionKey(), null, SecurityUtils.getDeptId() + "", AuditTypeEnum.Review.name(), projectProcess.getProjectId(), "t_project_info", task.getName() ); auditHistoryService.add(auditHistoryForm); return AjaxResult.success(); } @@ -195,9 +213,45 @@ ProcessLogEventTypeEnum.FINISHED, null)); } //保存日志 AuditHistoryForm auditHistoryForm = buildForm(taskId, task.getTaskDefinitionKey(), null, SecurityUtils.getDeptId() + "", AuditTypeEnum.Submit.name(), projectProcess.getProjectId(), "t_project_info", task.getName() ); auditHistoryService.add(auditHistoryForm); return AjaxResult.success("提交成功"); } public AuditHistoryForm buildForm( String taskId, String taskDefinitionKey, String viewer, String commitDept, String auditType, String businessKey, String businessTable, String content) { AuditHistoryForm auditHistoryForm = new AuditHistoryForm(); auditHistoryForm.setTaskId(taskId); auditHistoryForm.setTaskDefinitionKey(taskDefinitionKey); auditHistoryForm.setViewer(viewer); auditHistoryForm.setCommitDept(commitDept); auditHistoryForm.setAuditType(auditType); auditHistoryForm.setBusinessKey(businessKey); auditHistoryForm.setBusinessTable(businessTable); auditHistoryForm.setContent(content); // 5. 特殊字段默认值:isRead 默认为 (未读) auditHistoryForm.setIsRead(AuditHistoryEnum.UN_READ.name()); return auditHistoryForm; } /** * 容缺补交 @@ -499,6 +553,21 @@ task.getName(), ProcessLogEventTypeEnum.REJECT, new RejectData(flowTaskVo.getComment()))); //保存日志 AuditHistoryForm auditHistoryForm = buildForm(task.getId(), task.getTaskDefinitionKey(), null, SecurityUtils.getDeptId() + "", AuditTypeEnum.Reject.name(), projectProcess.getProjectId(), "t_project_info", task.getName() ); auditHistoryService.add(auditHistoryForm); } /** @@ -688,6 +757,7 @@ @Transactional(rollbackFor = Exception.class) public void assignTask(FlowTaskVo flowTaskVo) { // 直接转派就可以覆盖掉之前的 log.info("flowTaskVo:{}",flowTaskVo); taskService.setAssignee(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); // // 删除指派人重新指派 // taskService.deleteCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); @@ -695,6 +765,17 @@ // // 如果要查询转给他人处理的任务,可以同时将OWNER进行设置: // taskService.setOwner(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); // AuditHistoryForm auditHistoryForm = // buildForm(flowTaskVo.getTaskId(), // task.getTaskDefinitionKey(), // null, // SecurityUtils.getDeptId() + "", // AuditTypeEnum.Submit.name(), // projectProcess.getProjectId(), // "t_project_info", // task.getName() // ); } /** business/src/main/java/com/ycl/service/impl/IndexHomeServiceImpl.java
@@ -1,9 +1,7 @@ package com.ycl.service.impl; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.ycl.common.annotation.DataScope; import com.ycl.common.base.Result; import com.ycl.common.constant.ProcessOverTimeConstants; import com.ycl.common.core.domain.BaseEntity; @@ -17,14 +15,15 @@ import com.ycl.domain.entity.ProjectInfo; import com.ycl.domain.entity.ProjectProcess; import com.ycl.domain.form.ProjectProgressStatisticsForm; import com.ycl.domain.query.WaitTodoQuery; import com.ycl.domain.vo.*; import com.ycl.factory.FlowServiceFactory; import com.ycl.mapper.ProcessCodingMapper; import com.ycl.mapper.ProcessLogMapper; import com.ycl.mapper.ProjectInfoMapper; import com.ycl.mapper.ProjectProcessMapper; import com.ycl.service.IndexHomeService; import com.ycl.service.ProjectProcessService; import com.ycl.service.common.TaskCommonService; import com.ycl.system.mapper.SysDeptMapper; import com.ycl.system.service.ISysDeptService; import com.ycl.system.service.ISysRoleService; @@ -39,8 +38,8 @@ import org.flowable.engine.runtime.ProcessInstance; import org.flowable.identitylink.api.IdentityLink; import org.flowable.identitylink.api.IdentityLinkInfo; import org.flowable.identitylink.api.IdentityLinkType; import org.flowable.task.api.Task; import org.flowable.task.api.TaskQuery; import org.flowable.task.api.history.HistoricTaskInstance; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -78,13 +77,14 @@ private final ISysDeptService sysDeptService; private final ISysRoleService sysRoleService; private final TaskCommonService taskCommonService; @Override public Result projectCodingStatusCount() { //权限控制 List<ProjectInfo> loginUserOwnProjectInfo = getLoginUserOwnProjectInfo(); Map<String,Integer> map = new HashMap<>(); map.put(ProcessOverTimeConstants.GREEN,0); @@ -151,6 +151,7 @@ return Result.ok().data(map); } @Override public List<ProjectInfo> getLoginUserOwnProjectInfo(){ //权限控制 Long userId = SecurityUtils.getUserId(); @@ -389,6 +390,58 @@ return Result.ok().data(projectInfoAndFunding); } @Override public int countWaitTask() { Long userId = SecurityUtils.getUserId(); List<ProjectInfo> loginUserOwnProjectInfo = getLoginUserOwnProjectInfo(); if (CollectionUtils.isEmpty(loginUserOwnProjectInfo)) { return 0; } List<Long> ids = loginUserOwnProjectInfo.stream() .filter(Objects::nonNull) .map(ProjectInfo::getId) .filter(Objects::nonNull) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(ids)) { return 0; } List<ProjectProcess> projectProcessList = new LambdaQueryChainWrapper<>(projectProcessService.getBaseMapper()) .eq(ProjectProcess::getDeleted, Boolean.FALSE) .in(ProjectProcess::getProjectId, ids) .list(); if (CollectionUtils.isEmpty(projectProcessList)) { return 0; } List<String> targetProcessInsIds = projectProcessList.stream() .filter(Objects::nonNull) // 过滤null的ProjectProcess对象 .map(ProjectProcess::getProcessInsId) .filter(defId -> defId != null && !defId.trim().isEmpty()) // 先判null再trim,修复风险3 .distinct() .collect(Collectors.toList()); if (CollectionUtils.isEmpty(targetProcessInsIds)) { return 0; } List<Task> allRunningTasks = taskService.createTaskQuery().active().list(); if (CollectionUtils.isEmpty(allRunningTasks)) { return 0; } List<Task> targetRunningTasks = allRunningTasks.stream() .filter(Objects::nonNull) .filter(task -> { // 先判null,再匹配,修复风险1 String processInsId = task.getProcessInstanceId(); return processInsId != null && targetProcessInsIds.contains(processInsId); }) .filter(task -> { String assignee = task.getAssignee(); return assignee == null || userId.toString().equals(assignee);//排除领取人 }) .collect(Collectors.toList()); return targetRunningTasks.size(); } @Override public Result projectAdvanceCheckPoint() { @@ -401,7 +454,7 @@ List<ProjectProcess> projectProcessList = new LambdaQueryChainWrapper<>(projectProcessService.getBaseMapper()) .eq(ProjectProcess::getDeleted, Boolean.FALSE) .in(ProjectProcess::getProjectId, ids).list(); Long userId = SecurityUtils.getUserId(); //流程实例id集合 List<String> targetProcessInsIds = projectProcessList.stream() .map(ProjectProcess::getProcessInsId) @@ -454,11 +507,16 @@ List<Task> allRunningTasks = taskService.createTaskQuery().active().list(); List<Task> targetRunningTasks = allRunningTasks.stream() // 关键匹配:任务的流程定义ID 存在于 projectProcessList 的流程定义ID列表中 .filter(task -> targetProcessInsIds.contains(task.getProcessInstanceId())) .filter(task -> { String assignee = task.getAssignee(); log.info("assignee:{}",assignee); return assignee == null || userId.toString().equals(assignee); }) .collect(Collectors.toList()); List<TaskInfoVo> list = new ArrayList<>(); // 按流程实例id分组 @@ -471,9 +529,11 @@ .eq(ProcessCoding::getDeleted, Boolean.FALSE) .in(ProcessCoding::getTaskId, taskIds) .list(); log.info("processCodingList.size():{}", processCodingList.size()); // 看日志是否为0 Map<String, ProcessCoding> processCodingMap = new HashMap<>(); //按taskId转换为map方面下面获取 if (!CollectionUtils.isEmpty(processCodingList)){ Map<String, ProcessCoding> processCodingMap = processCodingList.stream() processCodingMap = processCodingList.stream() .filter(Objects::nonNull) // 避免null元素导致NPE(可选保留) .filter(vo -> vo.getTaskId() != null) // 过滤taskId为null的无效数据 .collect(Collectors.toMap( @@ -493,128 +553,126 @@ return newCreateTime.getTime() > oldCreateTime.getTime() ? newVal : oldVal; } )); } for (Task task : targetRunningTasks) { for (Task task : targetRunningTasks) { TaskInfoVo taskVo = new TaskInfoVo(); TaskInfoVo taskVo = new TaskInfoVo(); taskVo.setId(task.getId()); taskVo.setTaskName(task.getName()); taskVo.setStartTime(task.getCreateTime()); taskVo.setEndTime(task.getDueDate()); taskVo.setId(task.getId()); taskVo.setTaskName(task.getName()); taskVo.setStartTime(task.getCreateTime()); taskVo.setEndTime(task.getDueDate()); // taskVo.setTaskType(); 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); CustomerTaskVO customerTaskVO = new CustomerTaskVO(); customerTaskVO.setHandlerId(handlerIds); customerTaskVO.setHandlerName(handlerNames); customerTaskVO.setHandlerUnitId(handlerUnitIds); customerTaskVO.setHandlerUnitName(handlerUnitNames); customerTaskVO.setPromoterName(promoterNames); customerTaskVO.setPromoterUnitName(promoterUnitNames); 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); CustomerTaskVO customerTaskVO = new CustomerTaskVO(); customerTaskVO.setHandlerId(handlerIds); customerTaskVO.setHandlerName(handlerNames); customerTaskVO.setHandlerUnitId(handlerUnitIds); customerTaskVO.setHandlerUnitName(handlerUnitNames); customerTaskVO.setPromoterName(promoterNames); customerTaskVO.setPromoterUnitName(promoterUnitNames); // 流程处理人信息 List<IdentityLink> identityLinksForTask = taskService.getIdentityLinksForTask(task.getId()); for (IdentityLinkInfo identityLink : identityLinksForTask) { // 绑定的是用户,查出用户姓名、部门 if (StringUtils.isNotBlank(identityLink.getUserId())) { // 处理变量表达式,运行中的任务无需再处理表达式了,flowable已经自动根据变量设置了 customerTaskVO.setHandlerType(HandlerTypeEnum.USER); SysUser sysUser = sysUserService.selectUserById(Long.parseLong(identityLink.getUserId())); if (Objects.nonNull(sysUser)) { customerTaskVO.getHandlerId().add(sysUser.getUserId()); customerTaskVO.getHandlerName().add(this.getUserShowName(sysUser)); if (Objects.nonNull(sysUser.getDept())) { customerTaskVO.getHandlerUnitId().add(sysUser.getDept().getDeptId()); customerTaskVO.getHandlerUnitName().add(sysUser.getDept().getDeptName()); customerTaskVO.getPromoterName().add(this.getUserShowName(sysUser)); String[] str = sysUser.getDept().getAncestors().split(","); if (str.length >= 4){ customerTaskVO.getPromoterUnitName().add(sysUser.getDept().getParentName() +"-"+sysUser.getDept().getDeptName()); }else { customerTaskVO.getPromoterUnitName().add(sysUser.getDept().getDeptName()); } // 流程处理人信息 List<IdentityLink> identityLinksForTask = taskService.getIdentityLinksForTask(task.getId()); for (IdentityLinkInfo identityLink : identityLinksForTask) { // 绑定的是用户,查出用户姓名、部门 if (StringUtils.isNotBlank(identityLink.getUserId())) { // 处理变量表达式,运行中的任务无需再处理表达式了,flowable已经自动根据变量设置了 customerTaskVO.setHandlerType(HandlerTypeEnum.USER); SysUser sysUser = sysUserService.selectUserById(Long.parseLong(identityLink.getUserId())); if (Objects.nonNull(sysUser)) { customerTaskVO.getHandlerId().add(sysUser.getUserId()); customerTaskVO.getHandlerName().add(this.getUserShowName(sysUser)); if (Objects.nonNull(sysUser.getDept())) { customerTaskVO.getHandlerUnitId().add(sysUser.getDept().getDeptId()); customerTaskVO.getHandlerUnitName().add(sysUser.getDept().getDeptName()); customerTaskVO.getPromoterName().add(this.getUserShowName(sysUser)); String[] str = sysUser.getDept().getAncestors().split(","); if (str.length >= 4){ customerTaskVO.getPromoterUnitName().add(sysUser.getDept().getParentName() +"-"+sysUser.getDept().getDeptName()); }else { customerTaskVO.getPromoterUnitName().add(sysUser.getDept().getDeptName()); } } // 绑定的是角色或者部门 } else if (StringUtils.isNotBlank(identityLink.getGroupId())) { if (identityLink.getGroupId().startsWith("dept")) { // 部门的id是加了前缀的如:dept:1 customerTaskVO.setHandlerType(HandlerTypeEnum.DEPT); String[] split = identityLink.getGroupId().split(":"); if (split.length > 1) { // 部门 SysDept dept = sysDeptService.selectDeptById(Long.parseLong(split[1])); if (Objects.nonNull(dept)) { customerTaskVO.getHandlerUnitId().add(dept.getDeptId()); customerTaskVO.getHandlerUnitName().add(dept.getDeptName()); customerTaskVO.getPromoterName().add(this.getDeptLeaderShowName(dept)); customerTaskVO.getPromoterUnitName().add(this.setDeptNameWithParentName(dept)); } } } else { customerTaskVO.setHandlerType(HandlerTypeEnum.ROLE); SysRole role = sysRoleService.selectRoleById(Long.parseLong(identityLink.getGroupId())); if (Objects.nonNull(role)) { customerTaskVO.getHandlerUnitId().add(Long.parseLong(identityLink.getGroupId())); customerTaskVO.getHandlerUnitName().add(role.getRoleName()); } } } this.distinctVo(customerTaskVO); } taskVo.setCustomerTaskInfo(customerTaskVO); //组装任务信息 CheckPointVO checkPointVO = processInfoMap.get(task.getProcessInstanceId()); if (checkPointVO != null) { taskVo.setCheckPointInfo(checkPointVO); } //超时时间和次数在 processCodingList中 这里需要更具taskId来获得对应信息 ProcessCoding processCoding = processCodingMap.get(task.getId()); //设置超时信息 if (processCoding != null) { String overTimeTotalStr = processCoding.getOverTimeTotal(); long overTimeTotal = 0L; if (StringUtils.isNotBlank(overTimeTotalStr)) { try { overTimeTotal = Long.parseLong(overTimeTotalStr.trim()); } catch (NumberFormatException e) { } } String overTimeDesc = convertHoursToDayHourStr(overTimeTotal); taskVo.setTotalOverTime(overTimeDesc); // 2. 提取红码阈值 如: 0-22 天-小时 并转换为小时 String redTimeStr = processCoding.getRedTime(); Long redTimeSec = getTime(redTimeStr); Double redTimeHour = null; if (redTimeSec != null && redTimeSec > 0) { redTimeHour = redTimeSec / 3600.0; // 绑定的是角色或者部门 } else if (StringUtils.isNotBlank(identityLink.getGroupId())) { if (identityLink.getGroupId().startsWith("dept")) { // 部门的id是加了前缀的如:dept:1 customerTaskVO.setHandlerType(HandlerTypeEnum.DEPT); String[] split = identityLink.getGroupId().split(":"); if (split.length > 1) { // 部门 SysDept dept = sysDeptService.selectDeptById(Long.parseLong(split[1])); if (Objects.nonNull(dept)) { customerTaskVO.getHandlerUnitId().add(dept.getDeptId()); customerTaskVO.getHandlerUnitName().add(dept.getDeptName()); customerTaskVO.getPromoterName().add(this.getDeptLeaderShowName(dept)); customerTaskVO.getPromoterUnitName().add(this.setDeptNameWithParentName(dept)); } } } else { customerTaskVO.setHandlerType(HandlerTypeEnum.ROLE); SysRole role = sysRoleService.selectRoleById(Long.parseLong(identityLink.getGroupId())); if (Objects.nonNull(role)) { customerTaskVO.getHandlerUnitId().add(Long.parseLong(identityLink.getGroupId())); customerTaskVO.getHandlerUnitName().add(role.getRoleName()); } } long overTimeCount = 0L; // 判断超时次数 if (overTimeTotal > 0 && redTimeHour != null && redTimeHour > 0) { overTimeCount = (long) (overTimeTotal / redTimeHour); } taskVo.setOverTimeCount(overTimeCount); }else { //为null 说明未配置超时时间 默认未超时 taskVo.setOverTimeCount(0L); taskVo.setTotalOverTime("0小时"); } list.add(taskVo); this.distinctVo(customerTaskVO); } taskVo.setCustomerTaskInfo(customerTaskVO); //组装任务信息 CheckPointVO checkPointVO = processInfoMap.get(task.getProcessInstanceId()); if (checkPointVO != null) { taskVo.setCheckPointInfo(checkPointVO); } //超时时间和次数在 processCodingList中 这里需要更具taskId来获得对应信息 ProcessCoding processCoding = processCodingMap.get(task.getId()); //设置超时信息 if (processCoding != null) { String overTimeTotalStr = processCoding.getOverTimeTotal(); long overTimeTotal = 0L; if (StringUtils.isNotBlank(overTimeTotalStr)) { try { overTimeTotal = Long.parseLong(overTimeTotalStr.trim()); } catch (NumberFormatException e) { } } String overTimeDesc = convertHoursToDayHourStr(overTimeTotal); taskVo.setTotalOverTime(overTimeDesc); // 2. 提取红码阈值 如: 0-22 天-小时 并转换为小时 String redTimeStr = processCoding.getRedTime(); Long redTimeSec = getTime(redTimeStr); Double redTimeHour = null; if (redTimeSec != null && redTimeSec > 0) { redTimeHour = redTimeSec / 3600.0; } long overTimeCount = 0L; // 判断超时次数 if (overTimeTotal > 0 && redTimeHour != null && redTimeHour > 0) { overTimeCount = (long) (overTimeTotal / redTimeHour); } taskVo.setOverTimeCount(overTimeCount); }else { //为null 说明未配置超时时间 默认未超时 taskVo.setOverTimeCount(0L); taskVo.setTotalOverTime("0小时"); } list.add(taskVo); } @@ -622,6 +680,211 @@ return Result.ok().data(list); } public HashMap<String,Object> buildEmptyResultMap(){ HashMap<String,Object> map = new HashMap<>(); map.put("data", new ArrayList<>()); map.put("total", 0L); return map; } @Override public Result getWaitTaskList(WaitTodoQuery baseQuery) { int pageNum = (int) baseQuery.getCurrentPage(); int pageSize = (int) baseQuery.getPageSize(); Long userId = SecurityUtils.getUserId(); // 当前用户ID boolean isAdmin = SecurityUtils.getLoginUser().getUser().isAdmin(); // 是否管理员 List<String> userGroups = taskCommonService.getCurrentUserGroups(); // 当前用户所属组 List<ProjectInfo> targetProjectList = new ArrayList<>(); String queryProjectId = baseQuery.getId(); if ("all".equals(queryProjectId) || StringUtils.isBlank(queryProjectId)){ // 查询全部 targetProjectList = getLoginUserOwnProjectInfo(); }else{ try { Long projectId = Long.parseLong(queryProjectId); // ① 查询该项目基础信息(替换为实际查询方法) ProjectInfo projectInfo = projectInfoMapper.selectById(projectId); if (projectInfo == null) { // 项目不存在,返回空数据 return Result.ok().data(buildEmptyResultMap()); } // targetProjectList.add(projectInfo); } catch (NumberFormatException e) { // 项目ID格式错误,返回空数据 return Result.ok().data(buildEmptyResultMap()); } } List<Long> projectIds = targetProjectList.stream().map(ProjectInfo::getId).collect(Collectors.toList()); List<ProjectProcess> projectProcessList = new LambdaQueryChainWrapper<>(projectProcessService.getBaseMapper()) .eq(ProjectProcess::getDeleted, Boolean.FALSE) .in(ProjectProcess::getProjectId, projectIds).list(); // 4. 提取有效的流程实例ID(去重+非空过滤) List<String> targetProcessInsIds = projectProcessList.stream() .map(ProjectProcess::getProcessInsId) .distinct() .filter(defId -> defId != null && !defId.trim().isEmpty()) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(targetProcessInsIds)) { return Result.ok().data(new HashMap<String, Object>() {{ put("data", new ArrayList<>()); put("total", 0L); }}); } // 5. 构建processInfoMap(核心:项目ID→CheckPointVO,再关联流程信息) Map<String, CheckPointVO> map = new HashMap<>(); // 先填充项目基础信息 for (ProjectInfo projectInfo : targetProjectList) { CheckPointVO checkPointVO = new CheckPointVO(); checkPointVO.setId(projectInfo.getId()); checkPointVO.setProjectName(projectInfo.getProjectName()); map.put(String.valueOf(projectInfo.getId()), checkPointVO); } // 再填充流程信息(部署ID、流程名称等) for (ProjectProcess projectProcess : projectProcessList) { CheckPointVO checkPointVO = map.get(projectProcess.getProjectId()); if (checkPointVO == null) { continue; // 无匹配项目,跳过 } checkPointVO.setProcessDefinitionId(projectProcess.getProcessDefId()); checkPointVO.setProcessInstanceId(projectProcess.getProcessInsId()); // 获得流程部署id和流程名称(兼容运行中/已结束流程) ProcessInstance process = runtimeService.createProcessInstanceQuery() .processInstanceId(projectProcess.getProcessInsId()).singleResult(); if (Objects.nonNull(process)) { checkPointVO.setDeployId(process.getDeploymentId()); checkPointVO.setProcessName(process.getProcessDefinitionName()); } else { HistoricProcessInstance hisProcess = historyService.createHistoricProcessInstanceQuery() .processInstanceId(projectProcess.getProcessInsId()).singleResult(); if (Objects.nonNull(hisProcess)) { // 避免历史流程为null的NPE checkPointVO.setDeployId(hisProcess.getDeploymentId()); checkPointVO.setProcessName(hisProcess.getProcessDefinitionName()); } } map.put(projectProcess.getProjectId(), checkPointVO); } Map<String, CheckPointVO> processInfoMap = map.values().stream() .filter(Objects::nonNull) .filter(vo -> vo.getProcessInstanceId() != null) .collect(Collectors.toMap( CheckPointVO::getProcessInstanceId, vo -> vo, (oldVal, newVal) -> newVal // 重复流程实例ID保留后者 )); TaskQuery query = getTaskService().createTaskQuery() .active() // 未完成、未挂起的活跃任务 .processInstanceIdIn(targetProcessInsIds); // 仅查项目关联的流程实例任务 // 权限过滤:非管理员仅查自己有权限的任务(已领取+候选) if (!isAdmin) { query.or() .taskAssignee(userId+"") // 自己已领取的任务 .taskCandidateUser(userId+"") // 自己是候选用户的任务 .taskCandidateGroupIn(userGroups) // 自己在候选组的任务 .endOr(); } query.orderByTaskCreateTime().desc(); List<Task> allTaskList = query.list(); // 排除领取人问题 allTaskList = allTaskList.stream() .filter(Objects::nonNull) // 空任务防护 .filter(task -> { String assignee = task.getAssignee(); return assignee == null || userId.toString().equals(assignee); }) .collect(Collectors.toList()); Set<String> taskIdSet = new HashSet<>(); List<Task> distinctAllTaskList = new ArrayList<>(); for (Task task : allTaskList) { if (!taskIdSet.contains(task.getId())) { taskIdSet.add(task.getId()); distinctAllTaskList.add(task); // } } long distinctTotal = distinctAllTaskList.size(); System.out.println("去重后总数:" + distinctTotal); int startIndex = (pageNum - 1) * pageSize; List<Task> pageTaskList = new ArrayList<>(); if (startIndex < distinctAllTaskList.size()) { int endIndex = Math.min(startIndex + pageSize, distinctAllTaskList.size()); pageTaskList = distinctAllTaskList.subList(startIndex, endIndex); } List<TaskInfoVo> taskInfoVoList = new ArrayList<>(); //查询任务状态 List<String> taskIds = pageTaskList.stream().map(Task::getId).collect(Collectors.toList()); HashMap<String,ProcessLog> taskInfoMap = new HashMap<>(); if (!CollectionUtils.isEmpty(taskIds)) { taskInfoMap = new LambdaQueryChainWrapper<>(processLogMapper) .eq(ProcessLog::getDeleted, Boolean.FALSE) .in(ProcessLog::getTaskId, taskIds).list() .stream() .filter(processLog -> processLog.getTaskId() != null && processLog.getGmtCreate() != null) .collect(Collectors.toMap( // Key:taskId转字符串 ProcessLog::getTaskId, // Value:ProcessLog对象本身 processLog -> processLog, // 冲突策略:保留gmtCreate更新的那条记录 (existing, replacement) -> { return replacement.getGmtCreate().after(existing.getGmtCreate()) ? replacement : existing; }, HashMap::new )); } for (Task task : pageTaskList) { TaskInfoVo taskVo = new TaskInfoVo(); taskVo.setId(task.getId()); taskVo.setTaskName(task.getName()); taskVo.setStartTime(task.getCreateTime()); taskVo.setEndTime(task.getDueDate()); // 填充任务状态字段 taskVo.setTaskType("待完成"); ProcessLog processLog = taskInfoMap.get(task.getId()); if (Objects.nonNull(processLog)) { taskVo.setTaskType(processLog.getEventType().getDesc()); } CheckPointVO checkPointVO = processInfoMap.get(task.getProcessInstanceId()); if (checkPointVO != null) { taskVo.setCheckPointInfo(checkPointVO); } taskInfoVoList.add(taskVo); } //排除 已完成的 taskInfoVoList = taskInfoVoList.stream().filter(taskInfoVo -> { if (ProcessLogEventTypeEnum.FINISHED.getDesc().equals(taskInfoVo.getTaskType())){ return false; } return true; }).collect(Collectors.toList()); HashMap<String, Object> resultMap = new HashMap<>(); resultMap.put("data", taskInfoVoList); resultMap.put("total", distinctTotal); return Result.ok().data(resultMap); } private String convertHoursToDayHourStr(long totalHours) { if (totalHours < 0) { return "0小时"; // 防御性处理负数场景 @@ -681,11 +944,18 @@ } @Override public Result getProjectList(){ List<ProjectInfo> loginUserOwnProjectInfo = getLoginUserOwnProjectInfo(); return Result.ok().data(loginUserOwnProjectInfo); } @Override public Result getProjectSelectList() { List<ProjectInfo> loginUserOwnProjectInfo = getLoginUserOwnProjectInfo(); if (CollectionUtils.isEmpty(loginUserOwnProjectInfo)) { return Result.ok().data(loginUserOwnProjectInfo); } Map<String, String> projectMap = loginUserOwnProjectInfo.stream() .collect(Collectors.toMap( projectInfo -> String.valueOf(projectInfo.getId()), // key: 项目ID @@ -695,4 +965,6 @@ )); return Result.ok().data(projectMap); } } business/src/main/java/com/ycl/service/impl/ProjectProcessServiceImpl.java
@@ -95,7 +95,7 @@ private final ProcessLogService processLogService; private final ISysDictTypeService dictTypeService; private final ProcessConfigInfoService processConfigInfoService; private final AuditHistoryService auditHistoryService; /** * 分页查询 * @@ -815,9 +815,44 @@ // 发布转办事件 publisher.publishEvent(new TaskLogEvent(this, null, SecurityUtils.getUserId(), form.getProjectId(), form.getProcessInsId(), task.getId(), task.getTaskDefinitionKey(), task.getName(), ProcessLogEventTypeEnum.DELEGATE, jsonData)); AuditHistoryForm auditHistoryForm = buildForm(task.getId(), task.getTaskDefinitionKey(), null, SecurityUtils.getDeptId() + "", AuditTypeEnum.Forward.name(), form.getProjectId(), "t_project_info", task.getName() ); auditHistoryService.add(auditHistoryForm); return Result.ok("转办成功"); } public AuditHistoryForm buildForm( String taskId, String taskDefinitionKey, String viewer, String commitDept, String auditType, String businessKey, String businessTable, String content) { AuditHistoryForm auditHistoryForm = new AuditHistoryForm(); auditHistoryForm.setTaskId(taskId); auditHistoryForm.setTaskDefinitionKey(taskDefinitionKey); auditHistoryForm.setViewer(viewer); auditHistoryForm.setCommitDept(commitDept); auditHistoryForm.setAuditType(auditType); auditHistoryForm.setBusinessKey(businessKey); auditHistoryForm.setBusinessTable(businessTable); auditHistoryForm.setContent(content); // 5. 特殊字段默认值:isRead 默认为 (未读) auditHistoryForm.setIsRead(AuditHistoryEnum.UN_READ.name()); return auditHistoryForm; } @Override @Transactional(rollbackFor = Exception.class) public Result taskJump(TaskJumpForm form) { business/src/main/java/com/ycl/service/impl/WorkStationScheduleServiceImpl.java
@@ -3,8 +3,12 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.ycl.common.enums.business.WorkStationEnum; import com.ycl.common.utils.DateUtils; import com.ycl.common.utils.SecurityUtils; import com.ycl.common.utils.StringUtils; import com.ycl.domain.entity.ProjectInfo; import com.ycl.domain.entity.WorkStationSchedule; import com.ycl.domain.vo.DailyStatVO; import com.ycl.factory.FlowServiceFactory; import com.ycl.mapper.WorkStationScheduleMapper; import com.ycl.service.WorkStationScheduleService; @@ -13,6 +17,9 @@ import com.ycl.domain.form.WorkStationScheduleForm; import com.ycl.domain.vo.WorkStationScheduleVO; import com.ycl.domain.query.WorkStationScheduleQuery; import com.ycl.service.common.TaskCommonService; import org.flowable.task.api.Task; import org.flowable.task.api.TaskQuery; import org.flowable.task.api.history.HistoricTaskInstance; import org.flowable.task.api.history.HistoricTaskInstanceQuery; import org.springframework.stereotype.Service; @@ -22,8 +29,9 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import java.util.HashMap; import java.util.List; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.*; import java.util.stream.Collectors; /** @@ -39,6 +47,10 @@ private final WorkStationScheduleMapper workStationScheduleMapper; private final FlowServiceFactory flowServiceFactory; private final TaskCommonService taskCommonService; private final IndexHomeServiceImpl indexHomeServiceImpl; /** * 添加 * @param form @@ -47,6 +59,9 @@ @Override public Result add(WorkStationScheduleForm form) { WorkStationSchedule entity = WorkStationScheduleForm.getEntityByForm(form, null); Long userId = SecurityUtils.getUserId(); entity.setUserId(userId); entity.setStatus(WorkStationEnum.Incomplete.name()); baseMapper.insert(entity); return Result.ok("添加成功"); } @@ -101,6 +116,39 @@ return Result.ok().data(page.getRecords()).total(page.getTotal()); } @Override public Result listByDate(WorkStationScheduleQuery query) { Integer projectId = null; if (!"all".equals(query.getProjectId())){ projectId = Integer.valueOf(query.getProjectId()); } Long userId = SecurityUtils.getUserId(); //查询出指定时间段内的数据 List<WorkStationScheduleVO> list = baseMapper.groupByProjectAndDate(userId,projectId, DateUtils.getMonthStartTime(query.getCompletedTime()), DateUtils.getMonthEndTime(query.getCompletedTime())); List<DailyStatVO> dailyStatVOList = new ArrayList<>(); for (WorkStationScheduleVO vo : list) { if (vo == null) { continue; } DailyStatVO statVO = new DailyStatVO(); statVO.setId(vo.getId() == null ? -1 : vo.getId()); statVO.setTitle(StringUtils.isBlank(vo.getContent()) ? "无内容" : vo.getContent()); Map<String, String> stringTimeMap = DateUtils.splitDateToDateAndTime(vo.getCompletedTime()); String date = StringUtils.defaultIfBlank(stringTimeMap.get("date"), ""); String time = StringUtils.defaultIfBlank(stringTimeMap.get("time"), ""); statVO.setDate(date); statVO.setTime(time); statVO.setProject(StringUtils.isBlank(vo.getProjectName()) ? "未关联项目" : vo.getProjectName()); statVO.setProjectId(vo.getProjectId()); statVO.setCompletedTime(vo.getCompletedTime()); dailyStatVOList.add(statVO); } return Result.ok().data(dailyStatVOList); } /** * 根据id查找 * @param id @@ -123,6 +171,7 @@ List<WorkStationScheduleVO> vos = entities.stream() .map(entity -> WorkStationScheduleVO.getVoByEntity(entity, null)) .collect(Collectors.toList()); return Result.ok().data(vos); } @@ -142,9 +191,18 @@ HistoricTaskInstanceQuery query = flowServiceFactory.getHistoryService() .createHistoricTaskInstanceQuery() .finished() .taskAssignee(userId.toString()) .taskAssignee(SecurityUtils.getUserId() + "") .orderByHistoricTaskInstanceEndTime() .desc(); if (!SecurityUtils.getLoginUser().getUser().isAdmin()) { query .or() .taskCandidateGroupIn(taskCommonService.getCurrentUserGroups()) .taskCandidateUser(SecurityUtils.getUserId() + "") .taskAssignee(SecurityUtils.getUserId() + "") .endOr(); } List<HistoricTaskInstance> taskList = query.list(); long totalDuration = 0L; int taskCount = 0; @@ -163,7 +221,8 @@ double avgDurationMs = (double) totalDuration / taskCount; // 平均耗时(毫秒) double avgDurationSec = avgDurationMs / 1000; // 转换为秒 double avgDurationMin = avgDurationSec / 60; // 转换为分钟 double avgDurationHour = avgDurationMin / 60; // 转换为分钟 BigDecimal avgDurationHour = new BigDecimal(avgDurationMin / 60).setScale(2, RoundingMode.HALF_UP); // 转换为分钟 System.out.println("用户完成任务总数:" + taskCount); System.out.println("平均耗时(秒):" + avgDurationSec); System.out.println("平均耗时(分钟):" + avgDurationMin); @@ -183,10 +242,37 @@ } HashMap<String,Object> map = new HashMap<>(); map.put("totalDuration",totalDuration); map.put("avgDurationHour",avgDurationHour); map.put("scheduleCount",list.size()); map.put("completedCount",count); map.put("totalSchedules",list.size()); map.put("completedSchedules",count); map.put("avgDuration",avgDurationHour.doubleValue()); map.put("completedTasks",taskCount); return Result.ok().data(map); } @Override public Result countTodayTask() { Long userId = SecurityUtils.getUserId(); Date now = new Date(); Date dayStart = DateUtils.getDayStart(now); Date dayEnd = DateUtils.getDayEnd(now); List<WorkStationSchedule> list = new LambdaQueryChainWrapper<>(baseMapper) .eq(WorkStationSchedule::getUserId, SecurityUtils.getUserId()) .eq(WorkStationSchedule::getDeleted, Boolean.FALSE) .between(WorkStationSchedule::getCompletedTime, dayStart, dayEnd) .list(); HashMap<String,Object> map = new HashMap<>(); map.put("todaySchedules",list.size()); //查询今日处理的 的任务 long claimedAndCompletedCount = flowServiceFactory.getHistoryService().createHistoricTaskInstanceQuery() .finished() .taskAssignee(userId+"") // 任务由当前用户认领(处理人是该用户) .taskCompletedAfter(dayStart) .taskCompletedBefore(dayEnd) .count(); map.put("totalActiveTasks",claimedAndCompletedCount); int distinctTotal = indexHomeServiceImpl.countWaitTask(); map.put("todayTasks",distinctTotal); return Result.ok().data(map); } } business/src/main/resources/mapper/AuditHistoryMapper.xml
@@ -3,7 +3,7 @@ <mapper namespace="com.ycl.mapper.AuditHistoryMapper"> <!-- 通用查询映射结果 --> <resultMap id="BaseResultMap" type="com.ycl.domain.entity.AuditHistory"> <resultMap id="BaseResultMap" type="com.ycl.domain.vo.AuditHistoryVO"> <result column="task_id" property="taskId" /> <result column="task_definition_key" property="taskDefinitionKey" /> <result column="viewer" property="viewer" /> @@ -13,7 +13,36 @@ <result column="business_key" property="businessKey" /> <result column="business_table" property="businessTable" /> <result column="content" property="content" /> <result column="project_name" property="projectName" /> </resultMap> <select id="getPage" resultMap="BaseResultMap"> SELECT ah.task_id, ah.task_definition_key, ah.viewer, ah.commit_dept, ah.audit_type, ah.is_read, ah.business_key, ah.business_table, ah.content, pi.project_name FROM t_audit_history ah LEFT JOIN t_project_info pi ON ah.business_key = pi.id WHERE ah.deleted = 0 AND pi.deleted = 0 <if test="query.projectIds != null and query.projectIds.size() > 0"> AND ah.business_key IN <foreach collection="query.projectIds" item="id" open="(" separator="," close=")"> #{id} </foreach> </if> <if test="query.deptId != null"> AND ah.commit_dept = #{query.deptId} </if> ORDER BY ah.gmt_create DESC </select> business/src/main/resources/mapper/WorkStationScheduleMapper.xml
@@ -7,8 +7,8 @@ <result column="user_id" property="userId" /> <result column="content" property="content" /> <result column="status" property="status" /> <result column="gmtCreate" property="gmtcreate" /> <result column="gmtUpdate" property="gmtupdate" /> <result column="gmt_create" property="gmtCreate" /> <result column="gmt_update" property="gmtUpdate" /> </resultMap> @@ -41,4 +41,24 @@ TWSS.deleted = 0 </select> <select id="groupByProjectAndDate" resultType="com.ycl.domain.vo.WorkStationScheduleVO"> SELECT TWSS.id, TWSS.user_id, TWSS.content, TWSS.status, TWSS.project_id, TWSS.completed_time, TPI.project_name FROM t_work_station_schedule TWSS LEFT JOIN t_project_info TPI on TWSS.project_id = TPI.id WHERE TWSS.deleted = 0 AND TWSS.user_id = #{userId} AND TWSS.completed_time BETWEEN #{startTime} AND #{endTime} <if test="projectId != null"> AND TWSS.project_id = #{projectId} </if>; </select> </mapper> common/src/main/java/com/ycl/common/enums/business/AuditHistoryEnum.java
New file @@ -0,0 +1,17 @@ package com.ycl.common.enums.business; import lombok.Getter; @Getter public enum AuditHistoryEnum { UN_READ("未读"), READ("已读"); private String desc; AuditHistoryEnum(String desc) { this.desc = desc; } } common/src/main/java/com/ycl/common/enums/business/AuditTypeEnum.java
New file @@ -0,0 +1,15 @@ package com.ycl.common.enums.business; import lombok.Getter; @Getter public enum AuditTypeEnum { Submit("提交"), Review("审核"), Reject("驳回"), Forward("转交"); private String desc; AuditTypeEnum(String desc) { this.desc = desc; } } common/src/main/java/com/ycl/common/enums/business/WorkStationEnum.java
@@ -1,12 +1,15 @@ package com.ycl.common.enums.business; import lombok.Getter; /** * nongtou-project-java * * @author : zxl * @date : 2025-12-05 10:50 **/ @Getter public enum WorkStationEnum { COMPLETED("完成"), common/src/main/java/com/ycl/common/utils/DateUtils.java
@@ -5,10 +5,8 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.*; import java.util.Date; import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.time.format.DateTimeFormatter; import java.util.*; import org.apache.commons.lang3.time.DateFormatUtils; import org.springframework.lang.Nullable; @@ -38,6 +36,10 @@ private static String isHoliday = "1-1,1-28,1-29,1-30,1-31,2-1,2-2,2-3,2-4," +"4-4,4-5,4-6,5-1,5-2,5-3,5-4,5-5,5-31,6-1,6-2,10-1,10-2,10-3," +"10-4,10-5,10-6,10-7,10-8"; // 线程安全的格式化器(全局静态) private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); /** * 获取两个时间内,除去周末周日以及法定节假日的 小时总数。 @@ -309,4 +311,73 @@ LocalDateTime of = LocalDateTime.of(localDateTime.getYear(), localDateTime.getMonth(), localDateTime.getDayOfMonth(), 23, 59, 59); return Timestamp.valueOf(of); } /** * 获取指定时间所在月份的开始时间(当月1日 00:00:00) * @param date 任意时间(可为null,默认取当前时间) * @return 当月开始时间 */ public static Date getMonthStartTime(Date date) { Calendar calendar = Calendar.getInstance(); // 若传入时间为null,使用当前时间 if (date != null) { calendar.setTime(date); } // 设置为当月1日 calendar.set(Calendar.DAY_OF_MONTH, 1); // 设置为00:00:00 calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); // 毫秒置0,避免时间偏差 return calendar.getTime(); } /** * 获取指定时间所在月份的结束时间(当月最后一日 23:59:59) * @param date 任意时间(可为null,默认取当前时间) * @return 当月结束时间 */ public static Date getMonthEndTime(Date date) { Calendar calendar = Calendar.getInstance(); if (date != null) { calendar.setTime(date); } // 设置为当月最后一天 calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH)); // 设置为23:59:59 calendar.set(Calendar.HOUR_OF_DAY, 23); calendar.set(Calendar.MINUTE, 59); calendar.set(Calendar.SECOND, 59); calendar.set(Calendar.MILLISECOND, 999); // 毫秒置999,覆盖所有毫秒数 return calendar.getTime(); } /** * 拆分Date为年月日和时分字符串 * @param date 待拆分的Date(可为null) * @return Map:key=date(年月日)、time(时分),值为对应字符串;null返回空字符串 */ public static Map<String, String> splitDateToDateAndTime(Date date) { Map<String, String> result = new HashMap<>(); if (date == null) { result.put("date", ""); result.put("time", ""); return result; } // Date转LocalDateTime(处理时区) LocalDateTime localDateTime = LocalDateTime.ofInstant( date.toInstant(), ZoneId.systemDefault() // 用系统默认时区,避免时差 ); // 拆分年月日和时分 String dateStr = DATE_FORMATTER.format(localDateTime); String timeStr = TIME_FORMATTER.format(localDateTime); result.put("date", dateStr); result.put("time", timeStr); return result; } }