yusm 1 miesiąc temu
rodzic
commit
012eeddd7c

+ 17 - 4
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java

@@ -11,16 +11,13 @@ import com.management.platform.config.NewLimitRequest;
 import com.management.platform.constant.Constant;
 import com.management.platform.entity.*;
 import com.management.platform.entity.vo.MonthWorkingTimeVO;
-import com.management.platform.entity.vo.ReportRateTaskVO;
 import com.management.platform.entity.vo.SysRichFunction;
 import com.management.platform.entity.vo.WorktimeItem;
 import com.management.platform.mapper.*;
 import com.management.platform.service.*;
-import com.management.platform.service.impl.ProjectConstructionStageServiceImpl;
 import com.management.platform.service.impl.WxCorpInfoServiceImpl;
 import com.management.platform.util.*;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.*;
 import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.client.RestTemplate;
@@ -34,7 +31,9 @@ import java.math.RoundingMode;
 import java.text.DecimalFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.time.*;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.YearMonth;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.time.temporal.TemporalAdjusters;
@@ -3676,6 +3675,20 @@ public class ReportController {
         return reportService.getDeptProjectCount(user.getCompanyId(), ymonth);
     }
 
+    //工时分析报告综合数据
+    @RequestMapping("/getDashboardAnalysisReport")
+    public HttpRespMsg getDashboardAnalysisReport(String ymonth) {
+        User user = userMapper.selectById(request.getHeader("TOKEN"));
+        return reportService.getDashboardAnalysisReport(user.getCompanyId(), ymonth);
+    }
+
+    //有工时投入项目明细,分页加载
+    @RequestMapping("/getWorkedProjectList")
+    public HttpRespMsg getWorkedProjectList(String ymonth, Integer pageIndex, Integer pageSize) {
+        User user = userMapper.selectById(request.getHeader("TOKEN"));
+        return reportService.getWorkedProjectList(user.getCompanyId(), ymonth, pageIndex, pageSize);
+    }
+
     @RequestMapping("/getAllReport")
     public HttpRespMsg getAllReport(Integer companyId) {
         HttpRespMsg msg = new HttpRespMsg();

+ 4 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ReportService.java

@@ -239,5 +239,9 @@ public interface ReportService extends IService<Report> {
 
     HttpRespMsg getDeptProjectCount(Integer companyId, String ymonth);
 
+    HttpRespMsg getDashboardAnalysisReport(Integer companyId, String ymonth);
+
+    HttpRespMsg getWorkedProjectList(Integer companyId, String ymonth, Integer pageIndex, Integer pageSize);
+
     HttpRespMsg getAllReportListByToken(String json);
 }

+ 448 - 49
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java

@@ -16038,20 +16038,40 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     @Override
     public HttpRespMsg getTop10ProjectReport(Integer companyId, String ymonth) {
         HttpRespMsg msg = new HttpRespMsg();
-        List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().select("id, project_code, project_name").eq("company_id", companyId));
-        List<Report> list = reportMapper.selectList(new QueryWrapper<Report>().select("sum(working_time) as working_time, sum(overtime_hours) as overtime_hours, project_id").eq("state", 1).eq("company_id", companyId).eq("date_format(create_date, '%Y-%m')", ymonth).groupBy("project_id").orderByDesc("sum(working_time)").last("limit 10"));
-        list.forEach(report->{
-            Project proj = projectList.stream().filter(p->p.getId().equals(report.getProjectId())).findFirst().get();
-            report.setProjectName(proj.getProjectName());
-            report.setContent(proj.getProjectCode());
-        });
+        String month = normalizeYearMonth(ymonth);
+        List<Map<String, Object>> reportList = reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("project_id as projectId", "sum(working_time) as workingTime", "sum(ifnull(overtime_hours, 0)) as overtimeHours")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .groupBy("project_id")
+                .orderByDesc("sum(working_time)")
+                .last("limit 10"));
+        if (reportList.isEmpty()) {
+            msg.data = new ArrayList<>();
+            return msg;
+        }
+
+        List<Integer> projectIds = reportList.stream()
+                .map(row -> toInteger(row.get("projectId")))
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+        Map<Integer, Project> projectMap = projectIds.isEmpty() ? new HashMap<>() : projectMapper.selectList(new QueryWrapper<Project>()
+                .select("id, project_code, project_name")
+                .eq("company_id", companyId)
+                .in("id", projectIds))
+                .stream().collect(Collectors.toMap(Project::getId, Function.identity(), (left, right) -> left));
+
         List<HashMap> resultList = new ArrayList<>();
