@@ -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; | |||
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; | |||
@@ -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; | |||
@@ -437,13 +437,13 @@ public class UserInfoManage { | |||
if (CollUtil.isNotEmpty(userRoleList)) { | |||
List<Long> roleIdList = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toList()); | |||
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.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); | |||