@@ -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); | |||||
} | |||||
} |
@@ -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<AnalysisChart> analysisChartList = new ArrayList<>(); | |||||
// 获取丽水区域 code name Map | |||||
Map<String, String> liShuiRegionCodeNameMap = dashboardHelper.getLiShuiRegionCodeNameMap(); | |||||
// 获取库内所有的专家列表 | |||||
List<ExpertUserFullInfo> evidenceHasBeenSubmittedExpertInfoList = iExpertUserFullInfoService.list(); | |||||
// 专家regionCode分组map列表 | |||||
Map<String, List<ExpertUserFullInfo>> regionCodeExpertMap = Collections.emptyMap(); | |||||
// 各区域专家数量 | |||||
AnalysisChart regionExpertNumberChartAnalysisChart = new AnalysisChart(); | |||||
List<AnalysisData> 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<ExpertUserFullInfo> 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<Long> regionDegreeExpertIdList = new ArrayList<>(); | |||||
if (StringUtils.isNotBlank(queryRegionCode)) { | |||||
List<ExpertUserFullInfo> 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<ExpertDictionary> degreeExpertDictionaryList = iExpertDictionaryService | |||||
.listByUserId(regionDegreeExpertIdList, ExpertDictTypeEnum.DEGREE); | |||||
Map<String, List<String>> 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<ExpertDictionary> titleLevelExpertDictionaryList = iExpertDictionaryService | |||||
.listByUserId(regionDegreeExpertIdList, ExpertDictTypeEnum.TITLE_LEVEL); | |||||
Map<String, List<ExpertDictionary>> titleLevelCodeMap = titleLevelExpertDictionaryList.stream() | |||||
.collect(Collectors.groupingBy(ExpertDictionary::getDictionaryCode)); | |||||
AnalysisChart regionExpertTitleLevelChartAnalysisChart = | |||||
dashboardChartAssembler.assemblerAnalysisChart(titleLevelCodeMap, ChartTypeEnum.REGION_EXPERT_TITLE_LEVEL_CHART); | |||||
analysisChartList.add(regionExpertTitleLevelChartAnalysisChart); | |||||
} | |||||
// 评审次数 | |||||
List<Meeting> normalMeetingList = iMeetingService.list(Wrappers.lambdaQuery(Meeting.class) | |||||
.ne(Meeting::getStatus, MeetingStatusEnum.CANCELED.getCode())); | |||||
Integer meetingCnt = normalMeetingList.size(); | |||||
// 各类型评审次数 | |||||
Map<String, List<Meeting>> meetingTypeMap = normalMeetingList.stream().collect(Collectors.groupingBy(Meeting::getType)); | |||||
AnalysisChart meetingTypeCntChartAnalysisChart = | |||||
dashboardChartAssembler.assemblerAnalysisChart(meetingTypeMap, ChartTypeEnum.MEETING_TYPE_CNT_CHART); | |||||
analysisChartList.add(meetingTypeCntChartAnalysisChart); | |||||
// 明星专家列表 | |||||
List<StarExpertBO> starExpertList = new ArrayList<>(); | |||||
List<MeetingExpertJudge> meetingExpertJudgeList = iMeetingExpertJudgeService.list(); | |||||
Map<Long, List<MeetingExpertJudge>> expertIdMeetingExpertJudgeMap = meetingExpertJudgeList.stream() | |||||
.collect(Collectors.groupingBy(MeetingExpertJudge::getExpertId)); | |||||
Map<Long, String> expertIdExpertNameMap = evidenceHasBeenSubmittedExpertInfoList.stream() | |||||
.collect(Collectors.toMap(ExpertUserFullInfo::getUserId, ExpertUserFullInfo::getExpertName)); | |||||
for (Long expertId : expertIdMeetingExpertJudgeMap.keySet()) { | |||||
String expertName = expertIdExpertNameMap.get(expertId); | |||||
List<MeetingExpertJudge> 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; | |||||
} | |||||
} | |||||
@@ -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<ExpertUserFullInfo> experts = userFullInfoService.list(); | |||||
List<ExpertIntentionWorkRegion> intentionWorkRegions = expertIntentionWorkRegionService.list(); | |||||
List<ExpertTag> expertTags = expertTagService.list(); | |||||
List<TagDTO> tags = metaTagService.queryAll(); | |||||
List<DictionaryDTO> dictionaries = metaDictionaryService.queryAll(); | |||||
Set<String> code = Sets.newHashSet(); | |||||
Map<String, String> dictionMap = dictionaries.stream().filter(d -> code.add(d.getDictionaryCode())) | |||||
.collect(Collectors.toMap(DictionaryDTO::getDictionaryCode, DictionaryDTO::getName)); | |||||
List<ExpertDictionary> 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<ExpertUserFullInfo> experts) { | |||||
List<ExpertUserFullInfo> 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<ExpertUserFullInfo> experts) { | |||||
List<DataDTO> 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<ExpertUserFullInfo> experts, List<ExpertIntentionWorkRegion> intentionWorkRegions, | |||||
List<ExpertTag> expertTags, List<TagDTO> tags, List<ExpertDictionary> expertDictionaries, | |||||
Map<String, String> dictionMap) { | |||||
res.setTotal(experts.size()); | |||||
Set<Long> userSet = Sets.newHashSet(); | |||||
Map<Long, ExpertUserFullInfo> expertMap = experts.stream().filter(e -> userSet.add(e.getUserId())) | |||||
.collect(Collectors.toMap(ExpertUserFullInfo::getUserId, e -> e)); | |||||
List<RegionDTO> 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<ExpertUserFullInfo> experts, | |||||
List<ExpertDictionary> expertDictionaries, Map<String, String> dictionMap) { | |||||
List<DataDTO> levels = Lists.newArrayList(); | |||||
Set<Long> userSets = Sets.newHashSet(); | |||||
Map<Long, ExpertUserFullInfo> expertUserFullInfoMap = experts.stream() | |||||
.filter(u -> userSets.add(u.getUserId())) | |||||
.collect(Collectors.toMap(ExpertUserFullInfo::getUserId, e -> e)); | |||||
Map<String, List<ExpertDictionary>> dictionaryMap = expertDictionaries.stream() | |||||
.collect(Collectors.groupingBy(ExpertDictionary::getDictionaryCode)); | |||||
for (String levelCode : LEVELS) { | |||||
int num = 0; | |||||
if (dictionaryMap.containsKey(levelCode)) { | |||||
List<ExpertDictionary> 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<ExpertUserFullInfo> experts, | |||||
List<ExpertDictionary> expertDictionaries, Map<String, String> dictionMap) { | |||||
List<DataDTO> types = Lists.newArrayList(); | |||||
Set<Long> userSets = Sets.newHashSet(); | |||||
Map<Long, ExpertUserFullInfo> expertUserFullInfoMap = experts.stream() | |||||
.filter(u -> userSets.add(u.getUserId())) | |||||
.collect(Collectors.toMap(ExpertUserFullInfo::getUserId, e -> e)); | |||||
Map<String, List<ExpertDictionary>> dictionaryMap = expertDictionaries.stream() | |||||
.collect(Collectors.groupingBy(ExpertDictionary::getDictionaryCode)); | |||||
for (String typeCode : TYPES) { | |||||
int num = 0; | |||||
if (dictionaryMap.containsKey(typeCode)) { | |||||
List<ExpertDictionary> 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<ExpertUserFullInfo> experts, | |||||
List<ExpertTag> expertTags, List<TagDTO> tags) { | |||||
Set<Long> userSets = Sets.newHashSet(); | |||||
Map<Long, ExpertUserFullInfo> expertUserFullInfoMap = experts.stream() | |||||
.filter(u -> userSets.add(u.getUserId())) | |||||
.collect(Collectors.toMap(ExpertUserFullInfo::getUserId, e -> e)); | |||||
Map<String, List<DataDTO>> typeDistribution = Maps.newHashMap(); | |||||
Map<String, List<ExpertTag>> tagsMap = | |||||
expertTags.stream().filter(e -> Objects.nonNull(e.getTagCode())) | |||||
.collect(Collectors.groupingBy(ExpertTag::getTagCode)); | |||||
Set<String> tagSets = Sets.newHashSet(); | |||||
Map<String, String> tagNameMap = tags.stream().filter(t -> tagSets.add(t.getTagCode())) | |||||
.collect(Collectors.toMap(TagDTO::getTagCode, TagDTO::getTagName)); | |||||
//1.省维度 | |||||
List<DataDTO> shengWeidu = Lists.newArrayList(); | |||||
for (String code : SHENGWEIDU) { | |||||
shengWeidu.add(getExpertData(expertUserFullInfoMap, | |||||
tagsMap, tagNameMap, code)); | |||||
} | |||||
//2.市维度 | |||||
List<DataDTO> 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<Long, ExpertUserFullInfo> expertUserFullInfoMap, Map<String, List<ExpertTag>> tagsMap, Map<String, String> tagNameMap, String code) { | |||||
int num = 0; | |||||
if (tagsMap.containsKey(code)) { | |||||
List<ExpertTag> 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<ExpertIntentionWorkRegion> intentionWorkRegions, Map<Long, ExpertUserFullInfo> expertMap, List<RegionDTO> regions) { | |||||
List<DataDTO> 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); | |||||
} | |||||
} |
@@ -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<Meeting> 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<Long> meetingIds = meetings.stream().map(Meeting::getId) | |||||
.collect(Collectors.toList()); | |||||
//会议总数 | |||||
res.setMeetingTotal(meetings.size()); | |||||
//评审数 | |||||
List<ExpertReview> 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<ExpertReview> passExpertReviews = expertReviews.stream() | |||||
.filter(r -> Objects.nonNull(r.getReviewResult()) && | |||||
r.getReviewResult().equals(ReviewResultEnum.PASSED.getCode())) | |||||
.collect(Collectors.toList()); | |||||
//不通过的评审 | |||||
List<ExpertReview> 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<RegionDTO> 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<DataDTO> 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<DataDTO> 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<DataDTO> regionNotpassReview = Lists.newArrayList(); | |||||
for(RegionDTO region : regions){ | |||||
DataDTO data = new DataDTO(); | |||||
data.setCode(region.getRegionCode()); | |||||
data.setName(region.getRegionName()); | |||||
//先求出 此区域会议 | |||||
List<Long> regionMeetingIds = meetings.stream().filter(m -> { | |||||
return Boolean.FALSE; | |||||
}).map(Meeting::getId).collect(Collectors.toList()); | |||||
List<ExpertReview> 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<DingOrganization> organizations = dingOrganizationService.list(Wrappers.lambdaQuery(DingOrganization.class) | |||||
.in(DingOrganization::getTypeCode, Lists.newArrayList(DingOrganizationContant.UNIT_TYPE, | |||||
DingOrganizationContant.GOV_TEMPORARY))); | |||||
List<Project> projects = projectService.list(Wrappers.lambdaQuery(Project.class) | |||||
.eq(Project::getNewest, Boolean.TRUE)); | |||||
//TOP10 | |||||
List<ExpertReview> finalExpertReviews = expertReviews; | |||||
List<DataDTO> notpassTop10 = organizations.stream() | |||||
.map(o -> { | |||||
DataDTO data = new DataDTO(); | |||||
data.setCode(o.getOrganizationCode()); | |||||
data.setName(o.getOrganizationName()); | |||||
List<Project> orgProjects = projects.stream().filter(p -> Objects.nonNull(p.getBuildOrgCode()) && | |||||
p.getBuildOrgCode().equals(o.getOrganizationCode())) | |||||
.collect(Collectors.toList()); | |||||
List<String> projectCodes = orgProjects.stream().map(Project::getProjectCode) | |||||
.collect(Collectors.toList()); | |||||
if(CollUtil.isEmpty(projectCodes)){ | |||||
return data; | |||||
} | |||||
List<ExpertReview> 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; | |||||
} | |||||
} |
@@ -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; | |||||
} | |||||
} |
@@ -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<DataDTO> progresses; | |||||
@ApiModelProperty("近三年平均批复周期") | |||||
private List<DataDTO> threeYearsApprovalCycleAvg; | |||||
@ApiModelProperty("近三年平均建设周期") | |||||
private List<DataDTO> threeYearsConstructionCycleAvg; | |||||
@ApiModelProperty("项目分布类型") | |||||
private List<DataDTO> projectTypeDistribution; | |||||
@ApiModelProperty("金额变更统计TOP10") | |||||
private List<DataDTO> amountChangeTop10; | |||||
@ApiModelProperty("项目撤销统计TOP10") | |||||
private List<DataDTO> revokeTop10; | |||||
@ApiModelProperty("项目终止统计TOP10") | |||||
private List<DataDTO> terminationTop10; | |||||
@ApiModelProperty("项目延期统计TOP10") | |||||
private List<DataDTO> delayTop10; | |||||
} |
@@ -1,5 +1,7 @@ | |||||
package com.hz.pm.api.fiscal.entity; | 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 com.baomidou.mybatisplus.annotation.TableName; | ||||
import io.swagger.annotations.ApiModel; | import io.swagger.annotations.ApiModel; | ||||
import lombok.Data; | import lombok.Data; | ||||
@@ -17,11 +19,12 @@ import java.time.LocalDateTime; | |||||
*/ | */ | ||||
@Data | @Data | ||||
@TableName("nd_company_fiscal_code") | @TableName("nd_company_fiscal_code") | ||||
@ApiModel(value = "NdCompanyFiscalCode对象", description = "") | |||||
@ApiModel(value = "NdCompanyFiscalCode对象") | |||||
public class CompanyFiscalCode implements Serializable { | public class CompanyFiscalCode implements Serializable { | ||||
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
@TableId(type = IdType.AUTO) | |||||
private Long id; | private Long id; | ||||
private String fiscalCode; | private String fiscalCode; | ||||
@@ -437,13 +437,13 @@ public class UserInfoManage { | |||||
if (CollUtil.isNotEmpty(userRoleList)) { | if (CollUtil.isNotEmpty(userRoleList)) { | ||||
List<Long> roleIdList = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toList()); | List<Long> roleIdList = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toList()); | ||||
List<Role> roleList = iRoleService.list(Wrappers.lambdaQuery(Role.class).in(Role::getId, roleIdList)); | List<Role> roleList = iRoleService.list(Wrappers.lambdaQuery(Role.class).in(Role::getId, roleIdList)); | ||||
userRoleInfoList = roleList.stream().map(r -> { | |||||
roleList.forEach(r -> { | |||||
UserRoleVO userRoleVO = new UserRoleVO(); | UserRoleVO userRoleVO = new UserRoleVO(); | ||||
userRoleVO.setId(r.getId()); | userRoleVO.setId(r.getId()); | ||||
userRoleVO.setName(r.getName()); | userRoleVO.setName(r.getName()); | ||||
userRoleVO.setCode(r.getCode()); | userRoleVO.setCode(r.getCode()); | ||||
return userRoleVO; | |||||
}).collect(Collectors.toList()); | |||||
userRoleInfoList.add(userRoleVO); | |||||
}); | |||||
} | } | ||||
resUserDetailVO.setUserRoleInfoList(userRoleInfoList); | resUserDetailVO.setUserRoleInfoList(userRoleInfoList); | ||||
UserFullInfoDTO userFullInfo = userInfoHelper.getUserFullInfo(userId); | UserFullInfoDTO userFullInfo = userInfoHelper.getUserFullInfo(userId); | ||||