-        list.forEach(report->{
+        reportList.forEach(row -> {
+            Integer projectId = toInteger(row.get("projectId"));
+            Project project = projectMap.get(projectId);
             HashMap<String, Object> map = new HashMap<>();
-            map.put("projectCode", report.getContent());
-            map.put("projectName", report.getProjectName());
-            map.put("workingTime", report.getWorkingTime());
-            map.put("overtimeHours", report.getOvertimeHours());
+            map.put("projectId", projectId);
+            map.put("projectCode", project == null ? "未关联项目" : project.getProjectCode());
+            map.put("projectName", project == null ? "未关联项目" : project.getProjectName());
+            map.put("workingTime", roundHours(row.get("workingTime")));
+            map.put("overtimeHours", roundHours(row.get("overtimeHours")));
             resultList.add(map);
         });
         msg.data = resultList;
@@ -16061,44 +16081,82 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     @Override
     public HttpRespMsg getTop3ProjectReportGroupByDept(Integer companyId, String ymonth) {
         HttpRespMsg msg = new HttpRespMsg();
-        List<Report> list = reportMapper.selectList(new QueryWrapper<Report>().select("project_id").eq("company_id", companyId).eq("state", 1).eq("date_format(create_date, '%Y-%m')", ymonth).groupBy("project_id").orderByDesc("sum(working_time)").last("limit 3"));
-        List<Integer> pids = list.stream().map(Report::getProjectId).collect(Collectors.toList());
-        if (!pids.isEmpty()) {
-            List<Report> deptReportList = reportMapper.selectList(new QueryWrapper<Report>().select("dept_id, sum(working_time) as working_time, sum(overtime_hours) as overtime_hours").eq("company_id", companyId).eq("state", 1).eq("date_format(create_date, '%Y-%m')", ymonth).groupBy("dept_id").in("project_id", pids));
-            List<Department> departmentList = departmentMapper.selectList(new QueryWrapper<Department>().select("department_id, department_name").eq("company_id", companyId));
-            List<HashMap> resultList = new ArrayList<>();
-            deptReportList.forEach(report->{
-                HashMap<String, Object> map = new HashMap<>();
-                Department curDept = departmentList.stream().filter(dept->dept.getDepartmentId().equals(report.getDeptId())).findFirst().get();
-                map.put("departmentName", curDept.getDepartmentName());
-                map.put("workingTime", report.getWorkingTime());
-                map.put("overtimeHours", report.getOvertimeHours());
-                resultList.add(map);
-            });
-            msg.data = resultList;
+        String month = normalizeYearMonth(ymonth);
+        List<Map<String, Object>> topProjectList = reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("project_id as projectId")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .groupBy("project_id")
+                .orderByDesc("sum(working_time)")
+                .last("limit 3"));
+        List<Integer> projectIds = topProjectList.stream()
+                .map(row -> toInteger(row.get("projectId")))
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+        if (projectIds.isEmpty()) {
+            msg.data = new ArrayList<>();
+            return msg;
         }
 
+        List<Map<String, Object>> deptReportList = reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("dept_id as deptId", "sum(working_time) as workingTime", "sum(ifnull(overtime_hours, 0)) as overtimeHours")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .in("project_id", projectIds)
+                .groupBy("dept_id")
+                .orderByDesc("sum(working_time)"));
+        Map<Integer, Department> departmentMap = getDepartmentMap(companyId);
+        List<HashMap> resultList = buildDeptReportResult(deptReportList, departmentMap, false);
+        msg.data = resultList;
         return msg;
     }
 
     @Override
     public HttpRespMsg getProjectReportGroupByDept(Integer companyId, String ymonth) {
         HttpRespMsg msg = new HttpRespMsg();
-        List<Report> deptReportList = reportMapper.selectList(new QueryWrapper<Report>().select("dept_id, sum(working_time) as working_time,count(distinct creator_id) as project_id, sum(overtime_hours) as overtime_hours").eq("company_id", companyId).eq("state", 1).eq("date_format(create_date, '%Y-%m')", ymonth).groupBy("dept_id"));
-        List<Department> departmentList = departmentMapper.selectList(new QueryWrapper<Department>().select("department_id, department_name").eq("company_id", companyId));
+        String month = normalizeYearMonth(ymonth);
+        List<Map<String, Object>> deptReportList = reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("dept_id as deptId", "sum(working_time) as workingTime", "count(distinct creator_id) as memberCount", "sum(ifnull(overtime_hours, 0)) as overtimeHours")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .groupBy("dept_id")
+                .orderByDesc("sum(working_time)"));
+        Map<Integer, Department> departmentMap = getDepartmentMap(companyId);
+        List<HashMap> resultList = buildDeptReportResult(deptReportList, departmentMap, true);
+        msg.data = resultList;
+        return msg;
+    }
+
+    @Override
+    public HttpRespMsg getDeptProjectCount(Integer companyId, String ymonth) {
+        HttpRespMsg msg = new HttpRespMsg();
+        String month = normalizeYearMonth(ymonth);
+        List<Map<String, Object>> deptReportList = reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("dept_id as deptId", "count(distinct project_id) as projectCount", "count(distinct if(ifnull(overtime_hours, 0) > 0, project_id, null)) as overtimeProjectCount", "count(distinct creator_id) as memberCount")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .groupBy("dept_id")
+                .orderByDesc("count(distinct project_id)"));
+        Map<Integer, Department> departmentMap = getDepartmentMap(companyId);
         List<HashMap> resultList = new ArrayList<>();
