liu
9 小时以前 5d34bdd3c426bb2ca5f6627424ae67984f745603
yudao-module-research/src/main/java/cn/iocoder/yudao/module/research/service/application/ResearchApplicationServiceImpl.java
@@ -7,6 +7,7 @@
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.archive.api.document.ArchiveDocumentApi;
import cn.iocoder.yudao.module.archive.api.document.dto.ArchiveDocumentCreateReqDTO;
import cn.iocoder.yudao.module.archive.api.document.dto.ArchiveDocumentRespDTO;
@@ -18,9 +19,14 @@
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.research.controller.admin.application.vo.ResearchApplicationPageReqVO;
import cn.iocoder.yudao.module.research.controller.admin.application.vo.ResearchApplicationSaveReqVO;
import cn.iocoder.yudao.module.research.controller.admin.application.vo.member.ResearchApplicationMemberSaveReqVO;
import cn.iocoder.yudao.module.research.controller.admin.application.vo.member.ResearchApplicationMemberStatisticsReqVO;
import cn.iocoder.yudao.module.research.controller.admin.application.vo.member.ResearchApplicationMemberStatisticsRespVO;
import cn.iocoder.yudao.module.research.dal.dataobject.application.ResearchApplicationDO;
import cn.iocoder.yudao.module.research.dal.dataobject.application.ResearchApplicationMemberDO;
import cn.iocoder.yudao.module.research.dal.dataobject.guideline.ResearchGuidelineDO;
import cn.iocoder.yudao.module.research.dal.mysql.application.ResearchApplicationMapper;
import cn.iocoder.yudao.module.research.dal.mysql.application.ResearchApplicationMemberMapper;
import cn.iocoder.yudao.module.research.dal.redis.ResearchNoRedisDAO;
import cn.iocoder.yudao.module.research.enums.ResearchApplicationStatusEnum;
import cn.iocoder.yudao.module.research.enums.ResearchGuidelineCategoryEnum;
@@ -78,6 +84,9 @@
    private ResearchApplicationMapper applicationMapper;
    @Resource
    private ResearchApplicationMemberMapper memberMapper;
    @Resource
    private ArchiveDocumentApi archiveDocumentApi;
    @Resource
@@ -125,6 +134,7 @@
                .setStatus(ResearchApplicationStatusEnum.DRAFT.getStatus())
                .setBudgetAmount(in.getBudgetAmount() == null ? BigDecimal.ZERO : in.getBudgetAmount()));
        applicationMapper.insert(application);
        saveMembers(application.getId(), createReqVO.getMembers());
        storageAttachmentApi.bindAttachments("file", APPLICATION_RECORD_TYPE,
                application.getId(), createReqVO.getBlobIds());
@@ -147,6 +157,10 @@
        updateObj.setCategory(guideline.getCategory());
        applicationMapper.updateById(updateObj);
        // 成员子表:先删后增,全量覆盖
        memberMapper.deleteByApplicationId(updateReqVO.getId());
        saveMembers(updateReqVO.getId(), updateReqVO.getMembers());
        storageAttachmentApi.updateAttachments("file", APPLICATION_RECORD_TYPE,
                updateReqVO.getId(), updateReqVO.getBlobIds());
    }
@@ -160,6 +174,7 @@
                    application.getNo(), ResearchApplicationStatusEnum.nameOf(application.getStatus()));
        }
        memberMapper.deleteByApplicationId(id);
        applicationMapper.deleteById(id);
        storageAttachmentApi.deleteAttachmentsByRecord(APPLICATION_RECORD_TYPE, id);
    }
