package com.ruoyi.satisfaction.controller; import com.ruoyi.common.annotation.AddOrgId; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.satisfaction.domain.ServiceSubtaskDetailRatioExport; import com.ruoyi.satisfaction.domain.dto.QuestionResultDTO; import com.ruoyi.satisfaction.domain.row.SatisfactionSubtaskQuery; import com.ruoyi.satisfaction.domain.row.SatisfactionSubtaskRef; import com.ruoyi.satisfaction.domain.vo.MydTrendReqVO; import com.ruoyi.satisfaction.domain.vo.MzMydScoreRankVO; import com.ruoyi.satisfaction.domain.vo.QuestionCountKsOrBqVO; import com.ruoyi.satisfaction.domain.vo.SltdMydTotalVO; import com.ruoyi.satisfaction.service.SatisfactionSubtaskService; import com.ruoyi.satisfaction.service.strategy.SatisfactionScoreStrategyResolver; import com.ruoyi.satisfaction.service.strategy.SatisfactionSites; 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.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * 省立同德 / 门诊满意度统计 Controller(已迁入 ruoyi-satisfaction 模块)。 *

* ⚠️ URL 与出入参一字未改:这些端点原本挂在 * {@code ServiceSubtaskDetailController}({@code /smartor/serviceSubtaskDetail})下, * 迁移后 {@code @RequestMapping} 与全部 {@code @PostMapping} 路径保持原样,前端无需任何改动。 *

* 迁移时唯一调整的一处:{@code sltdMydTotalByScore} 原本调用 * {@code serviceSubtaskService.getServiceSubtasks(...)}(随访主干 Service,不能搬), * 现改为调用满意度模块自带的 {@link SatisfactionSubtaskService#getMydSubtaskRefs}; * 查询条件与后续统计口径逐字保持一致(见各方法内注释)。 *

* 开关:本 Controller 始终注册(不随 {@code satisfaction.enabled} 消失)。 * 模块被关闭时由 {@code SatisfactionAvailabilityInterceptor} * (见 {@code config/SatisfactionWebConfig})在进入方法前抛出 {@code ServiceException}, * 调用方会拿到明确的错误提示(而不是 404)。 * 注意:同一前缀 {@code /smartor/serviceSubtaskDetail} 下的随访明细端点不受影响 * —— 拦截器只挂在本类的 5 个满意度路径上。 */ @Api(description = "省立同德满意度统计") @RestController @RequestMapping("/smartor/serviceSubtaskDetail") public class SltdSatisfactionController extends BaseController { /** * 站点口径策略解析器:综合满意度得分按站点(省立同德)取策略计算, * 不再把算法与常量写在 Controller 里。 */ @Autowired private SatisfactionScoreStrategyResolver satisfactionScoreStrategyResolver; @Autowired private SatisfactionSubtaskService satisfactionSubtaskService; // ================================================================ 端点(URL 原样) /** * 省立同德满意度数据统计(按题目统计选项数量/占比,并给出该题的综合得分) *

* 综合满意度 = (很满意项数×1 + 满意项数×0.8 + 一般项数×0.6 + 不满意项数×0.4 + 很不满意项数×0.2) * / (很满意项数 + 满意项数 + 一般项数 + 不满意项数 + 很不满意项数) × 100%; * 「未经历」「不适用」等不在五档里的选项既不进分子也不进分母。 */ @ApiOperation("省立同德满意度数据统计") @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid") @PostMapping("/sltdMydTotalByScore") public Map sltdMydTotalByScore(@RequestBody SltdMydTotalVO sltdMydTotalVO) { List subtaskRefs = null; if (CollectionUtils.isNotEmpty(sltdMydTotalVO.getServiceTypeList())) { //serviceType可能会出现多个,不想多写方法了,一个一个查询吧 for (String st : sltdMydTotalVO.getServiceTypeList()) { SatisfactionSubtaskQuery query = new SatisfactionSubtaskQuery(); query.setServiceType(st); query.setEndOutHospTime(sltdMydTotalVO.getEndOutHospTime()); query.setStartOutHospTime(sltdMydTotalVO.getStartOutHospTime()); query.setDeptOrDistrict(sltdMydTotalVO.getDeptOrDistrict()); query.setLeavehospitaldistrictcodes(sltdMydTotalVO.getLeavehospitaldistrictcodes()); query.setLeaveldeptcodes(sltdMydTotalVO.getLeaveldeptcodes()); //机构/院区由 @AddOrgId 注入,落到查询条件上,避免跨机构取数 query.setOrgid(sltdMydTotalVO.getOrgid()); query.setCampusid(sltdMydTotalVO.getCampusid()); List refs = satisfactionSubtaskService.getMydSubtaskRefs(query); if (CollectionUtils.isNotEmpty(subtaskRefs)) { subtaskRefs.addAll(refs); } else { if (CollectionUtils.isEmpty(subtaskRefs)) subtaskRefs = refs; } } } // 按 questiontext 分组后转换为 QuestionResultVO List result = new ArrayList<>(); if (CollectionUtils.isNotEmpty(subtaskRefs)) { List detailList = satisfactionSubtaskService.getDetailRatio(sltdMydTotalVO, subtaskRefs); 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); //综合得分:(很满意×1 + 满意×0.8 + 一般×0.6 + 不满意×0.4 + 很不满意×0.2) / 五档项数之和 × 100% questionResultDTO.setAvgScore(calcMydScore(exportList)); result.add(questionResultDTO); }); } } return success(result); } @ApiOperation("省立同德满意度数据统计通过科室或病区") @PostMapping("/statQuestionOptionByKsOrBq") public Map statQuestionOptionByKsOrBq(@RequestBody QuestionCountKsOrBqVO questionResultVO) { return success(satisfactionSubtaskService.statQuestionOptionByKsOrBq(questionResultVO)); } @ApiOperation("省立同德患者满意度趋势") @PostMapping("/mydTrend") public Map mydTrend(@RequestBody MydTrendReqVO mydTrendReqVO) { return success(satisfactionSubtaskService.mydTrend(mydTrendReqVO)); } @ApiOperation("省立同德患者满意度维护") @PostMapping("/sltdMydTotalByDimension") public Map sltdMydTotalByDimension(@RequestBody MydTrendReqVO mydTrendReqVO) { return success(satisfactionSubtaskService.sltdMydTotalByDimension(mydTrendReqVO)); } @ApiOperation("门诊满意度综合得分排名(按科室或病区)") @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid") @PostMapping("/mzMydScoreRank") public Map mzMydScoreRank(@RequestBody MzMydScoreRankVO mzMydScoreRankVO) { return success(satisfactionSubtaskService.mzMydScoreRank(mzMydScoreRankVO)); } // ================================================================ 综评计算(改走站点策略) /** * 计算一道题的满意度综合得分(百分制,保留两位小数)。 *

* 策略化改造:算法本身已原样搬到 * {@link com.ruoyi.satisfaction.service.strategy.SltdSatisfactionScoreStrategy} * (省立同德口径),本方法只负责"按站点取策略 + 把 null 呈现为 0.00"。 * 计算过程与返回值与迁移前完全一致。 * * @param exportList 同一道题目的选项统计明细 * @return 百分制综合得分,无有效项数时返回 "0.00" */ private String calcMydScore(List exportList) { BigDecimal score = satisfactionScoreStrategyResolver .resolve(SatisfactionSites.SLTD) .comprehensiveScore(exportList); return score == null ? "0.00" : score.toPlainString(); } }