-        deptReportList.forEach(report->{
+        deptReportList.forEach(row -> {
+            Integer deptId = toInteger(row.get("deptId"));
+            Department department = departmentMap.get(deptId);
             HashMap<String, Object> map = new HashMap<>();
-            Department curDept = departmentList.stream().filter(dept->dept.getDepartmentId().equals(report.getDeptId())).findFirst().get();
-            map.put("departmentName", curDept.getDepartmentName());
-            map.put("workingTime", report.getWorkingTime());
-            map.put("overtimeHours", report.getOvertimeHours());
-            int membCount = report.getProjectId();
-            //计算人均工时和加班工时
-            double avgWorkingTime = report.getWorkingTime() / membCount;
-            double avgOvertimeHours = report.getOvertimeHours() / membCount;
-            map.put("avgWorkingTime", avgWorkingTime);
-            map.put("avgOvertimeHours", avgOvertimeHours);
+            map.put("departmentId", deptId);
+            map.put("departmentName", department == null ? "未分配部门" : department.getDepartmentName());
+            map.put("corpwxDeptid", department == null ? null : department.getCorpwxDeptid());
+            map.put("departmentOpenId", department == null ? null : department.getCorpwxDeptid());
+            double projectCount = toDouble(row.get("projectCount"));
+            double memberCount = Math.max(toDouble(row.get("memberCount")), 1D);
+            map.put("projectCount", projectCount);
+            map.put("overtimeProjectCount", toDouble(row.get("overtimeProjectCount")));
+            map.put("memberCount", toDouble(row.get("memberCount")));
+            map.put("avgProjectCount", roundHours(projectCount / memberCount));
             resultList.add(map);
         });
         msg.data = resultList;
@@ -16106,20 +16164,361 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
-    public HttpRespMsg getDeptProjectCount(Integer companyId, String ymonth) {
+    public HttpRespMsg getDashboardAnalysisReport(Integer companyId, String ymonth) {
         HttpRespMsg msg = new HttpRespMsg();
-        List<Report> deptReportList = reportMapper.selectList(new QueryWrapper<Report>().select("dept_id, count(distinct project_id) as project_id").eq("company_id", companyId).eq("state", 1).eq("date_format(create_date, '%Y-%m')", ymonth).groupBy("dept_id"));
-        List<Department> departmentList = departmentMapper.selectList(new QueryWrapper<Department>().select("department_id, department_name").eq("company_id", companyId));
+        String month = normalizeYearMonth(ymonth);
+        Map<Integer, Department> departmentMap = getDepartmentMap(companyId);
+        Map<String, Object> result = new HashMap<>();
+        result.put("projectSituation", getProjectSituation(companyId, month));
+        result.put("projectOvertimeTop5", getProjectRank(companyId, month, "sum(ifnull(overtime_hours, 0))", 5, true));
+        result.put("top3OvertimeProjectDept", getTopProjectDeptReport(companyId, month, "sum(ifnull(overtime_hours, 0))", true));
+        result.put("deptOvertimeSummary", buildDeptReportResult(reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("dept_id as deptId", "sum(working_time) as workingTime", "count(distinct creator_id) as memberCount", "sum(ifnull(overtime_hours, 0)) as overtimeHours", "max(ifnull(overtime_hours, 0)) as maxOvertimeHours")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .groupBy("dept_id")
+                .orderByDesc("sum(ifnull(overtime_hours, 0))")), departmentMap, true));
+        result.put("deptProjectTop3", getDeptProjectTopN(companyId, month, departmentMap, false));
+        result.put("deptOvertimeProjectTop3", getDeptProjectTopN(companyId, month, departmentMap, true));
+        result.put("deptUserOvertimeTop3", getDeptUserOvertimeTopN(companyId, month, departmentMap));
+        result.put("userOvertimeTop10", getUserOvertimeTopN(companyId, month, departmentMap));
+        msg.data = result;
+        return msg;
+    }
+
+    @Override
+    public HttpRespMsg getWorkedProjectList(Integer companyId, String ymonth, Integer pageIndex, Integer pageSize) {
+        HttpRespMsg msg = new HttpRespMsg();
+        String month = normalizeYearMonth(ymonth);
+        int currentPage = pageIndex == null || pageIndex < 1 ? 1 : pageIndex;
+        int size = pageSize == null || pageSize < 1 ? 30 : Math.min(pageSize, 100);
+        int offset = (currentPage - 1) * size;
+
+        List<Map<String, Object>> countList = reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("count(distinct project_id) as total")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month));
+        int total = countList.isEmpty() ? 0 : toInteger(countList.get(0).get("total"));
+
+        List<Map<String, Object>> reportList = reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("project_id as projectId", "sum(working_time) as workingTime", "sum(ifnull(overtime_hours, 0)) as overtimeHours")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .groupBy("project_id")
+                .orderByDesc("sum(working_time)")
+                .last("limit " + offset + "," + size));
+
+        HashMap<String, Object> result = new HashMap<>();
+        result.put("list", fillProjectInfo(companyId, reportList));
+        result.put("total", total);
+        result.put("pageIndex", currentPage);
+        result.put("pageSize", size);
+        result.put("hasMore", offset + reportList.size() < total);
+        msg.data = result;
+        return msg;
+    }
+
+    private String normalizeYearMonth(String ymonth) {
+        if (StringUtils.isEmpty(ymonth)) {
+            return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"));
+        }
+        return ymonth;
+    }
+
+    private Map<Integer, Department> getDepartmentMap(Integer companyId) {
+        return departmentMapper.selectList(new QueryWrapper<Department>()
+                .select("department_id, department_name, corpwx_deptid")
+                .eq("company_id", companyId))
+                .stream()
+                .collect(Collectors.toMap(Department::getDepartmentId, Function.identity(), (left, right) -> left));
+    }
+
+    private HashMap<String, Object> getProjectSituation(Integer companyId, String month) {
+        long projectCount = projectMapper.selectCount(new QueryWrapper<Project>().eq("company_id", companyId));
+        long executingProjectCount = projectMapper.selectCount(new QueryWrapper<Project>().eq("company_id", companyId).eq("status", 1));
+        List<Map<String, Object>> workedProjectList = reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("project_id as projectId", "sum(working_time) as workingTime", "sum(ifnull(overtime_hours, 0)) as overtimeHours")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .groupBy("project_id"));
+        double totalWorkingTime = workedProjectList.stream().mapToDouble(row -> toDouble(row.get("workingTime"))).sum();
+        double totalOvertimeHours = workedProjectList.stream().mapToDouble(row -> toDouble(row.get("overtimeHours"))).sum();
+        HashMap<String, Object> map = new HashMap<>();
+        map.put("projectCount", projectCount);
+        map.put("executingProjectCount", executingProjectCount);
+        map.put("workedProjectCount", workedProjectList.size());
+        map.put("notWorkedExecutingProjectCount", Math.max(executingProjectCount - workedProjectList.size(), 0));
+        map.put("totalWorkingTime", roundHours(totalWorkingTime));
+        map.put("totalOvertimeHours", roundHours(totalOvertimeHours));
+        return map;
+    }
+
+    private List<HashMap> getProjectRank(Integer companyId, String month, String orderExpression, int limit, boolean overtimeOnly) {
+        QueryWrapper<Report> queryWrapper = new QueryWrapper<Report>()
+                .select("project_id as projectId", "sum(working_time) as workingTime", "sum(ifnull(overtime_hours, 0)) as overtimeHours")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .groupBy("project_id")
+                .having(overtimeOnly, "sum(ifnull(overtime_hours, 0)) > 0")
+                .orderByDesc(orderExpression);
+        if (limit > 0 && limit < Integer.MAX_VALUE) {
+            queryWrapper.last("limit " + limit);
+        }
+        List<Map<String, Object>> reportList = reportMapper.selectMaps(queryWrapper);
+        return fillProjectInfo(companyId, reportList);
+    }
+
+    private List<HashMap> getTopProjectDeptReport(Integer companyId, String month, String orderExpression, boolean orderByOvertime) {
+        List<Map<String, Object>> topProjectList = reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("project_id as projectId")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .groupBy("project_id")
+                .having(orderByOvertime, "sum(ifnull(overtime_hours, 0)) > 0")
+                .orderByDesc(orderExpression)
+                .last("limit 3"));
+        List<Integer> projectIds = topProjectList.stream()
+                .map(row -> toInteger(row.get("projectId")))
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+        if (projectIds.isEmpty()) {
+            return new ArrayList<>();
+        }
+        List<Map<String, Object>> deptReportList = reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("dept_id as deptId", "sum(working_time) as workingTime", "sum(ifnull(overtime_hours, 0)) as overtimeHours")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .in("project_id", projectIds)
+                .groupBy("dept_id")
+                .orderByDesc(orderByOvertime ? "sum(ifnull(overtime_hours, 0))" : "sum(working_time)"));
+        return buildDeptReportResult(deptReportList, getDepartmentMap(companyId), false);
+    }
+
+    private List<HashMap> getDeptProjectTopN(Integer companyId, String month, Map<Integer, Department> departmentMap, boolean overtimeOnly) {
+        List<Map<String, Object>> reportList = reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("dept_id as deptId", "project_id as projectId", "sum(working_time) as workingTime", "sum(ifnull(overtime_hours, 0)) as overtimeHours")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .groupBy("dept_id", "project_id")
+                .having(overtimeOnly, "sum(ifnull(overtime_hours, 0)) > 0")
+                .orderByAsc("dept_id")
+                .orderByDesc(overtimeOnly ? "sum(ifnull(overtime_hours, 0))" : "sum(working_time)"));
+        Map<Integer, List<HashMap>> grouped = new LinkedHashMap<>();
+        fillProjectInfo(companyId, reportList).forEach(item -> {
+            Integer deptId = toInteger(item.get("deptId"));
+            Department department = departmentMap.get(deptId);
+            item.put("departmentName", department == null ? "未分配部门" : department.getDepartmentName());
+            item.put("corpwxDeptid", department == null ? null : department.getCorpwxDeptid());
+            item.put("departmentOpenId", department == null ? null : department.getCorpwxDeptid());
+            List<HashMap> list = grouped.computeIfAbsent(deptId == null ? -1 : deptId, key -> new ArrayList<>());
+            if (list.size() < 3) {
+                list.add(item);
+            }
+        });
+        List<HashMap> resultList = new ArrayList<>();
+        grouped.forEach((deptId, items) -> {
+            HashMap<String, Object> deptMap = new HashMap<>();
+            Department department = departmentMap.get(deptId);
+            deptMap.put("departmentId", deptId == -1 ? null : deptId);
+            deptMap.put("departmentName", department == null ? "未分配部门" : department.getDepartmentName());
+            deptMap.put("corpwxDeptid", department == null ? null : department.getCorpwxDeptid());
+            deptMap.put("departmentOpenId", department == null ? null : department.getCorpwxDeptid());
+            deptMap.put("items", items);
+            resultList.add(deptMap);
+        });
+        return resultList;
+    }
+
+    private List<HashMap> getDeptUserOvertimeTopN(Integer companyId, String month, Map<Integer, Department> departmentMap) {
+        List<Map<String, Object>> reportList = reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("dept_id as deptId", "creator_id as userId", "sum(ifnull(overtime_hours, 0)) as overtimeHours")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .groupBy("dept_id", "creator_id")
+                .having("sum(ifnull(overtime_hours, 0)) > 0")
+                .orderByAsc("dept_id")
+                .orderByDesc("sum(ifnull(overtime_hours, 0))"));
+        List<String> userIds = reportList.stream().map(row -> String.valueOf(row.get("userId"))).filter(id -> !StringUtils.isEmpty(id)).collect(Collectors.toList());
+        Map<String, User> userMap = userIds.isEmpty() ? new HashMap<>() : userMapper.selectList(new QueryWrapper<User>().select("id, name, corpwx_userid").in("id", userIds))
+                .stream().collect(Collectors.toMap(User::getId, Function.identity(), (left, right) -> left));
+        Map<Integer, List<HashMap>> grouped = new LinkedHashMap<>();
+        reportList.forEach(row -> {
+            Integer deptId = toInteger(row.get("deptId"));
+            List<HashMap> list = grouped.computeIfAbsent(deptId == null ? -1 : deptId, key -> new ArrayList<>());
+            if (list.size() >= 3) {
+                return;
+            }
+            String userId = String.valueOf(row.get("userId"));
+            User user = userMap.get(userId);
+            HashMap<String, Object> map = new HashMap<>();
+            map.put("departmentId", deptId);
+            Department department = departmentMap.get(deptId);
+            map.put("departmentName", department == null ? "未分配部门" : department.getDepartmentName());
+            map.put("corpwxDeptid", department == null ? null : department.getCorpwxDeptid());
+            map.put("departmentOpenId", department == null ? null : department.getCorpwxDeptid());
+            map.put("userId", userId);
+            map.put("userName", user == null ? userId : user.getName());
+            map.put("corpwxUserid", user == null ? null : user.getCorpwxUserid());
+            map.put("overtimeHours", roundHours(row.get("overtimeHours")));
+            list.add(map);
+        });
+        List<HashMap> resultList = new ArrayList<>();
+        grouped.forEach((deptId, items) -> {
+            HashMap<String, Object> deptMap = new HashMap<>();
+            Department department = departmentMap.get(deptId);
+            deptMap.put("departmentId", deptId == -1 ? null : deptId);
+            deptMap.put("departmentName", department == null ? "未分配部门" : department.getDepartmentName());
+            deptMap.put("corpwxDeptid", department == null ? null : department.getCorpwxDeptid());
+            deptMap.put("departmentOpenId", department == null ? null : department.getCorpwxDeptid());
+            deptMap.put("items", items);
+            resultList.add(deptMap);
+        });
+        return resultList;
+    }
+
+    private List<HashMap> getUserOvertimeTopN(Integer companyId, String month, Map<Integer, Department> departmentMap) {
+        List<Map<String, Object>> reportList = reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("creator_id as userId", "dept_id as deptId", "sum(ifnull(overtime_hours, 0)) as overtimeHours")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .groupBy("creator_id", "dept_id")
+                .having("sum(ifnull(overtime_hours, 0)) > 0")
+                .orderByDesc("sum(ifnull(overtime_hours, 0))")
+                .last("limit 10"));
+        List<String> userIds = reportList.stream()
+                .map(row -> String.valueOf(row.get("userId")))
+                .filter(id -> !StringUtils.isEmpty(id))
+                .collect(Collectors.toList());
+        Map<String, User> userMap = userIds.isEmpty() ? new HashMap<>() : userMapper.selectList(new QueryWrapper<User>()
+                .select("id, name, corpwx_userid")
+                .in("id", userIds))
+                .stream()
+                .collect(Collectors.toMap(User::getId, Function.identity(), (left, right) -> left));
         List<HashMap> resultList = new ArrayList<>();
