liusheng
2026-10-12 0c1f8c479065739f3c7a5126de8e667035d00b58
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
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 模块)。
 * <p>
 * ⚠️ <b>URL 与出入参一字未改</b>:这些端点原本挂在
 * {@code ServiceSubtaskDetailController}({@code /smartor/serviceSubtaskDetail})下,
 * 迁移后 {@code @RequestMapping} 与全部 {@code @PostMapping} 路径保持原样,前端无需任何改动。
 * <p>
 * 迁移时唯一调整的一处:{@code sltdMydTotalByScore} 原本调用
 * {@code serviceSubtaskService.getServiceSubtasks(...)}(随访主干 Service,不能搬),
 * 现改为调用满意度模块自带的 {@link SatisfactionSubtaskService#getMydSubtaskRefs};
 * 查询条件与后续统计口径逐字保持一致(见各方法内注释)。
 * <p>
 * <b>开关</b>:本 Controller 始终注册(不随 {@code satisfaction.enabled} 消失)。
 * 模块被关闭时由 {@code SatisfactionAvailabilityInterceptor}
 * (见 {@code config/SatisfactionWebConfig})在进入方法前抛出 {@code ServiceException},
 * 调用方会拿到<b>明确的错误提示</b>(而不是 404)。
 * 注意:同一前缀 {@code /smartor/serviceSubtaskDetail} 下的<b>随访明细</b>端点不受影响
 * —— 拦截器只挂在本类的 5 个满意度路径上。
 */
@Api(description = "省立同德满意度统计")
@RestController
@RequestMapping("/smartor/serviceSubtaskDetail")
public class SltdSatisfactionController extends BaseController {
 
    /**
     * 站点口径策略解析器:综合满意度得分按站点(省立同德)取策略计算,
     * 不再把算法与常量写在 Controller 里。
     */
    @Autowired
    private SatisfactionScoreStrategyResolver satisfactionScoreStrategyResolver;
 
    @Autowired
    private SatisfactionSubtaskService satisfactionSubtaskService;
 
    // ================================================================ 端点(URL 原样)
 
    /**
     * 省立同德满意度数据统计(按题目统计选项数量/占比,并给出该题的综合得分)
     * <p>
     * 综合满意度 = (很满意项数×1 + 满意项数×0.8 + 一般项数×0.6 + 不满意项数×0.4 + 很不满意项数×0.2)
     * / (很满意项数 + 满意项数 + 一般项数 + 不满意项数 + 很不满意项数) × 100%;
     * 「未经历」「不适用」等不在五档里的选项既不进分子也不进分母。
     */
    @ApiOperation("省立同德满意度数据统计")
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @PostMapping("/sltdMydTotalByScore")
    public Map<String, Object> sltdMydTotalByScore(@RequestBody SltdMydTotalVO sltdMydTotalVO) {
        List<SatisfactionSubtaskRef> 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<SatisfactionSubtaskRef> refs = satisfactionSubtaskService.getMydSubtaskRefs(query);
                if (CollectionUtils.isNotEmpty(subtaskRefs)) {
                    subtaskRefs.addAll(refs);
                } else {
                    if (CollectionUtils.isEmpty(subtaskRefs)) subtaskRefs = refs;
                }
            }
        }
        // 按 questiontext 分组后转换为 QuestionResultVO
        List<QuestionResultDTO> result = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(subtaskRefs)) {
            List<ServiceSubtaskDetailRatioExport> detailList =
                    satisfactionSubtaskService.getDetailRatio(sltdMydTotalVO, subtaskRefs);
 
            if (detailList == null) {
                //serviceType 组合不在支持范围内(如同时含随访 2 和满意度 6/14),给明确提示而不是悄悄返回空数组
                return error("当前 serviceType 组合暂不支持统计问题占比,请分开查询(仅支持随访 2 或满意度 6/14)");
            }
            if (CollectionUtils.isNotEmpty(detailList)) {
                Map<String, List<ServiceSubtaskDetailRatioExport>> 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<String, Object> statQuestionOptionByKsOrBq(@RequestBody QuestionCountKsOrBqVO questionResultVO) {
        return success(satisfactionSubtaskService.statQuestionOptionByKsOrBq(questionResultVO));
    }
 
    @ApiOperation("省立同德患者满意度趋势")
    @PostMapping("/mydTrend")
    public Map<String, Object> mydTrend(@RequestBody MydTrendReqVO mydTrendReqVO) {
        return success(satisfactionSubtaskService.mydTrend(mydTrendReqVO));
    }
 
    @ApiOperation("省立同德患者满意度维护")
    @PostMapping("/sltdMydTotalByDimension")
    public Map<String, Object> sltdMydTotalByDimension(@RequestBody MydTrendReqVO mydTrendReqVO) {
        return success(satisfactionSubtaskService.sltdMydTotalByDimension(mydTrendReqVO));
    }
 
    @ApiOperation("门诊满意度综合得分排名(按科室或病区)")
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @PostMapping("/mzMydScoreRank")
    public Map<String, Object> mzMydScoreRank(@RequestBody MzMydScoreRankVO mzMydScoreRankVO) {
        return success(satisfactionSubtaskService.mzMydScoreRank(mzMydScoreRankVO));
    }
 
    // ================================================================ 综评计算(改走站点策略)
 
    /**
     * 计算一道题的满意度综合得分(百分制,保留两位小数)。
     * <p>
     * <b>策略化改造</b>:算法本身已原样搬到
     * {@link com.ruoyi.satisfaction.service.strategy.SltdSatisfactionScoreStrategy}
     * (省立同德口径),本方法只负责"按站点取策略 + 把 null 呈现为 0.00"。
     * 计算过程与返回值与迁移前<b>完全一致</b>。
     *
     * @param exportList 同一道题目的选项统计明细
     * @return 百分制综合得分,无有效项数时返回 "0.00"
     */
    private String calcMydScore(List<ServiceSubtaskDetailRatioExport> exportList) {
        BigDecimal score = satisfactionScoreStrategyResolver
                .resolve(SatisfactionSites.SLTD)
                .comprehensiveScore(exportList);
        return score == null ? "0.00" : score.toPlainString();
    }
}