diff --git a/excel-handle/src/main/java/com/ruoyi/excel/wecom/domain/CorpDepartment.java b/excel-handle/src/main/java/com/ruoyi/excel/wecom/domain/CorpDepartment.java index 5f7bb51..135840c 100644 --- a/excel-handle/src/main/java/com/ruoyi/excel/wecom/domain/CorpDepartment.java +++ b/excel-handle/src/main/java/com/ruoyi/excel/wecom/domain/CorpDepartment.java @@ -12,9 +12,14 @@ import java.io.Serializable; @Data @TableName("corp_department") public class CorpDepartment implements Serializable { + private static final long serialVersionUID = 1L; + private Long id; + private String corpId; + private Long parentid; + private Long orderNo; private String name; diff --git a/excel-handle/src/main/java/com/ruoyi/excel/wecom/dto/CorpDepartmentDTO.java b/excel-handle/src/main/java/com/ruoyi/excel/wecom/dto/CorpDepartmentDTO.java new file mode 100644 index 0000000..b19d731 --- /dev/null +++ b/excel-handle/src/main/java/com/ruoyi/excel/wecom/dto/CorpDepartmentDTO.java @@ -0,0 +1,14 @@ +package com.ruoyi.excel.wecom.dto; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class CorpDepartmentDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private String corpId; + + private String name; +} diff --git a/excel-handle/src/main/java/com/ruoyi/excel/wecom/helper/HandleAllData.java b/excel-handle/src/main/java/com/ruoyi/excel/wecom/helper/HandleAllData.java index 29bde49..633af87 100644 --- a/excel-handle/src/main/java/com/ruoyi/excel/wecom/helper/HandleAllData.java +++ b/excel-handle/src/main/java/com/ruoyi/excel/wecom/helper/HandleAllData.java @@ -153,81 +153,69 @@ import java.util.concurrent.atomic.AtomicInteger; } /** - * 创建所有日期的流量看板数据(包含当日统计)- 多线程版本 + * 创建所有日期的流量看板数据(包含当日统计)- 优化版 + * 采用分批处理策略,控制并发数量,避免CPU持续满载 */ public void createAllReportData() { List corpInfos = corpInfoMapper.selectCorpInfoList(new CorpInfo()); - corpInfos.forEach(item-> { + int batchSize = 10; + + for (CorpInfo item : corpInfos) { try { String corpId = item.getCorpId(); - List allDate = customerExportService.getAllDate(corpId); - - // 创建任务列表 - List> futures = new ArrayList<>(); - - // 为每个日期创建异步任务 - for (Date date : allDate) { - CompletableFuture future = CompletableFuture.runAsync(() -> { - try { - createReportData(corpId,date); - } catch (Exception e) { - throw new RuntimeException("处理日期 " + date + " 的报表数据失败: " + e.getMessage(), e); - } - }, executorService); - - futures.add(future); - } - - // 等待所有任务完成 - try { + + for (int i = 0; i < allDate.size(); i += batchSize) { + int end = Math.min(i + batchSize, allDate.size()); + List batchDates = allDate.subList(i, end); + + List> futures = new ArrayList<>(); + for (Date date : batchDates) { + CompletableFuture future = CompletableFuture.runAsync(() -> { + createReportData(corpId, date); + }, executorService); + futures.add(future); + } + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); - } catch (Exception e) { - throw new RuntimeException("多线程处理流量看板数据时发生错误: " + e.getMessage(), e); } - } catch (Exception e) { + } catch (Exception e) { throw new RuntimeException("多线程处理流量看板数据时发生错误: " + e.getMessage(), e); } - }); + } } /** - * 创建所有部门指标数据 - 多线程版本 + * 创建所有部门指标数据 - 优化版 + * 采用分批处理策略,控制并发数量,避免CPU持续满载 */ public void createAllDepartmentReportData() { List corpInfos = corpInfoMapper.selectCorpInfoList(new CorpInfo()); - corpInfos.forEach(item-> { + int batchSize = 10; + + for (CorpInfo item : corpInfos) { try { - // 为每个日期创建异步任务 - String finalCorpId = item.getCorpId(); - List allDate = customerExportService.getAllDate(finalCorpId); - - // 创建任务列表 - List> futures = new ArrayList<>(); - - - for (Date date : allDate) { - CompletableFuture future = CompletableFuture.runAsync(() -> { - try { - createDepartmentReportData(finalCorpId,date); - } catch (Exception e) { - throw new RuntimeException("处理日期 " + date + " 的部门报表数据失败: " + e.getMessage(), e); - } - }, executorService); - - futures.add(future); - } - - // 等待所有任务完成 - try { + String corpId = item.getCorpId(); + List allDate = customerExportService.getAllDate(corpId); + + for (int i = 0; i < allDate.size(); i += batchSize) { + int end = Math.min(i + batchSize, allDate.size()); + List batchDates = allDate.subList(i, end); + + List> futures = new ArrayList<>(); + for (Date date : batchDates) { + CompletableFuture future = CompletableFuture.runAsync(() -> { + createDepartmentReportData(corpId, date); + }, executorService); + futures.add(future); + } + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); - } catch (Exception e) { - throw new RuntimeException("多线程处理部门报表数据时发生错误: " + e.getMessage(), e); } } catch (Exception e) { throw new RuntimeException("多线程处理部门报表数据时发生错误: " + e.getMessage(), e); } - }); + } } /** @@ -389,7 +377,8 @@ import java.util.concurrent.atomic.AtomicInteger; * @return 统计结果列表 */ /** - * 计算统计数据 - 使用分页查询+单次遍历方案 + * 计算统计数据 - 优化版 + * SQL层面过滤日期,减少数据传输和Java层过滤开销 * @param date 目标日期 (格式: yyyy-MM-dd) * @return 统计结果列表 */ @@ -397,23 +386,21 @@ import java.util.concurrent.atomic.AtomicInteger; // 1. 初始化累加器 StatisticsAccumulator accumulator = new StatisticsAccumulator(); - // 2. 分页查询并累加统计 - int pageSize = 1000; // 每页1000条 + // 2. 分页查询并累加统计(SQL层面直接过滤日期) + int pageSize = 1000; int pageNum = 1; - LambdaQueryWrapper wrapper= new LambdaQueryWrapper<>(); - wrapper.eq(CustomerExportData::getCorpId,corpId); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(CustomerExportData::getCorpId, corpId) + .eq(CustomerExportData::getAddDate, date); + while (true) { - // 分页查询(不在SQL层过滤,在Java层过滤以保证准确性) Page page = new Page<>(pageNum, pageSize); Page pageData = customerExportDataMapper.selectPage(page, wrapper); - // 处理当前页数据 for (CustomerExportData data : pageData.getRecords()) { - // 处理该条数据,累加到所有相关组的统计中 processDataRecord(data, date, accumulator); } - // 检查是否还有下一页 if (!pageData.hasNext()) { break; } @@ -426,7 +413,8 @@ import java.util.concurrent.atomic.AtomicInteger; /** - * 计算部门维度的统计数据(当日) + * 计算部门维度的统计数据(当日)- 优化版 + * SQL层面过滤日期,减少数据传输和Java层过滤开销 * @param targetDate 目标日期 (格式: yyyy-MM-dd) * @return 部门统计结果列表 */ @@ -434,17 +422,17 @@ import java.util.concurrent.atomic.AtomicInteger; // 1. 初始化部门累加器 DepartmentStatisticsAccumulator accumulator = new DepartmentStatisticsAccumulator(); - // 2. 分页查询并累加统计 + // 2. 分页查询并累加统计(SQL层面直接过滤日期) int pageSize = 1000; int pageNum = 1; LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.eq(CustomerExportData::getCorpId,corpId); + wrapper.eq(CustomerExportData::getCorpId, corpId) + .eq(CustomerExportData::getAddDate, targetDate); + while (true) { - // 分页查询 Page page = new Page<>(pageNum, pageSize); Page pageData = customerExportDataMapper.selectPage(page, wrapper); - // 处理当前页数据 for (CustomerExportData data : pageData.getRecords()) { // 获取部门路径 String departmentPath = data.getAddUserDepartment(); @@ -454,7 +442,6 @@ import java.util.concurrent.atomic.AtomicInteger; // 先计算所有条件,避免重复判断 boolean isQValueMatch = matchesQValue(data, targetDate); - boolean isDateMatch = matchesDate(data, targetDate); boolean isSourceMatch = matchesSource(data); boolean isTimelyOrder = isTimelyOrder(data); String customerAttr = data.getTagGroup6(); @@ -474,7 +461,7 @@ import java.util.concurrent.atomic.AtomicInteger; String currentPath = pathBuilder.toString(); DepartmentStatisticsAccumulator.DepartmentStats stats = accumulator.getDepartmentStats(currentPath); - // 累加统计(条件已预先计算,直接使用) + // 累加统计(SQL已过滤日期,所有数据都是目标日期的) if (isQValueMatch) { stats.setTotalOrderCount(stats.getTotalOrderCount() + 1); if (isTimelyOrder) { @@ -484,26 +471,23 @@ import java.util.concurrent.atomic.AtomicInteger; } } - if (isDateMatch) { - if (isSourceMatch) { - stats.setTotalAcceptCount(stats.getTotalAcceptCount() + 1); - if (isParent) { - stats.setParentDailyCount(stats.getParentDailyCount() + 1); - if (isQValueMatch && isTimelyOrder) { - stats.setParentDailyOrderCount(stats.getParentDailyOrderCount() + 1); - } - } else if (isStudent) { - stats.setStudentDailyCount(stats.getStudentDailyCount() + 1); - if (isQValueMatch && isTimelyOrder) { - stats.setStudentDailyOrderCount(stats.getStudentDailyOrderCount() + 1); - } + if (isSourceMatch) { + stats.setTotalAcceptCount(stats.getTotalAcceptCount() + 1); + if (isParent) { + stats.setParentDailyCount(stats.getParentDailyCount() + 1); + if (isQValueMatch && isTimelyOrder) { + stats.setParentDailyOrderCount(stats.getParentDailyOrderCount() + 1); + } + } else if (isStudent) { + stats.setStudentDailyCount(stats.getStudentDailyCount() + 1); + if (isQValueMatch && isTimelyOrder) { + stats.setStudentDailyOrderCount(stats.getStudentDailyOrderCount() + 1); } - } else { - stats.setManagerAcceptCount(stats.getManagerAcceptCount() + 1); } + } else { + stats.setManagerAcceptCount(stats.getManagerAcceptCount() + 1); } } - } // 检查是否还有下一页 diff --git a/excel-handle/src/main/java/com/ruoyi/excel/wecom/mapper/CorpDepartmentMapper.java b/excel-handle/src/main/java/com/ruoyi/excel/wecom/mapper/CorpDepartmentMapper.java index adbaba8..625ab93 100644 --- a/excel-handle/src/main/java/com/ruoyi/excel/wecom/mapper/CorpDepartmentMapper.java +++ b/excel-handle/src/main/java/com/ruoyi/excel/wecom/mapper/CorpDepartmentMapper.java @@ -2,15 +2,14 @@ package com.ruoyi.excel.wecom.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.excel.wecom.domain.CorpDepartment; -import com.ruoyi.excel.wecom.domain.CorpUser; +import com.ruoyi.excel.wecom.dto.CorpDepartmentDTO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; -/** - * 部门mapper - */ @Mapper public interface CorpDepartmentMapper extends BaseMapper { - + List selectCorpDepartmentList(@Param("dto") CorpDepartmentDTO dto); } - diff --git a/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/ICorpDepartmentService.java b/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/ICorpDepartmentService.java new file mode 100644 index 0000000..5843b4b --- /dev/null +++ b/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/ICorpDepartmentService.java @@ -0,0 +1,10 @@ +package com.ruoyi.excel.wecom.service; + +import com.ruoyi.excel.wecom.vo.CorpDepartmentVO; + +import java.util.List; + +public interface ICorpDepartmentService { + + List selectCorpDepartmentTree(String corpId); +} diff --git a/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/impl/CorpDepartmentServiceImpl.java b/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/impl/CorpDepartmentServiceImpl.java new file mode 100644 index 0000000..2e1e30e --- /dev/null +++ b/excel-handle/src/main/java/com/ruoyi/excel/wecom/service/impl/CorpDepartmentServiceImpl.java @@ -0,0 +1,125 @@ +package com.ruoyi.excel.wecom.service.impl; + +import com.ruoyi.excel.wecom.domain.CorpDepartment; +import com.ruoyi.excel.wecom.dto.CorpDepartmentDTO; +import com.ruoyi.excel.wecom.mapper.CorpDepartmentMapper; +import com.ruoyi.excel.wecom.service.ICorpDepartmentService; +import com.ruoyi.excel.wecom.vo.CorpDepartmentVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +public class CorpDepartmentServiceImpl implements ICorpDepartmentService { + + @Autowired + private CorpDepartmentMapper corpDepartmentMapper; + + @Override + public List selectCorpDepartmentTree(String corpId) { + CorpDepartmentDTO query = new CorpDepartmentDTO(); + query.setCorpId(corpId); + List allDepartments = corpDepartmentMapper.selectCorpDepartmentList(query); + + Map deptMap = new HashMap<>(); + Map parentMap = new HashMap<>(); + + for (CorpDepartment dept : allDepartments) { + deptMap.put(dept.getId(), dept); + parentMap.put(dept.getId(), dept.getParentid()); + } + + Map ancestorsMap = new HashMap<>(); + for (CorpDepartment dept : allDepartments) { + String fullPath = buildDepartmentPath(dept.getId(), deptMap, parentMap); + ancestorsMap.put(dept.getId(), fullPath); + } + + return buildTree(allDepartments, ancestorsMap); + } + + private String buildDepartmentPath(Long deptId, Map deptMap, Map parentMap) { + if (deptId == null || !deptMap.containsKey(deptId)) { + return ""; + } + + CorpDepartment dept = deptMap.get(deptId); + String currentName = dept.getName(); + Long parentId = parentMap.get(deptId); + + if (parentId == null || parentId == 0L || !parentMap.containsKey(parentId)) { + return currentName; + } + + String parentPath = buildDepartmentPath(parentId, deptMap, parentMap); + if (parentPath.isEmpty()) { + return currentName; + } else { + return parentPath + "/" + currentName; + } + } + + private List buildTree(List departments, Map ancestorsMap) { + List result = new ArrayList<>(); + List ids = departments.stream().map(CorpDepartment::getId).collect(Collectors.toList()); + + for (CorpDepartment dept : departments) { + Long parentId = dept.getParentid(); + if (parentId == null || parentId == 0L || !ids.contains(parentId)) { + CorpDepartmentVO vo = convertToVO(dept, ancestorsMap.get(dept.getId())); + recursionFn(departments, vo, ancestorsMap); + result.add(vo); + } + } + + if (result.isEmpty()) { + for (CorpDepartment dept : departments) { + result.add(convertToVO(dept, ancestorsMap.get(dept.getId()))); + } + } + return result; + } + + private CorpDepartmentVO convertToVO(CorpDepartment dept, String ancestors) { + CorpDepartmentVO vo = new CorpDepartmentVO(); + vo.setId(dept.getId()); + vo.setName(dept.getName()); + vo.setAncestors(ancestors); + return vo; + } + + private void recursionFn(List list, CorpDepartmentVO parent, Map ancestorsMap) { + List childList = getChildList(list, parent, ancestorsMap); + parent.setChildren(childList); + for (CorpDepartmentVO child : childList) { + if (hasChild(list, child)) { + recursionFn(list, child, ancestorsMap); + } + } + } + + private List getChildList(List list, CorpDepartmentVO parent, Map ancestorsMap) { + List result = new ArrayList<>(); + for (CorpDepartment dept : list) { + if (dept.getParentid() != null && dept.getParentid().longValue() == parent.getId().longValue()) { + CorpDepartmentVO vo = convertToVO(dept, ancestorsMap.get(dept.getId())); + result.add(vo); + } + } + return result; + } + + private boolean hasChild(List list, CorpDepartmentVO t) { + for (CorpDepartment dept : list) { + if (dept.getParentid() != null && dept.getParentid().longValue() == t.getId().longValue()) { + return true; + } + } + return false; + } +} diff --git a/excel-handle/src/main/java/com/ruoyi/excel/wecom/vo/CorpDepartmentVO.java b/excel-handle/src/main/java/com/ruoyi/excel/wecom/vo/CorpDepartmentVO.java new file mode 100644 index 0000000..bdc1caf --- /dev/null +++ b/excel-handle/src/main/java/com/ruoyi/excel/wecom/vo/CorpDepartmentVO.java @@ -0,0 +1,19 @@ +package com.ruoyi.excel.wecom.vo; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +public class CorpDepartmentVO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + + private String name; + + private String ancestors; + + private List children; +} diff --git a/excel-handle/src/main/resources/mapper/wecom/CorpDepartmentMapper.xml b/excel-handle/src/main/resources/mapper/wecom/CorpDepartmentMapper.xml new file mode 100644 index 0000000..d7601d2 --- /dev/null +++ b/excel-handle/src/main/resources/mapper/wecom/CorpDepartmentMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + diff --git a/excel-handle/src/main/resources/mapper/wecom/DepartmentStatisticsDataMapper.xml b/excel-handle/src/main/resources/mapper/wecom/DepartmentStatisticsDataMapper.xml index 2778372..a9ccd9f 100644 --- a/excel-handle/src/main/resources/mapper/wecom/DepartmentStatisticsDataMapper.xml +++ b/excel-handle/src/main/resources/mapper/wecom/DepartmentStatisticsDataMapper.xml @@ -130,7 +130,7 @@ sum(ifnull(daily_total_accepted,0)+ifnull(manager_accepted,0)) as totalIns from department_statistics_data - corp_id = #{corpId} and person_flag = true + corp_id = #{corpId} AND stat_date >= #{startDate} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wocom/CorpDepartmentController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wocom/CorpDepartmentController.java new file mode 100644 index 0000000..058a776 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wocom/CorpDepartmentController.java @@ -0,0 +1,27 @@ +package com.ruoyi.web.controller.wocom; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.CorpContextHolder; +import com.ruoyi.excel.wecom.service.ICorpDepartmentService; +import com.ruoyi.excel.wecom.vo.CorpDepartmentVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/wecom/department") +public class CorpDepartmentController { + + @Autowired + private ICorpDepartmentService corpDepartmentService; + + @GetMapping("/tree") + public AjaxResult getDepartmentTree() { + String corpId = CorpContextHolder.getCurrentCorpId(); + List tree = corpDepartmentService.selectCorpDepartmentTree(corpId); + return AjaxResult.success(tree); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wocom/DepartmentStatisticsDataController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wocom/DepartmentStatisticsDataController.java index 89ce0f3..585a2d8 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wocom/DepartmentStatisticsDataController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wocom/DepartmentStatisticsDataController.java @@ -111,7 +111,6 @@ public class DepartmentStatisticsDataController extends BaseController { Date[] dateRange = calculateDateRange(dataType, year, week, month, startDate, endDate); startDate = dateRange[0]; endDate = dateRange[1]; - Map map = departmentStatisticsDataService.getSummary(corpId, startDate, endDate, departmentPath, dataType); if(map == null) { return success(new HashMap<>()); diff --git a/ruoyi-ui/src/api/wecom/departmentStatistics.js b/ruoyi-ui/src/api/wecom/departmentStatistics.js index 7e0928f..8a31075 100644 --- a/ruoyi-ui/src/api/wecom/departmentStatistics.js +++ b/ruoyi-ui/src/api/wecom/departmentStatistics.js @@ -61,3 +61,11 @@ export function getDepartmentStatisticsSummary(query) { params: query }) } + +// 获取部门树 +export function getDepartmentTree() { + return request({ + url: '/wecom/department/tree', + method: 'get' + }) +} diff --git a/ruoyi-ui/src/views/wecom/departmentStatistics/index.vue b/ruoyi-ui/src/views/wecom/departmentStatistics/index.vue index 0a1ac9a..8483012 100644 --- a/ruoyi-ui/src/views/wecom/departmentStatistics/index.vue +++ b/ruoyi-ui/src/views/wecom/departmentStatistics/index.vue @@ -1,6 +1,28 @@ + +