-        deptReportList.forEach(report->{
+        reportList.forEach(row -> {
+            Integer deptId = toInteger(row.get("deptId"));
+            Department department = departmentMap.get(deptId);
+            String userId = String.valueOf(row.get("userId"));
+            User user = userMap.get(userId);
             HashMap<String, Object> map = new HashMap<>();
-            Department curDept = departmentList.stream().filter(dept->dept.getDepartmentId().equals(report.getDeptId())).findFirst().get();
-            map.put("departmentName", curDept.getDepartmentName());
-            map.put("projectCount", report.getProjectId());
+            map.put("departmentId", deptId);
+            map.put("departmentName", department == null ? "未分配部门" : department.getDepartmentName());
+            map.put("corpwxDeptid", department == null ? null : department.getCorpwxDeptid());
+            map.put("departmentOpenId", department == null ? null : department.getCorpwxDeptid());
+            map.put("userId", userId);
+            map.put("userName", user == null ? userId : user.getName());
+            map.put("corpwxUserid", user == null ? null : user.getCorpwxUserid());
+            map.put("overtimeHours", roundHours(row.get("overtimeHours")));
             resultList.add(map);
         });
-        msg.data = resultList;
-        return msg;
+        return resultList;
+    }
+
+    private List<HashMap> fillProjectInfo(Integer companyId, List<Map<String, Object>> reportList) {
+        if (reportList.isEmpty()) {
+            return new ArrayList<>();
+        }
+        List<Integer> projectIds = reportList.stream()
+                .map(row -> toInteger(row.get("projectId")))
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+        Map<Integer, Project> projectMap = projectIds.isEmpty() ? new HashMap<>() : projectMapper.selectList(new QueryWrapper<Project>()
+                .select("id, project_code, project_name, incharger_id")
+                .eq("company_id", companyId)
+                .in("id", projectIds))
+                .stream().collect(Collectors.toMap(Project::getId, Function.identity(), (left, right) -> left));
+        List<String> inchargerIds = projectMap.values().stream()
+                .map(Project::getInchargerId)
+                .filter(id -> !StringUtils.isEmpty(id))
+                .distinct()
+                .collect(Collectors.toList());
+        Map<String, User> inchargerMap = inchargerIds.isEmpty() ? new HashMap<>() : userMapper.selectList(new QueryWrapper<User>()
+                .select("id, name, corpwx_userid")
+                .eq("company_id", companyId)
+                .in("id", inchargerIds))
+                .stream().collect(Collectors.toMap(User::getId, Function.identity(), (left, right) -> left));
+        List<HashMap> resultList = new ArrayList<>();
+        reportList.forEach(row -> {
+            Integer projectId = toInteger(row.get("projectId"));
+            Project project = projectMap.get(projectId);
+            User incharger = project == null ? null : inchargerMap.get(project.getInchargerId());
+            HashMap<String, Object> map = new HashMap<>();
+            map.putAll(row);
+            map.put("projectId", projectId);
+            map.put("projectCode", project == null ? "未关联项目" : project.getProjectCode());
+            map.put("projectName", project == null ? "未关联项目" : project.getProjectName());
+            map.put("inchargerId", project == null ? null : project.getInchargerId());
+            map.put("inchargerName", incharger == null ? "" : incharger.getName());
+            map.put("inchargerCorpwxUserid", incharger == null ? null : incharger.getCorpwxUserid());
+            map.put("contactUserId", project == null ? null : project.getInchargerId());
+            map.put("contactUserName", incharger == null ? "" : incharger.getName());
+            map.put("contactCorpwxUserid", incharger == null ? null : incharger.getCorpwxUserid());
+            map.put("workingTime", roundHours(row.get("workingTime")));
+            map.put("overtimeHours", roundHours(row.get("overtimeHours")));
+            resultList.add(map);
+        });
+        return resultList;
+    }
+
+    private List<HashMap> buildDeptReportResult(List<Map<String, Object>> deptReportList, Map<Integer, Department> departmentMap, boolean includeAverage) {
+        List<HashMap> resultList = new ArrayList<>();
+        deptReportList.forEach(row -> {
+            Integer deptId = toInteger(row.get("deptId"));
+            Department department = departmentMap.get(deptId);
+            double workingTime = roundHours(row.get("workingTime"));
+            double overtimeHours = roundHours(row.get("overtimeHours"));
+            HashMap<String, Object> map = new HashMap<>();
+            map.put("departmentId", deptId);
+            map.put("departmentName", department == null ? "未分配部门" : department.getDepartmentName());
+            map.put("corpwxDeptid", department == null ? null : department.getCorpwxDeptid());
+            map.put("departmentOpenId", department == null ? null : department.getCorpwxDeptid());
+            map.put("workingTime", workingTime);
+            map.put("overtimeHours", overtimeHours);
+            if (row.containsKey("maxOvertimeHours")) {
+                map.put("maxOvertimeHours", roundHours(row.get("maxOvertimeHours")));
+            }
+            if (includeAverage) {
+                double memberCount = Math.max(toDouble(row.get("memberCount")), 1D);
+                map.put("avgWorkingTime", roundHours(workingTime / memberCount));
+                map.put("avgOvertimeHours", roundHours(overtimeHours / memberCount));
+            }
+            resultList.add(map);
+        });
+        return resultList;
+    }
+
+    private Integer toInteger(Object value) {
+        if (value == null) {
+            return null;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).intValue();
+        }
+        return Integer.valueOf(String.valueOf(value));
+    }
+
+    private double toDouble(Object value) {
+        if (value == null) {
+            return 0D;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).doubleValue();
+        }
+        return Double.parseDouble(String.valueOf(value));
+    }
+
+    private double roundHours(Object value) {
+        return BigDecimal.valueOf(toDouble(value)).setScale(2, RoundingMode.HALF_UP).doubleValue();
     }
 
     @Override

Plik diff jest za duży
+ 1759 - 406
fhKeeper/formulahousekeeper/timesheet/src/views/dashboard/index.vue