package com.smartor.service.impl; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.*; import java.util.stream.Collectors; import com.ruoyi.common.utils.Arith; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.DtoConversionUtils; import com.smartor.domain.*; import com.smartor.domain.DTO.ServiceSubtaskDetailDTO; import com.smartor.domain.VO.PatSatisfactionReqVO; import com.smartor.domain.VO.PatSatisfactionResVO; import com.smartor.domain.entity.PatSatisfactionDetailEntity; import com.smartor.domain.entity.ServiceSubtaskSatisfactionEntity; import com.smartor.mapper.*; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.smartor.service.IPatSatisfactionService; /** * 患者满意度Service业务层处理 * * @author lihu * @date 2025-06-30 */ @Service public class PatSatisfactionServiceImpl implements IPatSatisfactionService { @Autowired private PatSatisfactionMapper patSatisfactionMapper; @Autowired private ServiceSubtaskMapper serviceSubtaskMapper; @Autowired private ServiceSubtaskDetailMapper serviceSubtaskDetailMapper; @Autowired private IvrLibaTemplateScriptMapper ivrLibaTemplateScriptMapper; @Autowired private IvrLibaScriptAssortMapper ivrLibaScriptAssortMapper; @Autowired private SvyLibScriptCategoryMapper svyLibScriptCategoryMapper; @Autowired private IvrLibaTemplateTargetoptionMapper ivrLibaTemplateTargetoptionMapper; @Autowired private SvyLibTemplateTargetoptionMapper svyLibTemplateTargetoptionMapper; @Autowired private SvyLibTemplateScriptMapper svyLibTemplateScriptMapper; /** * 查询患者满意度 * * @param id 患者满意度主键 * @return 患者满意度 */ @Override public PatSatisfaction selectPatSatisfactionById(Long id) { PatSatisfaction ps = patSatisfactionMapper.selectPatSatisfactionById(id); double total = ps.getDoctorSatisfaction() + ps.getAuthenticity() + ps.getWeekFinish() + ps.getStandard() + ps.getTimeliness() + ps.getLibrary() + ps.getEnvironment() + ps.getNurseSatisfaction(); ps.setTotal(total); return ps; } /** * 查询患者满意度列表 * * @param patSatisfaction 患者满意度 * @return 患者满意度 */ @Override public List selectPatSatisfactionList(PatSatisfaction patSatisfaction) { List patSatisfactions = patSatisfactionMapper.selectPatSatisfactionList(patSatisfaction); if (CollectionUtils.isNotEmpty(patSatisfactions)) { patSatisfactions.forEach(ps -> { double total = ps.getDoctorSatisfaction() + ps.getAuthenticity() + ps.getWeekFinish() + ps.getStandard() + ps.getTimeliness() + ps.getLibrary() + ps.getEnvironment() + ps.getNurseSatisfaction(); ps.setTotal(total); }); } return patSatisfactions; } /** * 新增患者满意度 * * @param patSatisfaction 患者满意度 * @return 结果 */ @Override public int insertPatSatisfaction(PatSatisfaction patSatisfaction) { patSatisfaction.setCreateTime(DateUtils.getNowDate()); patSatisfaction.setUpdateTime(DateUtils.getNowDate()); return patSatisfactionMapper.insertPatSatisfaction(patSatisfaction); } /** * 修改患者满意度 * * @param patSatisfaction 患者满意度 * @return 结果 */ @Override public int updatePatSatisfaction(PatSatisfaction patSatisfaction) { patSatisfaction.setUpdateTime(DateUtils.getNowDate()); return patSatisfactionMapper.updatePatSatisfaction(patSatisfaction); } /** * 批量删除患者满意度 * * @param ids 需要删除的患者满意度主键 * @return 结果 */ @Override public int deletePatSatisfactionByIds(Long[] ids) { return patSatisfactionMapper.deletePatSatisfactionByIds(ids); } /** * 删除患者满意度信息 * * @param id 患者满意度主键 * @return 结果 */ @Override public int deletePatSatisfactionById(Long id) { return patSatisfactionMapper.deletePatSatisfactionById(id); } /** * 患者满意度统计 * * @param patSatisfactionReqVO 患者满意度统计条件 * @return 患者满意度统计结果 */ @Override public PatSatisfactionResVO statistics(PatSatisfactionReqVO patSatisfactionReqVO) { //定义返参 PatSatisfactionResVO result = new PatSatisfactionResVO(); //答题总人数(已经回答问题的) int totalPerson = 0; // 总人数(包含未回答的) int allPerson = 0; //患者满意度统详情 List patSatisfactionDetailEntities = new ArrayList<>(); List serviceSubtasks = serviceSubtaskMapper.selectSatisfactionSubtaskList(patSatisfactionReqVO); // 按type分组:1-语音,2-问卷 Map> groupByType = serviceSubtasks.stream().filter(s -> s.getType() != null).collect(Collectors.groupingBy(ServiceSubtask::getType)); List voiceList = groupByType.getOrDefault("1", Collections.emptyList()); List questionnaireList = groupByType.getOrDefault("2", Collections.emptyList()); if (CollectionUtils.isNotEmpty(voiceList)) { //1. 先获取ivr_liba_script_assort的满意度分类ID IvrLibaScriptAssort ivrLibaScriptAssort = new IvrLibaScriptAssort(); ivrLibaScriptAssort.setOrgid(patSatisfactionReqVO.getOrgid()); List ivrLibaScriptAssorts = ivrLibaScriptAssortMapper.selectIvrLibaScriptAssortList(ivrLibaScriptAssort); List mydIdList = ivrLibaScriptAssorts.stream().filter(item -> item.getType() != null && item.getType().contains("myd")).map(IvrLibaScriptAssort::getId).collect(Collectors.toList()); //2 再获取该问题满意度分类下的IvrLibaTemplateScript的集合 List ivrLibaTemplateScripts = ivrLibaTemplateScriptMapper.queryLibTemplateIds(mydIdList); //3 再通过单个ivr_liba_template获取到所有的满意度问题 和 使用该模板的subTask人数 for (IvrLibaTemplateScript ivrLibaTemplateScript : ivrLibaTemplateScripts) { //记录单个明细信息 PatSatisfactionDetailEntity patSatisfactionDetailEntity = new PatSatisfactionDetailEntity(); //通过问题ID,获取问题选项(为了获取选项上的最高分最低分) IvrLibaTemplateTargetoption ivrLibaTemplateTargetoption = new IvrLibaTemplateTargetoption(); ivrLibaTemplateTargetoption.setScriptid(ivrLibaTemplateScript.getScriptid()); List ivrLibaTemplateTargetoptions = ivrLibaTemplateTargetoptionMapper.selectIvrLibaTemplateTargetoptionList(ivrLibaTemplateTargetoption); //获取libTemplateId List libTemplateIds = new ArrayList<>(); libTemplateIds.add(ivrLibaTemplateScript.getTemplateid()); // 再通过模板id获取问题发送总量 ServiceSubtaskSatisfactionEntity serviceSubtaskSatisfactionEntity = DtoConversionUtils.sourceToTarget(patSatisfactionReqVO, ServiceSubtaskSatisfactionEntity.class); serviceSubtaskSatisfactionEntity.setLibTemplateIds(libTemplateIds); serviceSubtaskSatisfactionEntity.setType(1); List subids = serviceSubtaskMapper.querySendCount(serviceSubtaskSatisfactionEntity); //获取填报数量 List fillCount = null; if (CollectionUtils.isNotEmpty(subids)) fillCount = serviceSubtaskDetailMapper.queryFillCount(subids, ivrLibaTemplateScript.getScriptContent()); // 根据matchedtext分组,统计各选项数量及占比 Map> matchedtextStats = new HashMap<>(); if (CollectionUtils.isNotEmpty(fillCount)) { int totalFill = fillCount.size(); fillCount.stream().filter(dto -> dto.getMatchedtext() != null).collect(Collectors.groupingBy(ServiceSubtaskDetailDTO::getMatchedtext, Collectors.counting())).forEach((matchedtext, count) -> { Map stat = new HashMap<>(); stat.put("count", count); stat.put("ratio", new BigDecimal((double) count / totalFill * 100).setScale(1, RoundingMode.HALF_UP).doubleValue()); matchedtextStats.put(matchedtext, stat); }); // 从fillCount中取targetvalue(&分隔的当前问题的所有选项),补全未出现的选项,count和ratio均为0 fillCount.stream().filter(dto -> dto.getTargetvalue() != null).findFirst().ifPresent(dto -> { for (String option : dto.getTargetvalue().split("&")) { String optionTrim = option.trim(); if (!matchedtextStats.containsKey(optionTrim)) { Map emptyStat = new HashMap<>(); emptyStat.put("count", 0L); emptyStat.put("ratio", 0.0); matchedtextStats.put(optionTrim, emptyStat); } } }); } patSatisfactionDetailEntity.setScriptContent(ivrLibaTemplateScript.getScriptContent()); patSatisfactionDetailEntity.setAnswerPerson(CollectionUtils.isEmpty(fillCount) ? 0 : fillCount.size()); patSatisfactionDetailEntity.setNoAnswerPerson(CollectionUtils.isNotEmpty(fillCount) && CollectionUtils.isNotEmpty(subids) ? (subids.size() - fillCount.size()) : CollectionUtils.isNotEmpty(subids) ? subids.size() : 0); patSatisfactionDetailEntity.setAverageScore(averageScore(fillCount)); patSatisfactionDetailEntity.setMaxScore(maxScore(ivrLibaTemplateTargetoptions)); patSatisfactionDetailEntity.setMinScore(minScore(ivrLibaTemplateTargetoptions)); patSatisfactionDetailEntity.setAnswerRate(CollectionUtils.isEmpty(subids) ? 0 : CollectionUtils.isEmpty(fillCount) ? 0 : fillCount.size() * 1.0 / subids.size()); patSatisfactionDetailEntity.setMatchedtextStats(matchedtextStats); patSatisfactionDetailEntities.add(patSatisfactionDetailEntity); totalPerson += CollectionUtils.isEmpty(fillCount) ? 0 : fillCount.size(); allPerson += subids.size(); } } if (CollectionUtils.isNotEmpty(questionnaireList)) { //1. 先获取svy_lib_script_category的满意度分类ID SvyLibScriptCategory svyLibScriptCategory = new SvyLibScriptCategory(); svyLibScriptCategory.setOrgid(patSatisfactionReqVO.getOrgid()); List svyLibScriptCategories = svyLibScriptCategoryMapper.selectSvyLibScriptCategoryList(svyLibScriptCategory); List mydIdList = svyLibScriptCategories.stream().filter(item -> item.getType() != null && item.getType().contains("myd")).map(SvyLibScriptCategory::getId).collect(Collectors.toList()); //2 再获取该问题满意度分类下的IvrLibaTemplateScript的集合 List svyLibTemplateScripts = svyLibTemplateScriptMapper.querySvyLibTemplateIds(mydIdList); for (SvyLibTemplateScript svyLibTemplateScript : svyLibTemplateScripts) { //记录单个明细信息 PatSatisfactionDetailEntity patSatisfactionDetailEntity = new PatSatisfactionDetailEntity(); //通过问题ID,获取问题选项(为了获取选项上的最高分最低分) SvyLibTemplateTargetoption svyLibTemplateTargetoption = new SvyLibTemplateTargetoption(); svyLibTemplateTargetoption.setScriptid(svyLibTemplateScript.getId()); List svyLibTemplateTargetoptions = svyLibTemplateTargetoptionMapper.selectSvyLibTemplateTargetoptionList(svyLibTemplateTargetoption); //获取libTemplateId List libTemplateIds = new ArrayList<>(); libTemplateIds.add(svyLibTemplateScript.getSvyid()); // 再通过模板id获取问题发送总量 ServiceSubtaskSatisfactionEntity serviceSubtaskSatisfactionEntity = DtoConversionUtils.sourceToTarget(patSatisfactionReqVO, ServiceSubtaskSatisfactionEntity.class); serviceSubtaskSatisfactionEntity.setLibTemplateIds(libTemplateIds); serviceSubtaskSatisfactionEntity.setType(2); List subids = serviceSubtaskMapper.querySendCount(serviceSubtaskSatisfactionEntity); //获取填报数量 List fillCount = null; if (CollectionUtils.isNotEmpty(subids)) fillCount = serviceSubtaskDetailMapper.queryFillCount(subids, svyLibTemplateScript.getScriptContent()); // 根据matchedtext分组,统计各选项数量及占比 Map> matchedtextStats = new HashMap<>(); if (CollectionUtils.isNotEmpty(fillCount)) { int totalFill = fillCount.size(); fillCount.stream().filter(dto -> dto.getMatchedtext() != null).collect(Collectors.groupingBy(ServiceSubtaskDetailDTO::getMatchedtext, Collectors.counting())).forEach((matchedtext, count) -> { Map stat = new HashMap<>(); stat.put("count", count); stat.put("ratio", new BigDecimal((double) count / totalFill * 100).setScale(1, RoundingMode.HALF_UP).doubleValue()); matchedtextStats.put(matchedtext, stat); }); // 从fillCount中取targetvalue(&分隔的当前问题的所有选项),补全未出现的选项,count和ratio均为0 fillCount.stream().filter(dto -> dto.getTargetvalue() != null).findFirst().ifPresent(dto -> { for (String option : dto.getTargetvalue().split("&")) { String optionTrim = option.trim(); if (!matchedtextStats.containsKey(optionTrim)) { Map emptyStat = new HashMap<>(); emptyStat.put("count", 0L); emptyStat.put("ratio", 0.0); matchedtextStats.put(optionTrim, emptyStat); } } }); } patSatisfactionDetailEntity.setScriptContent(svyLibTemplateScript.getScriptContent()); patSatisfactionDetailEntity.setAnswerPerson(CollectionUtils.isEmpty(fillCount) ? 0 : fillCount.size()); patSatisfactionDetailEntity.setNoAnswerPerson(CollectionUtils.isNotEmpty(fillCount) && CollectionUtils.isNotEmpty(subids) ? (subids.size() - fillCount.size()) : CollectionUtils.isNotEmpty(subids) ? subids.size() : 0); patSatisfactionDetailEntity.setAverageScore(averageScore(fillCount)); OptionalDouble maxOpt = svyLibTemplateTargetoptions.stream().filter(dto -> dto.getScore() != null).mapToDouble(dto -> dto.getScore().doubleValue()).max(); patSatisfactionDetailEntity.setMaxScore(maxOpt.isPresent() ? new BigDecimal(maxOpt.getAsDouble()).setScale(1, RoundingMode.HALF_UP).doubleValue() : 0.0); OptionalDouble minOpt = svyLibTemplateTargetoptions.stream().filter(dto -> dto.getScore() != null).mapToDouble(dto -> dto.getScore().doubleValue()).min(); patSatisfactionDetailEntity.setMinScore(minOpt.isPresent() ? new BigDecimal(minOpt.getAsDouble()).setScale(1, RoundingMode.HALF_UP).doubleValue() : 0.0); patSatisfactionDetailEntity.setAnswerRate(CollectionUtils.isEmpty(subids) ? 0 : CollectionUtils.isEmpty(fillCount) ? 0 : fillCount.size() * 1.0 / subids.size()); patSatisfactionDetailEntity.setMatchedtextStats(matchedtextStats); patSatisfactionDetailEntities.add(patSatisfactionDetailEntity); totalPerson += CollectionUtils.isEmpty(fillCount) ? 0 : fillCount.size(); allPerson += subids.size(); } } result.setTotalAnswerRate(Arith.div(totalPerson, allPerson)); result.setPatSatisfactionDetailEntities(patSatisfactionDetailEntities); result.setTotalPerson(totalPerson); return result; } @Override public Map satisfactionGraph(PatSatisfactionReqVO patSatisfactionReqVO) { //用来记录柱状图信息(key=满意度分类名称,value=满意度分类下的总人数和填报人数) Map zzt = new HashMap<>(); // 记录一下每种分类的填报情况 Map> assortNameFillCountMap = new HashMap<>(); if (patSatisfactionReqVO.getType() == null || patSatisfactionReqVO.getType() == 1) { //1. 先获取ivr_liba_script_assort的满意度分类ID IvrLibaScriptAssort ivrLibaScriptAssort = new IvrLibaScriptAssort(); ivrLibaScriptAssort.setOrgid(patSatisfactionReqVO.getOrgid()); List ivrLibaScriptAssorts = ivrLibaScriptAssortMapper.selectIvrLibaScriptAssortList(ivrLibaScriptAssort); List mydIdList = ivrLibaScriptAssorts.stream().filter(item -> item.getType() != null && item.getType().contains("myd")).map(IvrLibaScriptAssort::getId).collect(Collectors.toList()); //2 再获取该问题满意度分类下的IvrLibaTemplateScript的集合 List ivrLibaTemplateScripts = ivrLibaTemplateScriptMapper.queryLibTemplateIds(mydIdList); //3 按scriptAssortname分组,并获取每个集合进行遍历,计算每个分类的总人数和填报人数 Map> scriptGroupByAssortName = ivrLibaTemplateScripts.stream().filter(item -> item.getScriptAssortname() != null).collect(Collectors.groupingBy(IvrLibaTemplateScript::getScriptAssortname)); if (scriptGroupByAssortName != null && !scriptGroupByAssortName.isEmpty()) { for (String assortNameKey : scriptGroupByAssortName.keySet()) { //记录总人数 Double subidAll = 0.0; //记录总的填报人数 Double fillCountAll = 0.0; //获取该分类的填报总数量 List allFillCountList = new ArrayList<>(); List ivrLibaTemplateScriptList = scriptGroupByAssortName.get(assortNameKey); for (IvrLibaTemplateScript ivrLibaTemplateScript : ivrLibaTemplateScriptList) { //获取libTemplateId List libTemplateIds = new ArrayList<>(); libTemplateIds.add(ivrLibaTemplateScript.getTemplateid()); // 再通过模板id获取问题发送总量 ServiceSubtaskSatisfactionEntity serviceSubtaskSatisfactionEntity = DtoConversionUtils.sourceToTarget(patSatisfactionReqVO, ServiceSubtaskSatisfactionEntity.class); serviceSubtaskSatisfactionEntity.setLibTemplateIds(libTemplateIds); serviceSubtaskSatisfactionEntity.setType(1); List subids = serviceSubtaskMapper.querySendCount(serviceSubtaskSatisfactionEntity); //获取填报数量 List fillCount = null; if (CollectionUtils.isNotEmpty(subids)) { fillCount = serviceSubtaskDetailMapper.queryFillCount(subids, ivrLibaTemplateScript.getScriptContent()); if (CollectionUtils.isNotEmpty(fillCount)) allFillCountList.addAll(fillCount); } subidAll += subids.size(); fillCountAll += CollectionUtils.isEmpty(fillCount) ? 0 : fillCount.size(); } // 这里先记录一下每个分类的总人数和填报人数 Map personCount = new HashMap<>(); personCount.put("fillCountAll", fillCountAll); personCount.put("subidAll", subidAll); personCount.put("receiveRate", Arith.div(fillCountAll, subidAll)); personCount.put("averageScore", averageScore(allFillCountList)); zzt.put(assortNameKey, personCount); assortNameFillCountMap.put(assortNameKey, allFillCountList); } } } if (patSatisfactionReqVO.getType() == null || patSatisfactionReqVO.getType() == 2) { //1. 先获取svy_lib_script_category的满意度分类ID SvyLibScriptCategory svyLibScriptCategory = new SvyLibScriptCategory(); svyLibScriptCategory.setOrgid(patSatisfactionReqVO.getOrgid()); List svyLibScriptCategories = svyLibScriptCategoryMapper.selectSvyLibScriptCategoryList(svyLibScriptCategory); List mydIdList = svyLibScriptCategories.stream().filter(item -> item.getType() != null && item.getType().contains("myd")).map(SvyLibScriptCategory::getId).collect(Collectors.toList()); //2 再获取该问题满意度分类下的IvrLibaTemplateScript的集合 List svyLibTemplateScripts = svyLibTemplateScriptMapper.querySvyLibTemplateIds(mydIdList); //3 按scriptAssortname分组,并获取每个集合进行遍历,计算每个分类的总人数和填报人数 Map> scriptGroupByatCegoryName = svyLibTemplateScripts.stream().filter(item -> item.getCategoryName() != null).collect(Collectors.groupingBy(SvyLibTemplateScript::getCategoryName)); if (scriptGroupByatCegoryName != null && !scriptGroupByatCegoryName.isEmpty()) { for (String categoryNameKey : scriptGroupByatCegoryName.keySet()) { //记录总人数 Double subidAll = 0.0; //记录总的填报人数 Double fillCountAll = 0.0; //获取该分类的填报总数量 List allFillCountList = new ArrayList<>(); List svyLibTemplateScriptList = scriptGroupByatCegoryName.get(categoryNameKey); for (SvyLibTemplateScript svyLibTemplateScript : svyLibTemplateScriptList) { //获取libTemplateId List libTemplateIds = new ArrayList<>(); libTemplateIds.add(svyLibTemplateScript.getSvyid()); // 再通过模板id获取问题发送总量 ServiceSubtaskSatisfactionEntity serviceSubtaskSatisfactionEntity = DtoConversionUtils.sourceToTarget(patSatisfactionReqVO, ServiceSubtaskSatisfactionEntity.class); serviceSubtaskSatisfactionEntity.setLibTemplateIds(libTemplateIds); serviceSubtaskSatisfactionEntity.setType(2); List subids = serviceSubtaskMapper.querySendCount(serviceSubtaskSatisfactionEntity); //获取填报数量 List fillCount = null; if (CollectionUtils.isNotEmpty(subids)) { fillCount = serviceSubtaskDetailMapper.queryFillCount(subids, svyLibTemplateScript.getScriptContent()); if (CollectionUtils.isNotEmpty(fillCount)) allFillCountList.addAll(fillCount); } subidAll += subids.size(); fillCountAll += CollectionUtils.isEmpty(fillCount) ? 0 : fillCount.size(); } // 这里先记录一下每个分类的总人数和填报人数 Map personCount = (Map) zzt.get(categoryNameKey); if (!Objects.isNull(personCount)) { Double fillCountAll2 = (Double) personCount.get("fillCountAll"); Double subidAll2 = (Double) personCount.get("subidAll"); personCount.put("fillCountAll", fillCountAll2 + fillCountAll); personCount.put("subidAll", subidAll2 + subidAll); personCount.put("receiveRate", Arith.div((Double) personCount.get("fillCountAll"), (Double) personCount.get("subidAll"))); //看一下之前有没有该分类的填报量 List serviceSubtaskDetailDTOS = assortNameFillCountMap.get(categoryNameKey); if (CollectionUtils.isNotEmpty(serviceSubtaskDetailDTOS)) allFillCountList.addAll(serviceSubtaskDetailDTOS); personCount.put("averageScore", averageScore(allFillCountList)); } else { personCount = new HashMap<>(); personCount.put("fillCountAll", fillCountAll); personCount.put("subidAll", subidAll); personCount.put("receiveRate", Arith.div(fillCountAll, subidAll)); personCount.put("averageScore", averageScore(allFillCountList)); zzt.put(categoryNameKey, personCount); } } } } return zzt; } /** * 获取最高分 * * @param options * @return */ private Double maxScore(List options) { if (CollectionUtils.isEmpty(options)) return 0.0; OptionalDouble maxOpt = options.stream().filter(dto -> dto.getScore() != null).mapToDouble(dto -> dto.getScore().doubleValue()).max(); return maxOpt.isPresent() ? new BigDecimal(maxOpt.getAsDouble()).setScale(1, RoundingMode.HALF_UP).doubleValue() : 0.0; } /** * 获取最低分 * * @param options * @return */ private Double minScore(List options) { if (CollectionUtils.isEmpty(options)) return 0.0; OptionalDouble minOpt = options.stream().filter(dto -> dto.getScore() != null).mapToDouble(dto -> dto.getScore().doubleValue()).min(); return minOpt.isPresent() ? new BigDecimal(minOpt.getAsDouble()).setScale(1, RoundingMode.HALF_UP).doubleValue() : 0.0; } /** * 获取平均分 * * @param fillCount * @return */ private Double averageScore(List fillCount) { if (CollectionUtils.isEmpty(fillCount)) return 0.0; double sum = fillCount.stream().filter(dto -> dto.getScore() != null && !dto.getScore().isEmpty()).mapToDouble(dto -> Double.parseDouble(dto.getScore())).sum(); return new BigDecimal(sum / fillCount.size()).setScale(1, RoundingMode.HALF_UP).doubleValue(); } }