@@ -170,6 +185,116 @@
    }
    @Override
    public List<ResearchApplicationMemberDO> getMemberListByApplicationId(Long applicationId) {
        return memberMapper.selectListByApplicationId(applicationId);
    }
    @Override
    public List<ResearchApplicationMemberStatisticsRespVO> getMemberStatistics(ResearchApplicationMemberStatisticsReqVO reqVO) {
        // 1. 按状态过滤申报单
        List<ResearchApplicationDO> applications = applicationMapper.selectList(
                new LambdaQueryWrapperX<ResearchApplicationDO>()
                        .eqIfPresent(ResearchApplicationDO::getStatus, reqVO.getStatus()));
        if (CollUtil.isEmpty(applications)) {
            return Collections.emptyList();
        }
        Map<Long, ResearchApplicationDO> applicationMap = applications.stream()
                .collect(Collectors.toMap(ResearchApplicationDO::getId, application -> application, (a, b) -> a));
        // 2. 成员子表聚合:姓名 + 电话 + 单位 相同视为同一成员
        String keyword = StrUtil.trimToNull(reqVO.getKeyword());
        Map<String, ResearchApplicationMemberStatisticsRespVO> resultMap = new LinkedHashMap<>();
        Map<String, Set<String>> roleMap = new HashMap<>();
        Map<String, Set<Long>> projectIdMap = new HashMap<>();
        Map<String, Set<String>> projectTitleMap = new HashMap<>();
        for (ResearchApplicationMemberDO member : memberMapper.selectList()) {
            ResearchApplicationDO application = applicationMap.get(member.getApplicationId());
            if (application == null || !matchesMemberKeyword(member, keyword)) {
                continue;
            }
            String key = buildMemberKey(member);
            ResearchApplicationMemberStatisticsRespVO vo = resultMap.computeIfAbsent(key, k -> {
                ResearchApplicationMemberStatisticsRespVO item = new ResearchApplicationMemberStatisticsRespVO();
                item.setName(StrUtil.trim(member.getName()));
                item.setContactPhone(StrUtil.trim(member.getContactPhone()));
                item.setWorkUnit(StrUtil.trim(member.getWorkUnit()));
                item.setJobTitle(StrUtil.trim(member.getJobTitle()));
                return item;
            });
            if (StrUtil.isBlank(vo.getJobTitle()) && StrUtil.isNotBlank(member.getJobTitle())) {
                vo.setJobTitle(StrUtil.trim(member.getJobTitle()));
            }
            if (StrUtil.isNotBlank(member.getMemberRole())) {
                roleMap.computeIfAbsent(key, k -> new LinkedHashSet<>()).add(StrUtil.trim(member.getMemberRole()));
            }
            projectIdMap.computeIfAbsent(key, k -> new LinkedHashSet<>()).add(member.getApplicationId());
            if (StrUtil.isNotBlank(application.getTitle())) {
                projectTitleMap.computeIfAbsent(key, k -> new LinkedHashSet<>()).add(application.getTitle());
            }
        }
        // 3. 汇总成员维度结果
        List<ResearchApplicationMemberStatisticsRespVO> result = new ArrayList<>(resultMap.size());
        for (Map.Entry<String, ResearchApplicationMemberStatisticsRespVO> entry : resultMap.entrySet()) {
            String key = entry.getKey();
            ResearchApplicationMemberStatisticsRespVO vo = entry.getValue();
            vo.setMemberRoles(String.join("、", roleMap.getOrDefault(key, Collections.emptySet())));
            vo.setProjectCount(projectIdMap.getOrDefault(key, Collections.emptySet()).size());
            vo.setProjectTitles(String.join(";", projectTitleMap.getOrDefault(key, Collections.emptySet())));
            result.add(vo);
        }
        result.sort((a, b) -> {
            int compare = Integer.compare(
                    b.getProjectCount() == null ? 0 : b.getProjectCount(),
                    a.getProjectCount() == null ? 0 : a.getProjectCount());
            return compare != 0 ? compare : StrUtil.compare(a.getName(), b.getName(), true);
        });
        return result;
    }
    /** 保存申报书成员子表(调用方需自行保证 update 场景已先删除旧数据) */
    private void saveMembers(Long applicationId, List<ResearchApplicationMemberSaveReqVO> members) {
        if (CollUtil.isEmpty(members)) {
            return;
        }
        List<ResearchApplicationMemberDO> list = new ArrayList<>(members.size());
        int index = 1;
        for (ResearchApplicationMemberSaveReqVO member : members) {
            if (member == null || StrUtil.isBlank(member.getName())) {
                continue;
            }
            list.add(ResearchApplicationMemberDO.builder()
                    .applicationId(applicationId)
                    .name(StrUtil.trim(member.getName()))
                    .contactPhone(StrUtil.trim(member.getContactPhone()))
                    .workUnit(StrUtil.trim(member.getWorkUnit()))
                    .jobTitle(StrUtil.trim(member.getJobTitle()))
                    .memberRole(StrUtil.trim(member.getMemberRole()))
                    .sortNo(member.getSortNo() != null ? member.getSortNo() : index)
                    .build());
            index++;
        }
        if (CollUtil.isNotEmpty(list)) {
            memberMapper.insertBatch(list);
        }
    }
    private String buildMemberKey(ResearchApplicationMemberDO member) {
        return StrUtil.join("|", StrUtil.nullToEmpty(member.getName()).trim(),
                StrUtil.nullToEmpty(member.getContactPhone()).trim(),
                StrUtil.nullToEmpty(member.getWorkUnit()).trim());
    }
    private boolean matchesMemberKeyword(ResearchApplicationMemberDO member, String keyword) {
        if (StrUtil.isBlank(keyword)) {
            return true;
        }
        return StrUtil.containsIgnoreCase(member.getName(), keyword)
                || StrUtil.containsIgnoreCase(member.getContactPhone(), keyword)
                || StrUtil.containsIgnoreCase(member.getWorkUnit(), keyword);
    }
    @Override
    public PageResult<ResearchApplicationDO> getApplicationPage(ResearchApplicationPageReqVO pageReqVO) {
        return applicationMapper.selectPage(pageReqVO);
    }