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<String, Object> getPersonVoice(@RequestBody ServiceSubtaskDetail serviceSubtaskDetail) {
|
Map<String, Object> 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<ServiceSubtaskDetail> 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<Map<String, Object>> getServiceSubtaskDetails(@RequestBody ServiceSubtaskDetailVO serviceSubtaskDetailVO) {
|
List<Map<String, Object>> map = new ArrayList<>();
|
map = ServiceSubtaskDetailService.getServiceSubtaskDetails(serviceSubtaskDetailVO);
|
return map;
|
}
|
|
/**
|
* 获取随访详情-指标详情(超链接)
|
*/
|
@ApiOperation("获取随访详情-指标详情(超链接)")
|
@PostMapping("/getServiceSubtaskDetailsHyperlink")
|
public List<Map<String, Object>> 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<String, Object>> map = new ArrayList<>();
|
map = ServiceSubtaskDetailService.getServiceSubtaskDetails(serviceSubtaskDetailVO);
|
return map;
|
}
|
|
|
/**
|
* 省立同德满意度数据统计(按题目统计选项数量/占比,并给出该题的综合得分)
|
* <p>
|
* 综合满意度 = Σ(选项分值 × 项数) / Σ项数 / 本题最高分值 × 100%,
|
* 分值取 1、0.8、0.6、0.4、0.2 五档(很满意、满意、一般、不满意、很不满意);
|
* 「不适用」、0 分、空值等既不进分子也不进分母。
|
*/
|
@ApiOperation("省立同德满意度数据统计")
|
@AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
|
@PostMapping("/sltdMydTotalByScore")
|
public Map<String, Object> sltdMydTotalByScore(@RequestBody SltdMydTotalVO sltdMydTotalVO) {
|
List<ServiceSubtaskRes> 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<ServiceSubtask> serviceSubtasks = serviceSubtaskService.getServiceSubtasks(serviceSubtaskEntity);
|
List<ServiceSubtaskRes> serviceSubtaskRes = DtoConversionUtils.sourceToTarget(serviceSubtasks, ServiceSubtaskRes.class);
|
if (CollectionUtils.isNotEmpty(serviceSubtaskResList)) {
|
serviceSubtaskResList.addAll(serviceSubtaskRes);
|
} else {
|
if (CollectionUtils.isEmpty(serviceSubtaskResList)) serviceSubtaskResList = serviceSubtaskRes;
|
}
|
}
|
}
|
}
|
// 按 questiontext 分组后转换为 QuestionResultVO
|
List<QuestionResultDTO> result = new ArrayList<>();
|
if (CollectionUtils.isNotEmpty(serviceSubtaskResList)) {
|
List<ServiceSubtaskDetailRatioExport> detailList = serviceSubtaskService.getDetailRatio(sltdMydTotalVO, serviceSubtaskResList);
|
|
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);
|
//综合得分:Σ(分值×项数) / Σ项数 / 本题最高分值 × 100%,不适用/0分/空值不进分子也不进分母
|
questionResultDTO.setAvgScore(calcMydScore(exportList));
|
result.add(questionResultDTO);
|
});
|
}
|
}
|
|
return success(result);
|
}
|
|
@ApiOperation("省立同德满意度数据统计通过科室或病区")
|
@PostMapping("/statQuestionOptionByKsOrBq")
|
public Map<String, Object> statQuestionOptionByKsOrBq(@RequestBody QuestionCountKsOrBqVO questionResultVO) {
|
return success(serviceSubtaskService.statQuestionOptionByKsOrBq(questionResultVO));
|
}
|
|
@ApiOperation("省立同德患者满意度趋势")
|
@PostMapping("/mydTrend")
|
public Map<String, Object> mydTrend(@RequestBody MydTrendReqVO mydTrendReqVO) {
|
return success(serviceSubtaskService.mydTrend(mydTrendReqVO));
|
}
|
|
@ApiOperation("省立同德患者满意度维护")
|
@PostMapping("/sltdMydTotalByDimension")
|
public Map<String, Object> sltdMydTotalByDimension(@RequestBody MydTrendReqVO mydTrendReqVO) {
|
return success(serviceSubtaskService.sltdMydTotalByDimension(mydTrendReqVO));
|
}
|
|
@ApiOperation("门诊满意度综合得分排名(按科室或病区)")
|
@AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
|
@PostMapping("/mzMydScoreRank")
|
public Map<String, Object> mzMydScoreRank(@RequestBody MzMydScoreRankVO mzMydScoreRankVO) {
|
return success(serviceSubtaskService.mzMydScoreRank(mzMydScoreRankVO));
|
}
|
|
/**
|
* 多选题的问题类型
|
*/
|
private static final String VALUE_TYPE_MULTIPLE = "2";
|
|
/**
|
* 计算一道题的满意度综合得分(百分制,保留两位小数)
|
* <p>
|
* 综合满意度 = Σ(选项分值 × 项数) / Σ项数 / 本题最高分值 × 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<ServiceSubtaskDetailRatioExport> 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;
|
}
|
}
|
|
}
|