From 2bfb3b6f474ebe6ad0cb46b99cdfd67aa8f5c898 Mon Sep 17 00:00:00 2001 From: zhihao <18915542763@163.com> Date: Tue, 9 Jun 2026 09:47:09 +0800 Subject: [PATCH] =?UTF-8?q?czh-20260609-=E5=A2=9E=E5=8A=A0=E9=97=A8?= =?UTF-8?q?=E6=88=B7=E5=8D=A1=E7=89=87=E8=8E=B7=E5=8F=96=E2=80=9C=E5=BE=85?= =?UTF-8?q?=E5=8A=9E=E4=BA=8B=E9=A1=B9=E3=80=81=E6=9C=AC=E6=9C=88=E5=B7=B2?= =?UTF-8?q?=E5=8A=9E=E3=80=81=E5=B9=B3=E5=9D=87=E6=97=B6=E9=95=BF=E3=80=81?= =?UTF-8?q?=E8=B6=85=E6=97=B6=E9=A2=84=E8=AD=A6=E2=80=9D=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .codegraph/.gitignore | 16 ++++++ .../controller/ProcessPortalController.java | 34 +++++++++++ .../dashboard/dto/ProcessPortalStatsDTO.java | 22 +++++++ .../dashboard/mapper/ProcessPortalMapper.java | 57 +++++++++++++++++++ .../service/IProcessPortalService.java | 8 +++ .../impl/ProcessPortalServiceImpl.java | 52 +++++++++++++++++ 6 files changed, 189 insertions(+) create mode 100644 .codegraph/.gitignore create mode 100644 jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/controller/ProcessPortalController.java create mode 100644 jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/dto/ProcessPortalStatsDTO.java create mode 100644 jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/mapper/ProcessPortalMapper.java create mode 100644 jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/service/IProcessPortalService.java create mode 100644 jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/service/impl/ProcessPortalServiceImpl.java diff --git a/.codegraph/.gitignore b/.codegraph/.gitignore new file mode 100644 index 0000000..9de0f16 --- /dev/null +++ b/.codegraph/.gitignore @@ -0,0 +1,16 @@ +# CodeGraph data files +# These are local to each machine and should not be committed + +# Database +*.db +*.db-wal +*.db-shm + +# Cache +cache/ + +# Logs +*.log + +# Hook markers +.dirty diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/controller/ProcessPortalController.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/controller/ProcessPortalController.java new file mode 100644 index 0000000..b8d7622 --- /dev/null +++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/controller/ProcessPortalController.java @@ -0,0 +1,34 @@ +package org.jeecg.modules.dashboard.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.modules.dashboard.dto.ProcessPortalStatsDTO; +import org.jeecg.modules.dashboard.service.IProcessPortalService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@Tag(name = "流程门户") +@RestController +@RequestMapping("/dashboard/processPortal") +@Slf4j +public class ProcessPortalController { + + @Resource + private IProcessPortalService processPortalService; + + @Operation(summary = "流程门户-获取统计卡片数据") + @GetMapping("/stats") + public Result getStats() { + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + String userId = sysUser.getUsername(); + ProcessPortalStatsDTO stats = processPortalService.getStats(userId); + return Result.OK(stats); + } +} diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/dto/ProcessPortalStatsDTO.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/dto/ProcessPortalStatsDTO.java new file mode 100644 index 0000000..8987030 --- /dev/null +++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/dto/ProcessPortalStatsDTO.java @@ -0,0 +1,22 @@ +package org.jeecg.modules.dashboard.dto; + +import lombok.Data; + +/** + * 流程门户 - 统计卡片 DTO + */ +@Data +public class ProcessPortalStatsDTO { + + /** 待办事项数 */ + private Long todoCount; + + /** 本月已办数 */ + private Long doneThisMonth; + + /** 平均时长 */ + private String avgDuration; + + /** 超时预警数 */ + private Long overdueCount; +} diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/mapper/ProcessPortalMapper.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/mapper/ProcessPortalMapper.java new file mode 100644 index 0000000..c53b4ac --- /dev/null +++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/mapper/ProcessPortalMapper.java @@ -0,0 +1,57 @@ +package org.jeecg.modules.dashboard.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +@Mapper +public interface ProcessPortalMapper { + + /** + * 当前用户待办任务数(直接分配 + 候选人/候选组) + * 与 ActivitiServiceImpl#findBaseTodoTasks 逻辑一致 + */ + @Select("SELECT COUNT(*) FROM (" + + "(SELECT DISTINCT RES.ID_ FROM ACT_RU_TASK RES " + + "INNER JOIN ACT_RU_IDENTITYLINK I ON I.TASK_ID_ = RES.ID_ " + + "WHERE RES.ASSIGNEE_ IS NULL AND I.TYPE_ = 'candidate' " + + "AND (I.USER_ID_ = #{userId} OR I.GROUP_ID_ IN (SELECT g.GROUP_ID_ FROM ACT_ID_MEMBERSHIP g WHERE g.USER_ID_ = #{userId})) " + + "AND RES.SUSPENSION_STATE_ = 1) " + + "UNION " + + "(SELECT DISTINCT RES.ID_ FROM ACT_RU_TASK RES " + + "WHERE RES.ASSIGNEE_ = #{userId} AND RES.SUSPENSION_STATE_ = 1)" + + ") T") + Long countTodo(@Param("userId") String userId); + + /** + * 当前用户本月已办任务数 + * 与 ActivitiMapper#getHistoryTasks 逻辑一致:用 DURATION_ > 0 判定已完成 + */ + @Select("SELECT COUNT(*) FROM ACT_HI_TASKINST " + + "WHERE ASSIGNEE_ = #{userId} AND DURATION_ > 0 AND END_TIME_ >= #{startOfMonth}") + Long countDoneThisMonth(@Param("userId") String userId, @Param("startOfMonth") String startOfMonth); + + /** + * 当前用户任务平均处理时长(毫秒) + */ + @Select("SELECT COALESCE(AVG(DURATION_), 0) FROM ACT_HI_TASKINST " + + "WHERE ASSIGNEE_ = #{userId} AND DURATION_ > 0") + Long avgDurationMillis(@Param("userId") String userId); + + /** + * 当前用户超时未完成任务数(直接分配 + 候选人/候选组) + * deadline 变量类型为 localdatetime,存储在 LONG_ 列(epoch millis) + */ + @Select("SELECT COUNT(DISTINCT T.ID_) FROM ACT_RU_TASK T " + + "INNER JOIN ACT_RU_VARIABLE V ON V.PROC_INST_ID_ = T.PROC_INST_ID_ AND V.NAME_ = 'deadline' " + + "WHERE T.SUSPENSION_STATE_ = 1 " + + "AND (T.ASSIGNEE_ = #{userId} " + + " OR T.ID_ IN (" + + " SELECT I.TASK_ID_ FROM ACT_RU_IDENTITYLINK I " + + " WHERE I.TYPE_ = 'candidate' " + + " AND (I.USER_ID_ = #{userId} OR I.GROUP_ID_ IN (SELECT g.GROUP_ID_ FROM ACT_ID_MEMBERSHIP g WHERE g.USER_ID_ = #{userId}))" + + " )) " + + "AND V.LONG_ IS NOT NULL " + + "AND FROM_UNIXTIME(V.LONG_ / 1000) < CURDATE()") + Long countOverdue(@Param("userId") String userId); +} diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/service/IProcessPortalService.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/service/IProcessPortalService.java new file mode 100644 index 0000000..07040b0 --- /dev/null +++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/service/IProcessPortalService.java @@ -0,0 +1,8 @@ +package org.jeecg.modules.dashboard.service; + +import org.jeecg.modules.dashboard.dto.ProcessPortalStatsDTO; + +public interface IProcessPortalService { + + ProcessPortalStatsDTO getStats(String userId); +} diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/service/impl/ProcessPortalServiceImpl.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/service/impl/ProcessPortalServiceImpl.java new file mode 100644 index 0000000..df2b4e5 --- /dev/null +++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/dashboard/service/impl/ProcessPortalServiceImpl.java @@ -0,0 +1,52 @@ +package org.jeecg.modules.dashboard.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.jeecg.modules.dashboard.dto.ProcessPortalStatsDTO; +import org.jeecg.modules.dashboard.mapper.ProcessPortalMapper; +import org.jeecg.modules.dashboard.service.IProcessPortalService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +@Service +@Slf4j +public class ProcessPortalServiceImpl implements IProcessPortalService { + + @Resource + private ProcessPortalMapper processPortalMapper; + + @Override + public ProcessPortalStatsDTO getStats(String userId) { + ProcessPortalStatsDTO dto = new ProcessPortalStatsDTO(); + + String startOfMonth = LocalDate.now().withDayOfMonth(1).format(DateTimeFormatter.ISO_LOCAL_DATE) + " 00:00:00"; + + dto.setTodoCount(processPortalMapper.countTodo(userId)); + dto.setDoneThisMonth(processPortalMapper.countDoneThisMonth(userId, startOfMonth)); + + Long avgMillis = processPortalMapper.avgDurationMillis(userId); + dto.setAvgDuration(formatDuration(avgMillis)); + + dto.setOverdueCount(processPortalMapper.countOverdue(userId)); + + log.info("【流程门户-统计】查询统计完成, userId: {}, 待办: {}, 已办: {}, 平均时长: {}, 超时: {}", + userId, dto.getTodoCount(), dto.getDoneThisMonth(), dto.getAvgDuration(), dto.getOverdueCount()); + return dto; + } + + private String formatDuration(Long millis) { + if (millis == null || millis == 0) { + return "0时"; + } + double hours = millis / (1000.0 * 60 * 60); + if (hours < 1) { + return String.format("%.0f分", millis / (1000.0 * 60)); + } + if (hours < 24) { + return String.format("%.1f时", hours); + } + return String.format("%.1f天", hours / 24); + } +}