package com.ruoyi.web.controller.smartor; import com.ruoyi.common.annotation.AddOrgId; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.DtoConversionUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.system.service.ISysConfigService; import com.smartor.domain.DTO.QuestionResultDTO; import com.smartor.domain.*; import com.smartor.domain.VO.MydTrendReqVO; import com.smartor.domain.VO.MzMydScoreRankVO; import com.smartor.domain.VO.QuestionCountKsOrBqVO; import com.smartor.domain.VO.SltdMydTotalVO; import com.smartor.domain.entity.ServiceSubtaskEntity; import com.smartor.service.IServiceSubtaskDetailService; import com.smartor.service.IServiceSubtaskService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.*; import java.util.stream.Collectors; import static com.ruoyi.framework.datasource.DynamicDataSourceContextHolder.log; /** * 语音任务结果明细Controller * * @author smartor * @date 2023-03-24 */ @Api(description = "语音任务结果明细") @RestController @RequestMapping("/smartor/serviceSubtaskDetail") public class ServiceSubtaskDetailController extends BaseController { @Autowired private IServiceSubtaskDetailService ServiceSubtaskDetailService; @Autowired private IServiceSubtaskService serviceSubtaskService; @Autowired private ISysConfigService configService; /** * 查询单个人的语音信息 */ @ApiOperation("查询单个人的语音信息通过子任务ID") @PostMapping("/getPersonVoice") public Map getPersonVoice(@RequestBody ServiceSubtaskDetail serviceSubtaskDetail) { Map map = new HashMap<>(); if (serviceSubtaskDetail.getSubId() == null) { map.put("code", 500); map.put("msg", "查询失败,请查检入参"); } else map = ServiceSubtaskDetailService.selectServiceSubtaskDetailList(serviceSubtaskDetail); return map; } /** * 获取语音任务呼叫明细详细信息 */ //@PreAuthorize("@ss.hasPermi('smartor:ServiceSubtaskDetail:query')") @ApiOperation("获取任务结果明细详细信息") @GetMapping(value = "/getInfo/{calldetailid}") public AjaxResult getInfo(@PathVariable("calldetailid") String calldetailid) { return success(ServiceSubtaskDetailService.selectServiceSubtaskDetailByCalldetailid(calldetailid)); } /** * 新增任务结果明细 */ //@PreAuthorize("@ss.hasPermi('smartor:ServiceSubtaskDetail:add')") @ApiOperation("新增任务结果明细") @Log(title = "语音任务呼叫明细", businessType = BusinessType.INSERT) @PostMapping("/add") public AjaxResult add(@RequestBody ServiceSubtaskDetail serviceSubtaskDetail) { SysUser user = getLoginUser().getUser(); serviceSubtaskDetail.setOrgid(user.getOrgid()); return toAjax(ServiceSubtaskDetailService.insertServiceSubtaskDetail(serviceSubtaskDetail)); } /** * 新增任务结果明细 */ //@PreAuthorize("@ss.hasPermi('smartor:ServiceSubtaskDetail:add')") @ApiOperation("批量新增任务结果明细") @Log(title = "语音任务呼叫明细", businessType = BusinessType.INSERT) @PostMapping("/batchAdd") public AjaxResult batchAdd(@RequestBody ServiceSubTaskDetailReq serviceSubTaskDetailReq) { if (CollectionUtils.isEmpty(serviceSubTaskDetailReq.getServiceSubtaskDetailList()) && serviceSubTaskDetailReq.getSubId() == null) { return error("参数不全,请检查入参"); } int i = ServiceSubtaskDetailService.batchInsertServiceSubtaskDetail(serviceSubTaskDetailReq.getServiceSubtaskDetailList()); Boolean aBoolean = false; if (i == 1) { ServiceSubtask serviceSubtask = new ServiceSubtask(); serviceSubtask.setSendstate(6L); serviceSubtask.setId(serviceSubTaskDetailReq.getSubId()); serviceSubtask.setRemark("数据补充完成"); serviceSubtask.setUpdateTime(new Date()); serviceSubtask.setFinishtime(new Date()); serviceSubtask.setPreachform("1"); serviceSubtask.setResult("success"); aBoolean = serviceSubtaskService.updateServiceSubtask(serviceSubtask); } return toAjax(aBoolean); } /** * 修改语音任务呼叫明细 */ //@PreAuthorize("@ss.hasPermi('smartor:ServiceSubtaskDetail:edit')") @Log(title = "语音任务呼叫明细", businessType = BusinessType.UPDATE) @ApiOperation("修改任务结果明细") @PostMapping("/edit") public AjaxResult edit(@RequestBody ServiceSubtaskDetail serviceSubtaskDetail) { return toAjax(ServiceSubtaskDetailService.updateServiceSubtaskDetail(serviceSubtaskDetail)); } /** * 修改语音任务呼叫明细 */ //@PreAuthorize("@ss.hasPermi('smartor:ServiceSubtaskDetail:edit')") @Log(title = "语音任务呼叫明细", businessType = BusinessType.UPDATE) @ApiOperation("修改任务结果明细") @PostMapping("/batchEdit") public AjaxResult batchEdit(@RequestBody List serviceSubtaskDetails) { if (CollectionUtils.isEmpty(serviceSubtaskDetails)) { return error("需要修改语音详情为空"); } for (ServiceSubtaskDetail serviceSubtaskDetail : serviceSubtaskDetails) { ServiceSubtaskDetailService.updateServiceSubtaskDetail(serviceSubtaskDetail); } return toAjax(1); } @ApiOperation("修改任务结果明细通过条件") @PostMapping("/editByCondition") public AjaxResult updateSSDByCondition(@RequestBody ServiceSubtaskDetail serviceSubtaskDetail) { return toAjax(ServiceSubtaskDetailService.updateSSDByCondition(serviceSubtaskDetail)); } /** * 删除语音任务呼叫明细 */ //@PreAuthorize("@ss.hasPermi('smartor:ServiceSubtaskDetail:remove')") @Log(title = "语音任务呼叫明细", businessType = BusinessType.DELETE) @GetMapping("/remove/{calldetailids}") public AjaxResult remove(@PathVariable String[] calldetailids) { return toAjax(ServiceSubtaskDetailService.deleteServiceSubtaskDetailByCalldetailids(calldetailids)); } /** * 指标统计 */ //@PreAuthorize("@ss.hasPermi('smartor:ServiceSubtaskDetail:add')") @ApiOperation("指标统计") @GetMapping(value = "/countPatByTarget/{targetid}") public AjaxResult countPatByTarget(@PathVariable("targetid") Long targetid) { return success(ServiceSubtaskDetailService.countPatByTarget(targetid)); } /** * 获取随访详情-指标详情(超链接) */ @ApiOperation("获取随访详情-指标详情") @PostMapping("/getServiceSubtaskDetails") public List> getServiceSubtaskDetails(@RequestBody ServiceSubtaskDetailVO serviceSubtaskDetailVO) { List> map = new ArrayList<>(); map = ServiceSubtaskDetailService.getServiceSubtaskDetails(serviceSubtaskDetailVO); return map; } /** * 获取随访详情-指标详情(超链接) */ @ApiOperation("获取随访详情-指标详情(超链接)") @PostMapping("/getServiceSubtaskDetailsHyperlink") public List> getServiceSubtaskDetailsHyperlink(@RequestBody ServiceSubtaskDetailVO serviceSubtaskDetailVO) { LoginUser loginUser = getLoginUser(); String orgid = loginUser.getUser().getOrgid(); String configKey = serviceSubtaskDetailVO.getConfigKey(); if (StringUtils.isNotEmpty(configKey)) { String configValue = configService.selectConfigByKey(configKey, orgid); serviceSubtaskDetailVO.setConfigValue(configValue); if (StringUtils.isEmpty(configValue)) { String logInfo = "getSfStatisticsCount-参数 " + configKey + " 未配置, 请配置好后重试"; log.error(logInfo); } } List> map = new ArrayList<>(); map = ServiceSubtaskDetailService.getServiceSubtaskDetails(serviceSubtaskDetailVO); return map; } /** * 省立同德满意度数据统计(按题目统计选项数量/占比,并给出该题的综合得分) *

* 综合满意度 = Σ(选项分值 × 项数) / Σ项数 / 本题最高分值 × 100%, * 分值取 1、0.8、0.6、0.4、0.2 五档(很满意、满意、一般、不满意、很不满意); * 「不适用」、0 分、空值等既不进分子也不进分母。 */ @ApiOperation("省立同德满意度数据统计") @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid") @PostMapping("/sltdMydTotalByScore") public Map sltdMydTotalByScore(@RequestBody SltdMydTotalVO sltdMydTotalVO) { List serviceSubtaskResList = null; if (CollectionUtils.isNotEmpty(sltdMydTotalVO.getServiceTypeList())) { //serviceType可能会出现多个,不想多写方法了,一个一个查询吧 for (String st : sltdMydTotalVO.getServiceTypeList()) { ServiceSubtaskEntity serviceSubtaskEntity = new ServiceSubtaskEntity(); serviceSubtaskEntity.setServiceType(st); serviceSubtaskEntity.setEndOutHospTime(sltdMydTotalVO.getEndOutHospTime()); serviceSubtaskEntity.setStartOutHospTime(sltdMydTotalVO.getStartOutHospTime()); serviceSubtaskEntity.setDeptOrDistrict(sltdMydTotalVO.getDeptOrDistrict()); serviceSubtaskEntity.setLeavehospitaldistrictcodes(sltdMydTotalVO.getLeavehospitaldistrictcodes()); serviceSubtaskEntity.setLeaveldeptcodes(sltdMydTotalVO.getLeaveldeptcodes()); //机构/院区由 @AddOrgId 注入,落到查询条件上,避免跨机构取数 serviceSubtaskEntity.setOrgid(sltdMydTotalVO.getOrgid()); serviceSubtaskEntity.setCampusid(sltdMydTotalVO.getCampusid()); if (serviceSubtaskEntity != null) { List serviceSubtasks = serviceSubtaskService.getServiceSubtasks(serviceSubtaskEntity); List serviceSubtaskRes = DtoConversionUtils.sourceToTarget(serviceSubtasks, ServiceSubtaskRes.class); if (CollectionUtils.isNotEmpty(serviceSubtaskResList)) { serviceSubtaskResList.addAll(serviceSubtaskRes); } else { if (CollectionUtils.isEmpty(serviceSubtaskResList)) serviceSubtaskResList = serviceSubtaskRes; } } } } // 按 questiontext 分组后转换为 QuestionResultVO List result = new ArrayList<>(); if (CollectionUtils.isNotEmpty(serviceSubtaskResList)) { List detailList = serviceSubtaskService.getDetailRatio(sltdMydTotalVO, serviceSubtaskResList); if (detailList == null) { //serviceType 组合不在支持范围内(如同时含随访 2 和满意度 6/14),给明确提示而不是悄悄返回空数组 return error("当前 serviceType 组合暂不支持统计问题占比,请分开查询(仅支持随访 2 或满意度 6/14)"); } if (CollectionUtils.isNotEmpty(detailList)) { Map> groupedMap = detailList.stream().collect(Collectors.groupingBy(ServiceSubtaskDetailRatioExport::getQuestiontext)); groupedMap.forEach((questionText, exportList) -> { QuestionResultDTO questionResultDTO = new QuestionResultDTO(); questionResultDTO.setQeustionText(questionText); questionResultDTO.setSubtaskDetailRatioExportList(exportList); //综合得分:Σ(分值×项数) / Σ项数 / 本题最高分值 × 100%,不适用/0分/空值不进分子也不进分母 questionResultDTO.setAvgScore(calcMydScore(exportList)); result.add(questionResultDTO); }); } } return success(result); } @ApiOperation("省立同德满意度数据统计通过科室或病区") @PostMapping("/statQuestionOptionByKsOrBq") public Map statQuestionOptionByKsOrBq(@RequestBody QuestionCountKsOrBqVO questionResultVO) { return success(serviceSubtaskService.statQuestionOptionByKsOrBq(questionResultVO)); } @ApiOperation("省立同德患者满意度趋势") @PostMapping("/mydTrend") public Map mydTrend(@RequestBody MydTrendReqVO mydTrendReqVO) { return success(serviceSubtaskService.mydTrend(mydTrendReqVO)); } @ApiOperation("省立同德患者满意度维护") @PostMapping("/sltdMydTotalByDimension") public Map sltdMydTotalByDimension(@RequestBody MydTrendReqVO mydTrendReqVO) { return success(serviceSubtaskService.sltdMydTotalByDimension(mydTrendReqVO)); } @ApiOperation("门诊满意度综合得分排名(按科室或病区)") @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid") @PostMapping("/mzMydScoreRank") public Map mzMydScoreRank(@RequestBody MzMydScoreRankVO mzMydScoreRankVO) { return success(serviceSubtaskService.mzMydScoreRank(mzMydScoreRankVO)); } /** * 多选题的问题类型 */ private static final String VALUE_TYPE_MULTIPLE = "2"; /** * 计算一道题的满意度综合得分(百分制,保留两位小数) *

* 综合满意度 = Σ(选项分值 × 项数) / Σ项数 / 本题最高分值 × 100% * 等价于 Σ(很满意×1 + 满意×0.8 + 一般×0.6 + 不满意×0.4 + 很不满意×0.2) / Σ(五档项数) × 100%。 * 用本题最高分值归一化,兼容明细分值存 0-1 权重(1、0.8…)和存 5 分制(5、4…)两种口径。 * * @param exportList 同一道题目的选项统计明细 * @return 百分制综合得分,无有效项数时返回 "0.00" */ private String calcMydScore(List exportList) { if (CollectionUtils.isEmpty(exportList)) return "0.00"; BigDecimal scoreSum = BigDecimal.ZERO; //分子:Σ(选项分值 × 项数) BigDecimal countSum = BigDecimal.ZERO; //分母:Σ项数 BigDecimal maxScore = null; //本题最高分值,用于归一化 for (ServiceSubtaskDetailRatioExport detail : exportList) { //多选题一行是多个选项的分值之和,分母只算 1 项,会把得分算高,不计入综合得分 if (VALUE_TYPE_MULTIPLE.equals(detail.getValueType())) continue; //「不适用」不在公式的五档里,不计入综合得分 if (isNotApplicable(detail)) continue; BigDecimal score = parseDecimal(detail.getScore()); BigDecimal count = parseDecimal(detail.getCount()); if (score == null || count == null) continue; //0 分(未答/空答)、空数量:既不进分子也不进分母 if (score.signum() <= 0 || count.signum() <= 0) continue; if (maxScore == null || score.compareTo(maxScore) > 0) maxScore = score; scoreSum = scoreSum.add(score.multiply(count)); countSum = countSum.add(count); } if (maxScore == null || countSum.signum() <= 0) return "0.00"; return scoreSum.divide(countSum, 6, RoundingMode.HALF_UP) .divide(maxScore, 6, RoundingMode.HALF_UP) .multiply(BigDecimal.valueOf(100)) .setScale(2, RoundingMode.HALF_UP) .toPlainString(); } /** * 是否「不适用」选项 */ private boolean isNotApplicable(ServiceSubtaskDetailRatioExport detail) { return containsNotApplicable(detail.getOptionresult()) || containsNotApplicable(detail.getTargetvalue()); } private boolean containsNotApplicable(String optionText) { if (StringUtils.isEmpty(optionText)) return false; String text = optionText.trim(); return text.contains("不适用") || text.contains("未答") || text.contains("拒答"); } /** * 字符串转 BigDecimal,空值/非法值返回 null */ private BigDecimal parseDecimal(String value) { if (StringUtils.isEmpty(value)) return null; try { return new BigDecimal(value.trim()); } catch (NumberFormatException e) { return null; } } }