From a179482251b5ab16fcb17b89bd76bd1b4c7ffd82 Mon Sep 17 00:00:00 2001 From: WendyYang Date: Tue, 2 Jan 2024 19:33:38 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dashboard/controller/StatisticsController.java | 88 ++++++ .../dashboard/manage/DashboardExpertManage.java | 164 ++++++++++ .../dashboard/manage/ExpertStatisticsManage.java | 345 +++++++++++++++++++++ .../dashboard/manage/MeetingStatisticsManage.java | 201 ++++++++++++ .../dashboard/manage/ProjectStatisticsManage.java | 24 ++ .../dashboard/model/vo/ProjectStatisticsVO.java | 54 ++++ .../hz/pm/api/fiscal/entity/CompanyFiscalCode.java | 5 +- .../com/hz/pm/api/user/manage/UserInfoManage.java | 6 +- 8 files changed, 883 insertions(+), 4 deletions(-) create mode 100644 hz-pm-api/src/main/java/com/hz/pm/api/dashboard/controller/StatisticsController.java create mode 100644 hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/DashboardExpertManage.java create mode 100644 hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/ExpertStatisticsManage.java create mode 100644 hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/MeetingStatisticsManage.java create mode 100644 hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/ProjectStatisticsManage.java create mode 100644 hz-pm-api/src/main/java/com/hz/pm/api/dashboard/model/vo/ProjectStatisticsVO.java diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/controller/StatisticsController.java b/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/controller/StatisticsController.java new file mode 100644 index 0000000..546e8d3 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/controller/StatisticsController.java @@ -0,0 +1,88 @@ +package com.hz.pm.api.dashboard.controller; + +import com.hz.pm.api.dashboard.manage.*; +import com.hz.pm.api.dashboard.model.vo.*; +import com.ningdatech.log.annotation.DataScopeQueryCheck; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author ZPF + * @date 2023/11/25 上午10:36 + */ +@RestController +@RequestMapping("/api/v1/statistics") +@Validated +@RequiredArgsConstructor +@Api(value = "StatisticsController", tags = "统计") +public class StatisticsController { + + private final FundStatisticsManage fundStatisticsManage; + + private final ExpertStatisticsManage expertStatisticsManage; + + private final MeetingStatisticsManage meetingStatisticsManage; + + private final ProjectCostStatisticsManage costStatisticsManage; + + private final EarlyWarningStatisticsManage earlyWarningStatisticsManage; + + private final PerformanceStatisticsManage performanceStatisticsManage; + + private final ProjectStatisticsManage projectStatisticsManage; + + @DataScopeQueryCheck + @GetMapping("/fund") + @ApiOperation("项目资金统计") + public FundStatisticsVO fund(@RequestParam(required = false) Integer year) { + return fundStatisticsManage.statistics(year); + } + + @DataScopeQueryCheck + @GetMapping("/expert") + @ApiOperation("专家统计") + public ExpertStatisticsVO expert(@RequestParam(required = false) Integer year) { + return expertStatisticsManage.statistics(year); + } + + @DataScopeQueryCheck + @GetMapping("/meeting") + @ApiOperation("评审会议统计") + public MeetingStatisticsVO meeting(@RequestParam(required = false) Integer year) { + return meetingStatisticsManage.statistics(year); + } + + @DataScopeQueryCheck + @GetMapping("/project-cost") + @ApiOperation("项目成本统计") + public CostStatisticsVO projectCost() { + return costStatisticsManage.statistics(); + } + + @DataScopeQueryCheck + @GetMapping("/early-warning") + @ApiOperation("预警统计") + public EarlyWarningStatisticsVO earlyWarning(@RequestParam(required = false) Integer year) { + return earlyWarningStatisticsManage.statistics(year); + } + + @DataScopeQueryCheck + @GetMapping("/performance") + @ApiOperation("绩效统计") + public PerformanceStatisticsVO performance(@RequestParam(required = false) Integer year) { + return performanceStatisticsManage.statistics(year); + } + + @DataScopeQueryCheck + @GetMapping("/project") + @ApiOperation("项目统计") + public ProjectStatisticsVO project(@RequestParam(required = false) Integer year) { + return projectStatisticsManage.statistics(year); + } +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/DashboardExpertManage.java b/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/DashboardExpertManage.java new file mode 100644 index 0000000..34b6d30 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/DashboardExpertManage.java @@ -0,0 +1,164 @@ +package com.hz.pm.api.dashboard.manage; + +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.hz.pm.api.dashboard.constant.ChartTypeEnum; +import com.hz.pm.api.dashboard.helper.DashboardChartAssembler; +import com.hz.pm.api.dashboard.helper.DashboardHelper; +import com.hz.pm.api.dashboard.model.basic.AnalysisChart; +import com.hz.pm.api.dashboard.model.basic.AnalysisData; +import com.hz.pm.api.dashboard.model.basic.StarExpertBO; +import com.hz.pm.api.dashboard.model.po.QueryYearPO; +import com.hz.pm.api.dashboard.model.vo.ExpertDashboardSummaryVO; +import com.hz.pm.api.expert.constant.ExpertUserInfoStepEnum; +import com.hz.pm.api.expert.entity.ExpertUserFullInfo; +import com.hz.pm.api.expert.service.IExpertUserFullInfoService; +import com.hz.pm.api.meeting.entity.domain.Meeting; +import com.hz.pm.api.meeting.entity.domain.MeetingExpertJudge; +import com.hz.pm.api.meeting.entity.enumeration.MeetingStatusEnum; +import com.hz.pm.api.meeting.service.IMeetingExpertJudgeService; +import com.hz.pm.api.meeting.service.IMeetingService; +import com.hz.pm.api.meta.constant.ExpertDictTypeEnum; +import com.hz.pm.api.meta.model.entity.ExpertDictionary; +import com.hz.pm.api.meta.service.IExpertDictionaryService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author liuxinxin + * @date 2023/8/2 上午10:39 + */ + +@Component +@RequiredArgsConstructor +public class DashboardExpertManage { + + private final DashboardHelper dashboardHelper; + private final IExpertUserFullInfoService iExpertUserFullInfoService; + private final IMeetingService iMeetingService; + private final IMeetingExpertJudgeService iMeetingExpertJudgeService; + private final IExpertDictionaryService iExpertDictionaryService; + private final DashboardChartAssembler dashboardChartAssembler; + + + public ExpertDashboardSummaryVO getExpertDashboardSummary(QueryYearPO queryYearPO) { + String queryRegionCode = queryYearPO.getRegionCode(); + List analysisChartList = new ArrayList<>(); + + // 获取丽水区域 code name Map + Map liShuiRegionCodeNameMap = dashboardHelper.getLiShuiRegionCodeNameMap(); + // 获取库内所有的专家列表 + List evidenceHasBeenSubmittedExpertInfoList = iExpertUserFullInfoService.list(); + + // 专家regionCode分组map列表 + Map> regionCodeExpertMap = Collections.emptyMap(); + + // 各区域专家数量 + AnalysisChart regionExpertNumberChartAnalysisChart = new AnalysisChart(); + List regionExpertNumberChartDataList = new ArrayList<>(); + regionExpertNumberChartAnalysisChart.setChartType(ChartTypeEnum.REGION_EXPERT_NUMBER_CHART); + regionExpertNumberChartAnalysisChart.setDataList(regionExpertNumberChartDataList); + for (String regionCode : liShuiRegionCodeNameMap.keySet()) { + AnalysisData analysisData = new AnalysisData(); + String regionName = liShuiRegionCodeNameMap.get(regionCode); + List expertUserFullInfoList = regionCodeExpertMap.get(regionCode); + int expertCnt = 0; + if (CollectionUtil.isNotEmpty(expertUserFullInfoList)) { + expertCnt = expertUserFullInfoList.size(); + } + analysisData.setKey(regionName); + analysisData.setValue(expertCnt); + regionExpertNumberChartDataList.add(analysisData); + } + analysisChartList.add(regionExpertNumberChartAnalysisChart); + + // 查询区域的专家id 列表 + List regionDegreeExpertIdList = new ArrayList<>(); + if (StringUtils.isNotBlank(queryRegionCode)) { + List expertUserFullInfoList = regionCodeExpertMap.get(queryRegionCode); + if (CollectionUtil.isNotEmpty(expertUserFullInfoList)) { + regionDegreeExpertIdList = expertUserFullInfoList.stream() + .map(ExpertUserFullInfo::getUserId) + .collect(Collectors.toList()); + } + } else { + regionDegreeExpertIdList = evidenceHasBeenSubmittedExpertInfoList.stream() + .map(ExpertUserFullInfo::getUserId) + .collect(Collectors.toList()); + } + + if (CollectionUtil.isNotEmpty(regionDegreeExpertIdList)) { + // 区域学历分布 + List degreeExpertDictionaryList = iExpertDictionaryService + .listByUserId(regionDegreeExpertIdList, ExpertDictTypeEnum.DEGREE); + Map> degreeCodeMap = degreeExpertDictionaryList.stream() + .map(ExpertDictionary::getDictionaryCode) + .collect(Collectors.groupingBy(Function.identity())); + AnalysisChart regionExpertEducationChartAnalysisChart = + dashboardChartAssembler.assemblerAnalysisChart(degreeCodeMap, ChartTypeEnum.REGION_EXPERT_EDUCATION_CHART); + analysisChartList.add(regionExpertEducationChartAnalysisChart); + + // 区域职称级别分布 + List titleLevelExpertDictionaryList = iExpertDictionaryService + .listByUserId(regionDegreeExpertIdList, ExpertDictTypeEnum.TITLE_LEVEL); + Map> titleLevelCodeMap = titleLevelExpertDictionaryList.stream() + .collect(Collectors.groupingBy(ExpertDictionary::getDictionaryCode)); + AnalysisChart regionExpertTitleLevelChartAnalysisChart = + dashboardChartAssembler.assemblerAnalysisChart(titleLevelCodeMap, ChartTypeEnum.REGION_EXPERT_TITLE_LEVEL_CHART); + analysisChartList.add(regionExpertTitleLevelChartAnalysisChart); + } + + // 评审次数 + List normalMeetingList = iMeetingService.list(Wrappers.lambdaQuery(Meeting.class) + .ne(Meeting::getStatus, MeetingStatusEnum.CANCELED.getCode())); + Integer meetingCnt = normalMeetingList.size(); + + // 各类型评审次数 + Map> meetingTypeMap = normalMeetingList.stream().collect(Collectors.groupingBy(Meeting::getType)); + AnalysisChart meetingTypeCntChartAnalysisChart = + dashboardChartAssembler.assemblerAnalysisChart(meetingTypeMap, ChartTypeEnum.MEETING_TYPE_CNT_CHART); + analysisChartList.add(meetingTypeCntChartAnalysisChart); + + // 明星专家列表 + List starExpertList = new ArrayList<>(); + List meetingExpertJudgeList = iMeetingExpertJudgeService.list(); + Map> expertIdMeetingExpertJudgeMap = meetingExpertJudgeList.stream() + .collect(Collectors.groupingBy(MeetingExpertJudge::getExpertId)); + Map expertIdExpertNameMap = evidenceHasBeenSubmittedExpertInfoList.stream() + .collect(Collectors.toMap(ExpertUserFullInfo::getUserId, ExpertUserFullInfo::getExpertName)); + + for (Long expertId : expertIdMeetingExpertJudgeMap.keySet()) { + String expertName = expertIdExpertNameMap.get(expertId); + List expertMeetingExpertJudgeList = expertIdMeetingExpertJudgeMap.get(expertId); + DoubleSummaryStatistics statistics = expertMeetingExpertJudgeList + .stream().filter(r -> Objects.nonNull(r.getScore())) + .map(MeetingExpertJudge::getScore).mapToDouble(Number::doubleValue).summaryStatistics(); + double average = statistics.getAverage(); + StarExpertBO starExpertBO = new StarExpertBO(); + starExpertBO.setAveragePerformanceScore(average); + starExpertBO.setExpertId(expertId); + starExpertBO.setExpertName(expertName); + starExpertList.add(starExpertBO); + } + starExpertList = starExpertList.stream() + .sorted(Comparator.comparing(StarExpertBO::getAveragePerformanceScore) + .reversed()).collect(Collectors.toList()); + if (starExpertList.size() > 5) { + starExpertList = starExpertList.subList(0, 5); + } + + // 装配返回类 + ExpertDashboardSummaryVO expertDashboardSummaryVO = new ExpertDashboardSummaryVO(); + expertDashboardSummaryVO.setMeetingCnt(meetingCnt); + expertDashboardSummaryVO.setStarExpertList(starExpertList); + expertDashboardSummaryVO.setAnalysisChartList(analysisChartList); + return expertDashboardSummaryVO; + } + +} + diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/ExpertStatisticsManage.java b/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/ExpertStatisticsManage.java new file mode 100644 index 0000000..6216c39 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/ExpertStatisticsManage.java @@ -0,0 +1,345 @@ +package com.hz.pm.api.dashboard.manage; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.hz.pm.api.common.helper.RegionCacheHelper; +import com.hz.pm.api.common.model.constant.RegionConst; +import com.hz.pm.api.common.model.entity.DataDTO; +import com.hz.pm.api.dashboard.constant.DashboardConstant; +import com.hz.pm.api.dashboard.model.vo.ExpertStatisticsVO; +import com.hz.pm.api.expert.entity.ExpertIntentionWorkRegion; +import com.hz.pm.api.expert.entity.ExpertUserFullInfo; +import com.hz.pm.api.expert.service.IExpertIntentionWorkRegionService; +import com.hz.pm.api.expert.service.IExpertUserFullInfoService; +import com.hz.pm.api.meta.model.dto.DictionaryDTO; +import com.hz.pm.api.meta.model.dto.TagDTO; +import com.hz.pm.api.meta.model.entity.ExpertDictionary; +import com.hz.pm.api.meta.model.entity.ExpertTag; +import com.hz.pm.api.meta.service.IExpertDictionaryService; +import com.hz.pm.api.meta.service.IExpertTagService; +import com.hz.pm.api.meta.service.IMetaDictionaryService; +import com.hz.pm.api.meta.service.IMetaTagService; +import com.hz.pm.api.sys.model.dto.RegionDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ForkJoinPool; +import java.util.stream.Collectors; + +/** + * @Classname ExpertStatisticsManage + * @Description + * @Date 2023/12/04 17:44 + * @Author PoffyZhang + */ +@Component +@RequiredArgsConstructor +@Slf4j +public class ExpertStatisticsManage { + + private final RegionCacheHelper regionCacheHelper; + + private final IExpertUserFullInfoService userFullInfoService; + + private final IExpertIntentionWorkRegionService expertIntentionWorkRegionService; + + private final IExpertTagService expertTagService; + + private final IMetaTagService metaTagService; + + private final IExpertDictionaryService expertDictionaryService; + + private final IMetaDictionaryService metaDictionaryService; + + private Integer[] years = new Integer[]{LocalDateTime.now().getYear() - 2, + LocalDateTime.now().getYear() - 1, LocalDateTime.now().getYear(), + LocalDateTime.now().getYear() + 1, LocalDateTime.now().getYear() + 2}; + + private Integer[] threeYears = new Integer[]{LocalDateTime.now().getYear() - 2, + LocalDateTime.now().getYear() - 1, LocalDateTime.now().getYear()}; + + private String[] SHENGWEIDU = new String[]{DashboardConstant.Expert.NETWORK_CODE, + DashboardConstant.Expert.DANGZHENG_CODE, DashboardConstant.Expert.FANGAN_CODE, + DashboardConstant.Expert.JISHU_CODE, DashboardConstant.Expert.XINCHUANG_CODE, + DashboardConstant.Expert.RUANYING_CODE}; + + private String[] SHIWEIDU = new String[]{DashboardConstant.Expert.CAIWU_CODE, + DashboardConstant.Expert.XINXIHUA_CODE, DashboardConstant.Expert.XINCHUANG_OTTHER_CODE}; + + private String[] TYPES = new String[]{DashboardConstant.Expert.EXPERT_TYPE_INNER, + DashboardConstant.Expert.EXPERT_TYPE_OUT}; + + private String[] LEVELS = new String[]{DashboardConstant.Expert.EXPERT_LEVEL_ZHENGGAO, + DashboardConstant.Expert.EXPERT_LEVEL_FUGAO, DashboardConstant.Expert.EXPERT_LEVEL_MID, + DashboardConstant.Expert.EXPERT_LEVEL_CHUJI, DashboardConstant.Expert.EXPERT_LEVEL_NULL}; + + /** + * 专家统计 + * + * @param year + * @return + */ + public ExpertStatisticsVO statistics(Integer year) { + List experts = userFullInfoService.list(); + List intentionWorkRegions = expertIntentionWorkRegionService.list(); + + List expertTags = expertTagService.list(); + + List tags = metaTagService.queryAll(); + + List dictionaries = metaDictionaryService.queryAll(); + Set code = Sets.newHashSet(); + Map dictionMap = dictionaries.stream().filter(d -> code.add(d.getDictionaryCode())) + .collect(Collectors.toMap(DictionaryDTO::getDictionaryCode, DictionaryDTO::getName)); + + List expertDictionaries = expertDictionaryService.list(Wrappers.lambdaQuery(ExpertDictionary.class) + .eq(ExpertDictionary::getExpertInfoField, DashboardConstant.Expert.EXPERT_TYPE)); + + ExpertStatisticsVO res = new ExpertStatisticsVO(); + CompletableFuture.allOf( + CompletableFuture.runAsync(() -> { + //选择的年份去计算的数据 + yearCompleteExpert(res, year, experts, intentionWorkRegions, expertTags, + tags, expertDictionaries, dictionMap); + }, ForkJoinPool.commonPool()), + CompletableFuture.runAsync(() -> { + //近三年 + threeYearsCompleteExpert(res, experts); + }, ForkJoinPool.commonPool()), + CompletableFuture.runAsync(() -> { + //近一年 + lastYearsCompleteExpert(res, experts); + }, ForkJoinPool.commonPool()) + ).join(); + + return res; + } + + /** + * 近一年 + * + * @param res + */ + private void lastYearsCompleteExpert(ExpertStatisticsVO res, List experts) { + List lastYear = experts.stream().filter(expert -> { + if (expert.getCreateOn().compareTo(LocalDateTime.now().plusYears(-1)) >= 0 && + expert.getCreateOn().compareTo(LocalDateTime.now()) <= 0) { + return Boolean.TRUE; + } + return Boolean.FALSE; + }).collect(Collectors.toList()); + res.setThisYearAdded(lastYear.size()); + } + + /** + * 近三年 + * + * @param res + */ + private void threeYearsCompleteExpert(ExpertStatisticsVO res, List experts) { + List threeYearsAdds = Lists.newArrayList(); + for (Integer year : threeYears) { + threeYearsAdds.add(DataDTO.of(year.toString(), year.toString(), + experts.stream().filter(e -> { + if (Objects.nonNull(e.getCreateOn()) && year.equals(e.getCreateOn().getYear())) { + return Boolean.TRUE; + } + return Boolean.FALSE; + }).collect(Collectors.toList()).size())); + } + res.setThreeYearsAdded(threeYearsAdds); + } + + /** + * 选择年份或者全部 + * + * @param res + * @param year + */ + private void yearCompleteExpert(ExpertStatisticsVO res, Integer year, + List experts, List intentionWorkRegions, + List expertTags, List tags, List expertDictionaries, + Map dictionMap) { + res.setTotal(experts.size()); + + Set userSet = Sets.newHashSet(); + Map expertMap = experts.stream().filter(e -> userSet.add(e.getUserId())) + .collect(Collectors.toMap(ExpertUserFullInfo::getUserId, e -> e)); + + List regions = regionCacheHelper.listChildren(RegionConst.RC_LS, RegionConst.RL_CITY) + .stream().filter(r -> r.getRegionLevel().equals(RegionConst.RL_COUNTY)) + .sorted(Comparator.comparing(RegionDTO::getRegionCode)).collect(Collectors.toList()); + + //是否要筛选年 + experts = experts.stream().filter(e -> { + if (Objects.nonNull(e.getCreateOn()) && + (Objects.isNull(year) || year.equals(e.getCreateOn().getYear()))) { + return Boolean.TRUE; + } + return Boolean.FALSE; + }).collect(Collectors.toList()); + + //计算区域专家 + computeRegionExperts(res, intentionWorkRegions, expertMap, regions); + + //计算专家特长 + computeRegionDistribution(res, experts, expertTags, tags); + + //计算专家类型 + computeTypes(res, experts, expertDictionaries, dictionMap); + + //计算专家等级 + computeLevels(res, experts, expertDictionaries, dictionMap); + } + + /** + * 计算 专家等级 + * + * @param res + * @param experts + */ + private void computeLevels(ExpertStatisticsVO res, List experts, + List expertDictionaries, Map dictionMap) { + List levels = Lists.newArrayList(); + + Set userSets = Sets.newHashSet(); + Map expertUserFullInfoMap = experts.stream() + .filter(u -> userSets.add(u.getUserId())) + .collect(Collectors.toMap(ExpertUserFullInfo::getUserId, e -> e)); + + Map> dictionaryMap = expertDictionaries.stream() + .collect(Collectors.groupingBy(ExpertDictionary::getDictionaryCode)); + for (String levelCode : LEVELS) { + int num = 0; + if (dictionaryMap.containsKey(levelCode)) { + List dictionaries = dictionaryMap.get(levelCode); + num = dictionaries.stream().filter(d -> { + if (expertUserFullInfoMap.containsKey(d.getUserId())) { + return Boolean.TRUE; + } + return Boolean.FALSE; + }).collect(Collectors.toList()).size(); + } + levels.add(DataDTO.of(dictionMap.get(levelCode), levelCode, num)); + } + + res.setLevels(levels); + } + + /** + * 计算 专家类型 + * + * @param res + * @param experts + */ + private void computeTypes(ExpertStatisticsVO res, List experts, + List expertDictionaries, Map dictionMap) { + List types = Lists.newArrayList(); + + Set userSets = Sets.newHashSet(); + Map expertUserFullInfoMap = experts.stream() + .filter(u -> userSets.add(u.getUserId())) + .collect(Collectors.toMap(ExpertUserFullInfo::getUserId, e -> e)); + + Map> dictionaryMap = expertDictionaries.stream() + .collect(Collectors.groupingBy(ExpertDictionary::getDictionaryCode)); + for (String typeCode : TYPES) { + int num = 0; + if (dictionaryMap.containsKey(typeCode)) { + List dictionaries = dictionaryMap.get(typeCode); + num = dictionaries.stream().filter(d -> { + if (expertUserFullInfoMap.containsKey(d.getUserId())) { + return Boolean.TRUE; + } + return Boolean.FALSE; + }).collect(Collectors.toList()).size(); + } + types.add(DataDTO.of(dictionMap.get(typeCode), typeCode, num)); + } + + res.setTypes(types); + } + + //计算专家特长 + private void computeRegionDistribution(ExpertStatisticsVO res, List experts, + List expertTags, List tags) { + Set userSets = Sets.newHashSet(); + Map expertUserFullInfoMap = experts.stream() + .filter(u -> userSets.add(u.getUserId())) + .collect(Collectors.toMap(ExpertUserFullInfo::getUserId, e -> e)); + + Map> typeDistribution = Maps.newHashMap(); + + Map> tagsMap = + expertTags.stream().filter(e -> Objects.nonNull(e.getTagCode())) + .collect(Collectors.groupingBy(ExpertTag::getTagCode)); + + Set tagSets = Sets.newHashSet(); + Map tagNameMap = tags.stream().filter(t -> tagSets.add(t.getTagCode())) + .collect(Collectors.toMap(TagDTO::getTagCode, TagDTO::getTagName)); + + //1.省维度 + List shengWeidu = Lists.newArrayList(); + for (String code : SHENGWEIDU) { + shengWeidu.add(getExpertData(expertUserFullInfoMap, + tagsMap, tagNameMap, code)); + } + + //2.市维度 + List shiWeidu = Lists.newArrayList(); + for (String code : SHIWEIDU) { + shiWeidu.add(getExpertData(expertUserFullInfoMap, + tagsMap, tagNameMap, code)); + } + + typeDistribution.put(DashboardConstant.Expert.DISTRIBUTION_SHENG, shengWeidu); + typeDistribution.put(DashboardConstant.Expert.DISTRIBUTION_SHI, shiWeidu); + res.setTypeDistribution(typeDistribution); + } + + /** + * 包装 + * + * @param expertUserFullInfoMap + * @param tagsMap + * @param tagNameMap + * @param code + * @return + */ + private static DataDTO getExpertData(Map expertUserFullInfoMap, Map> tagsMap, Map tagNameMap, String code) { + int num = 0; + if (tagsMap.containsKey(code)) { + List ets = tagsMap.get(code); + num = ets.stream().filter(e -> { + if (expertUserFullInfoMap.containsKey(e.getUserId())) { + return Boolean.TRUE; + } + return Boolean.FALSE; + }).collect(Collectors.toList()).size(); + } + return DataDTO.of(tagNameMap.get(code), code, num); + } + + //计算区域专家 + private static void computeRegionExperts(ExpertStatisticsVO res, List intentionWorkRegions, Map expertMap, List regions) { + List regionExpert = Lists.newArrayList(); + for (RegionDTO region : regions) { + regionExpert.add(DataDTO.of(region.getRegionName(), region.getRegionCode(), + intentionWorkRegions.stream().filter(w -> { + if (Objects.nonNull(w) && Objects.nonNull(w.getRegionCode()) && + w.getRegionCode().equals(region.getRegionCode()) && + expertMap.containsKey(w.getUserId())) { + return Boolean.TRUE; + } + return Boolean.FALSE; + }).collect(Collectors.toList()).size())); + } + res.setRegionExpert(regionExpert); + } +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/MeetingStatisticsManage.java b/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/MeetingStatisticsManage.java new file mode 100644 index 0000000..7936f45 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/MeetingStatisticsManage.java @@ -0,0 +1,201 @@ +package com.hz.pm.api.dashboard.manage; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.google.common.collect.Lists; +import com.hz.pm.api.common.helper.RegionCacheHelper; +import com.hz.pm.api.common.model.constant.RegionConst; +import com.hz.pm.api.common.model.entity.DataDTO; +import com.hz.pm.api.dashboard.model.vo.MeetingStatisticsVO; +import com.hz.pm.api.ding.constants.DingOrganizationContant; +import com.hz.pm.api.expert.constant.ReviewResultEnum; +import com.hz.pm.api.expert.model.entity.ExpertReview; +import com.hz.pm.api.expert.model.enumeration.ReviewTemplateTypeEnum; +import com.hz.pm.api.expert.service.IExpertReviewService; +import com.hz.pm.api.meeting.entity.domain.Meeting; +import com.hz.pm.api.meeting.service.IMeetingService; +import com.hz.pm.api.organization.model.entity.DingOrganization; +import com.hz.pm.api.organization.service.IDingOrganizationService; +import com.hz.pm.api.projectlib.model.entity.Project; +import com.hz.pm.api.projectlib.service.IProjectService; +import com.hz.pm.api.sys.model.dto.RegionDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @Classname MeetingStatisticsManage + * @Description + * @Date 2023/12/05 17:44 + * @Author PoffyZhang + */ +@Component +@RequiredArgsConstructor +@Slf4j +public class MeetingStatisticsManage { + + private final IMeetingService meetingService; + + private final IExpertReviewService expertReviewService; + + private final RegionCacheHelper regionCacheHelper; + + private final IDingOrganizationService dingOrganizationService; + + private final IProjectService projectService; + + public MeetingStatisticsVO statistics(Integer year) { + MeetingStatisticsVO res = new MeetingStatisticsVO(); + List meetings = meetingService.list(Wrappers.lambdaQuery(Meeting.class)); + //查出 年份的 会议数据 + meetings = meetings.stream().filter(m -> { + if(Objects.nonNull(m.getStartTime()) && + (Objects.isNull(year) || year.equals(m.getStartTime().getYear()))){ + return Boolean.TRUE; + } + return Boolean.FALSE; + }).collect(Collectors.toList()); + List meetingIds = meetings.stream().map(Meeting::getId) + .collect(Collectors.toList()); + + //会议总数 + res.setMeetingTotal(meetings.size()); + + //评审数 + List expertReviews = Lists.newArrayList(); + Integer reviewsTotal = 0; + if(CollUtil.isNotEmpty(meetingIds)){ + expertReviews = expertReviewService.list(Wrappers.lambdaQuery(ExpertReview.class) + .eq(ExpertReview::getIsFinal, Boolean.TRUE) + .in(ExpertReview::getMeetingId,meetingIds)); + reviewsTotal = expertReviews.size(); + } + //通过的评审 + List passExpertReviews = expertReviews.stream() + .filter(r -> Objects.nonNull(r.getReviewResult()) && + r.getReviewResult().equals(ReviewResultEnum.PASSED.getCode())) + .collect(Collectors.toList()); + //不通过的评审 + List notpassExpertReviews = expertReviews.stream() + .filter(r -> Objects.nonNull(r.getReviewResult()) && + r.getReviewResult().equals(ReviewResultEnum.REFUSED.getCode())) + .collect(Collectors.toList()); + res.setPassReview(passExpertReviews.size()); + res.setNotPassRate(reviewsTotal.compareTo(0) == 0 ? BigDecimal.ZERO : + BigDecimal.valueOf(notpassExpertReviews.size()).multiply(BigDecimal.valueOf(100) + .divide(BigDecimal.valueOf(reviewsTotal), RoundingMode.HALF_UP))); + + //各区域 + List regions = regionCacheHelper.listChildren(RegionConst.RC_LS, RegionConst.RL_CITY) + .stream().filter(r -> r.getRegionLevel().equals(RegionConst.RL_COUNTY)) + .sorted(Comparator.comparing(RegionDTO::getRegionCode)).collect(Collectors.toList()); + List regionMeetngs = Lists.newArrayList(); + for(RegionDTO region : regions){ + regionMeetngs.add(DataDTO.of(region.getRegionName(),region.getRegionCode(),meetings.stream().filter(m -> { + + return Boolean.FALSE; + }).collect(Collectors.toList()).size())); + } + res.setRegionMeetings(regionMeetngs); + + //预审 验收 会议 + List meetingTypes = Lists.newArrayList(); + meetingTypes.add(DataDTO.of("预审会议", ReviewTemplateTypeEnum.PRELIMINARY_SCHEME_REVIEW.getCode().toString(), + meetings.stream().filter(m -> { + if(StringUtils.isNotBlank(m.getType()) && + m.getType().equals(ReviewTemplateTypeEnum.PRELIMINARY_SCHEME_REVIEW.getCode() + .toString())){ + return Boolean.TRUE; + } + return Boolean.FALSE; + }).collect(Collectors.toList()).size())); + Long yanshouCount = meetings.stream().filter(m -> { + if (StringUtils.isNotBlank(m.getType()) && + m.getType().equals(ReviewTemplateTypeEnum.ACCEPTANCE_SCHEME_REVIEW.getCode() + .toString())) { + return Boolean.TRUE; + } + return Boolean.FALSE; + }).count(); + meetingTypes.add(DataDTO.of("验收会议",ReviewTemplateTypeEnum.ACCEPTANCE_SCHEME_REVIEW.getCode().toString(), + yanshouCount.intValue())); + res.setMeetingTypes(meetingTypes); + + //各区县评审 不通过率 + List regionNotpassReview = Lists.newArrayList(); + for(RegionDTO region : regions){ + DataDTO data = new DataDTO(); + data.setCode(region.getRegionCode()); + data.setName(region.getRegionName()); + //先求出 此区域会议 + List regionMeetingIds = meetings.stream().filter(m -> { + + return Boolean.FALSE; + }).map(Meeting::getId).collect(Collectors.toList()); + + List regionReviews = expertReviews.stream().filter(r -> { + if (Objects.nonNull(r.getMeetingId()) && + regionMeetingIds.contains(r.getMeetingId())) { + return Boolean.TRUE; + } + return Boolean.FALSE; + }).collect(Collectors.toList()); + long regionNotpass = regionReviews.stream().filter(r -> Objects.nonNull(r.getReviewResult()) && + r.getReviewResult().equals(ReviewResultEnum.REFUSED.getCode())) + .count(); + + data.setRate(CollUtil.isEmpty(regionReviews) ? BigDecimal.ZERO : + BigDecimal.valueOf(regionNotpass).multiply(BigDecimal.valueOf(100)) + .divide(BigDecimal.valueOf(regionReviews.size()),BigDecimal.ROUND_CEILING,RoundingMode.HALF_UP)); + + regionNotpassReview.add(data); + } + res.setNotPassRegionMeetings(regionNotpassReview); + + //各部门 不通过项目TOP10 + List organizations = dingOrganizationService.list(Wrappers.lambdaQuery(DingOrganization.class) + .in(DingOrganization::getTypeCode, Lists.newArrayList(DingOrganizationContant.UNIT_TYPE, + DingOrganizationContant.GOV_TEMPORARY))); + List projects = projectService.list(Wrappers.lambdaQuery(Project.class) + .eq(Project::getNewest, Boolean.TRUE)); + + //TOP10 + List finalExpertReviews = expertReviews; + List notpassTop10 = organizations.stream() + .map(o -> { + DataDTO data = new DataDTO(); + data.setCode(o.getOrganizationCode()); + data.setName(o.getOrganizationName()); + List orgProjects = projects.stream().filter(p -> Objects.nonNull(p.getBuildOrgCode()) && + p.getBuildOrgCode().equals(o.getOrganizationCode())) + .collect(Collectors.toList()); + List projectCodes = orgProjects.stream().map(Project::getProjectCode) + .collect(Collectors.toList()); + if(CollUtil.isEmpty(projectCodes)){ + return data; + } + List reviews = finalExpertReviews.stream().filter(r -> Objects.nonNull(r.getProjectCode()) && + projectCodes.contains(r.getProjectCode())).collect(Collectors.toList()); + long orgNotpass = reviews.stream().filter(r -> Objects.nonNull(r.getReviewResult()) && + r.getReviewResult().equals(ReviewResultEnum.REFUSED.getCode())) + .count(); + data.setRate(CollUtil.isEmpty(reviews) ? BigDecimal.ZERO : + BigDecimal.valueOf(orgNotpass).multiply(BigDecimal.valueOf(100)) + .divide(BigDecimal.valueOf(reviews.size()),BigDecimal.ROUND_CEILING,RoundingMode.HALF_UP)); + return data; + }) + .filter(d -> Objects.nonNull(d.getRate())) + .sorted(Comparator.comparing(DataDTO::getRate).reversed()) + .limit(10).collect(Collectors.toList()); + res.setNotPassTop10(notpassTop10); + return res; + } +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/ProjectStatisticsManage.java b/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/ProjectStatisticsManage.java new file mode 100644 index 0000000..7d4cd2f --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/manage/ProjectStatisticsManage.java @@ -0,0 +1,24 @@ +package com.hz.pm.api.dashboard.manage; + +import com.hz.pm.api.dashboard.model.vo.ProjectStatisticsVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * @Classname ProjectStatisticsManage + * @Description + * @Date 2023/12/05 17:44 + * @Author PoffyZhang + */ +@Component +@RequiredArgsConstructor +@Slf4j +public class ProjectStatisticsManage { + + public ProjectStatisticsVO statistics(Integer year) { + ProjectStatisticsVO res = new ProjectStatisticsVO(); + + return res; + } +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/model/vo/ProjectStatisticsVO.java b/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/model/vo/ProjectStatisticsVO.java new file mode 100644 index 0000000..336507e --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/dashboard/model/vo/ProjectStatisticsVO.java @@ -0,0 +1,54 @@ +package com.hz.pm.api.dashboard.model.vo; + +import com.hz.pm.api.common.model.entity.DataDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +@ApiModel(value = "项目统计") +public class ProjectStatisticsVO implements Serializable { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("项目计划数") + private Integer planTotal = 0; + + @ApiModelProperty("批复项目数") + private Integer approvedTotal = 0; + + @ApiModelProperty("在建项目数") + private Integer constructionTotal = 0; + + @ApiModelProperty("平均批复周期(月)") + private Integer approvalCycleAvg = 0; + + @ApiModelProperty("平均建设周期(月)") + private Integer constructionCycleAvg = 0; + + @ApiModelProperty("各阶段项目统计") + private List progresses; + + @ApiModelProperty("近三年平均批复周期") + private List threeYearsApprovalCycleAvg; + + @ApiModelProperty("近三年平均建设周期") + private List threeYearsConstructionCycleAvg; + + @ApiModelProperty("项目分布类型") + private List projectTypeDistribution; + + @ApiModelProperty("金额变更统计TOP10") + private List amountChangeTop10; + + @ApiModelProperty("项目撤销统计TOP10") + private List revokeTop10; + + @ApiModelProperty("项目终止统计TOP10") + private List terminationTop10; + + @ApiModelProperty("项目延期统计TOP10") + private List delayTop10; +} \ No newline at end of file diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/fiscal/entity/CompanyFiscalCode.java b/hz-pm-api/src/main/java/com/hz/pm/api/fiscal/entity/CompanyFiscalCode.java index c1ff6bd..71cfdb7 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/fiscal/entity/CompanyFiscalCode.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/fiscal/entity/CompanyFiscalCode.java @@ -1,5 +1,7 @@ package com.hz.pm.api.fiscal.entity; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.annotations.ApiModel; import lombok.Data; @@ -17,11 +19,12 @@ import java.time.LocalDateTime; */ @Data @TableName("nd_company_fiscal_code") -@ApiModel(value = "NdCompanyFiscalCode对象", description = "") +@ApiModel(value = "NdCompanyFiscalCode对象") public class CompanyFiscalCode implements Serializable { private static final long serialVersionUID = 1L; + @TableId(type = IdType.AUTO) private Long id; private String fiscalCode; diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/user/manage/UserInfoManage.java b/hz-pm-api/src/main/java/com/hz/pm/api/user/manage/UserInfoManage.java index 2f850bc..8c54d17 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/user/manage/UserInfoManage.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/user/manage/UserInfoManage.java @@ -437,13 +437,13 @@ public class UserInfoManage { if (CollUtil.isNotEmpty(userRoleList)) { List roleIdList = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toList()); List roleList = iRoleService.list(Wrappers.lambdaQuery(Role.class).in(Role::getId, roleIdList)); - userRoleInfoList = roleList.stream().map(r -> { + roleList.forEach(r -> { UserRoleVO userRoleVO = new UserRoleVO(); userRoleVO.setId(r.getId()); userRoleVO.setName(r.getName()); userRoleVO.setCode(r.getCode()); - return userRoleVO; - }).collect(Collectors.toList()); + userRoleInfoList.add(userRoleVO); + }); } resUserDetailVO.setUserRoleInfoList(userRoleInfoList); UserFullInfoDTO userFullInfo = userInfoHelper.getUserFullInfo(userId); From 704803952592e936488863e8000059ff6a21c175 Mon Sep 17 00:00:00 2001 From: WendyYang Date: Tue, 2 Jan 2024 20:35:15 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BC=81=E4=B8=9A=E7=AD=BE=E7=AB=A0?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E4=B8=BB=E9=94=AE=E8=87=AA=E5=A2=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/hz/pm/api/signature/entity/CompanySignature.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/signature/entity/CompanySignature.java b/hz-pm-api/src/main/java/com/hz/pm/api/signature/entity/CompanySignature.java index 968a1e8..39dfcd3 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/signature/entity/CompanySignature.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/signature/entity/CompanySignature.java @@ -1,5 +1,7 @@ package com.hz.pm.api.signature.entity; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.annotations.ApiModel; import lombok.Data; @@ -17,11 +19,12 @@ import java.time.LocalDateTime; */ @TableName("nd_company_signature") @Data -@ApiModel(value = "NdCompanySignature对象", description = "") +@ApiModel(value = "NdCompanySignature对象") public class CompanySignature implements Serializable { private static final long serialVersionUID = 1L; + @TableId(type = IdType.AUTO) private Long id; private String sealSn;