liusheng
8 小时以前 18f876d637cf1e9b0b264a4586e4a676a21f8166
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskServiceImpl.java
@@ -1599,6 +1599,61 @@
    }
    /**
     * 取逗号串里的第一个值(任务上科室/病区是多选,而 {@code service_subtask} 上都是单值列)。
     */
    private String firstValue(String value) {
        if (StringUtils.isEmpty(value)) {
            return null;
        }
        for (String item : value.split(",")) {
            if (StringUtils.isNotEmpty(item.trim())) {
                return item.trim();
            }
        }
        return null;
    }
    /**
     * 任务配置({@link ServiceTaskVO})→ 子任务({@link ServiceSubtask})的字段搬运。
     * <p>
     * <b>为什么不能直接 {@code DtoConversionUtils.sourceToTarget(serviceTaskVO, ServiceSubtask.class)}:</b>
     * 它是纯同名拷贝,而两边同名的 {@code deptcode / deptname} <b>语义不同</b>:
     * <ul>
     *   <li>{@code ServiceTaskVO.deptcode / deptname} = 任务的「适用科室」,
     *       出院随访(serviceType=2)/ 出院宣教(4)/ 门诊随访(3)等场景下就是<b>离院科室</b>;</li>
     *   <li>{@code ServiceSubtask.deptcode / deptname} = <b>在院科室</b>
     *       (2026-11 由原 sendlimitpbegin/pend 复用而来,见 {@code ServiceSubtask} 字段注释)。</li>
     * </ul>
     * 于是纯同名拷贝会把「离院科室」写进「在院科室」列,后果有两个:
     * <ol>
     *   <li>按在院维度筛({@code deptcodes} → {@code service_subtask.deptcode})会误命中这些子任务;</li>
     *   <li>按离院维度筛({@code leaveldeptcodes} → {@code service_subtask.leaveldeptcode})时,
     *       患者自己没带科室的那些(前端手动勾选患者很常见)会因为 {@code leaveldeptcode} 为空而查不到。</li>
     * </ol>
     * 因此这里按项目既定口径({@code doc/service_subtask_字段改名_改动清单.md} §8:这几条
     * 「长期任务 / 任务组 / 手动建任务」路径<b>不填在院维度</b>)统一处理:
     * <b>在院维度 4 列清空</b>(在院科室/在院病区只有 HIS 链路能从 {@code pat_med_inhosp} 拿到),
     * <b>科室 / 病区一律落离院维度</b>({@code leaveldeptcode/name}、{@code leavehospitaldistrictcode/name})。
     *
     * @param serviceTaskVO 建任务入参
     * @return 已按离院维度归位的子任务对象(在院维度为空)
     */
    private ServiceSubtask toSubtaskFromTaskVO(ServiceTaskVO serviceTaskVO) {
        ServiceSubtask serviceSubtask = DtoConversionUtils.sourceToTarget(serviceTaskVO, ServiceSubtask.class);
        // ① 在院维度必须清空:任务配置的科室不是患者本人的在院科室
        serviceSubtask.setDeptcode(null);
        serviceSubtask.setDeptname(null);
        serviceSubtask.setHospitaldistrictcode(null);
        serviceSubtask.setHospitaldistrictname(null);
        // ② 科室 / 病区 → 离院维度(多选串取第一个,子任务是单值列)
        serviceSubtask.setLeaveldeptcode(firstValue(serviceTaskVO.getDeptcode()));
        serviceSubtask.setLeaveldeptname(firstValue(serviceTaskVO.getDeptname()));
        serviceSubtask.setLeavehospitaldistrictcode(firstValue(serviceTaskVO.getLeavehospitaldistrictcode()));
        serviceSubtask.setLeavehospitaldistrictname(firstValue(serviceTaskVO.getLeavehospitaldistrictname()));
        return serviceSubtask;
    }
    /**
     * 单一任务
     *
     * @return 结果
@@ -1815,6 +1870,25 @@
                            serviceTaskdeptService.insertServiceTaskdept(serviceTaskdept);
                        }
                    }
                    // 点位行(2026-11,appltype=7):科室 / 病区 / 点位在 service_taskdept 里是
                    // **各自独立的行**,可以共存也可以不共存 —— 所以点位行是"额外追加",
                    // 不替换上面的科室/病区行。点位行的 dept_code/dept_name 留空,编码只落 point_location。
                    if ("7".equals(serviceTaskVO.getAppltype())) {
                        String pointLocation = firstNonBlank(serviceTaskVO.getPointLocation(),
                                serviceTask.getPointLocation());
                        if (StringUtils.isNotEmpty(pointLocation)) {
                            ServiceTaskdept pointDept = new ServiceTaskdept();
                            pointDept.setTaskId(serviceTask.getTaskid());
                            pointDept.setTaskName(serviceTask.getTaskName());
                            pointDept.setOrgid(serviceTask.getOrgid());
                            pointDept.setLongtask(serviceTask.getLongTask() == null ? null
                                    : Long.valueOf(serviceTask.getLongTask()));
                            pointDept.setServiceType(serviceTaskVO.getServiceType());
                            pointDept.setDeptType("3");
                            pointDept.setPointLocation(pointLocation);
                            serviceTaskdeptService.insertServiceTaskdept(pointDept);
                        }
                    }
                }
            }
//长期任务,也允许加人了
@@ -1825,7 +1899,8 @@
//            }
            //将任务信息放到服务表中
            ServiceSubtask serviceSubtask = DtoConversionUtils.sourceToTarget(serviceTaskVO, ServiceSubtask.class);
            // 科室/病区按「离院维度」归位(出院随访的科室病区就是离院科室/病区),见 toSubtaskFromTaskVO
            ServiceSubtask serviceSubtask = toSubtaskFromTaskVO(serviceTaskVO);
            serviceSubtask.setTaskid(serviceTask.getTaskid().longValue());
            serviceSubtask.setTemplatename(ObjectUtils.isNotEmpty(tempid) ? tempName : serviceTask.getTemplatename());
            serviceSubtask.setTemplateid(ObjectUtils.isNotEmpty(tempid) ? tempid : serviceTask.getTemplateid());
@@ -1846,15 +1921,18 @@
                    serviceSubtask.setInhospid(patTaskRelevance.getHospid());
                    //新建完成就是待发送状态
                    serviceSubtask.setSendstate(2L);
                    String subtaskDeptCode = patTaskRelevance.getDeptcode();
                    if (StringUtils.isEmpty(subtaskDeptCode) && StringUtils.isNotEmpty(serviceTaskVO.getDeptcode())) {
                        // service_subtask.leaveldeptcode 为单值字段,不能写入任务的多科室拼接串,取第一个科室兜底
                        subtaskDeptCode = serviceTaskVO.getDeptcode().split(",")[0].trim();
                    }
                    serviceSubtask.setLeaveldeptcode(subtaskDeptCode);
                    serviceSubtask.setLeaveldeptname(StringUtils.isNotEmpty(patTaskRelevance.getDeptName()) ? patTaskRelevance.getDeptName() : serviceTaskVO.getDeptname());
                    serviceSubtask.setLeavehospitaldistrictcode(patTaskRelevance.getLeavehospitaldistrictcode());
                    serviceSubtask.setLeavehospitaldistrictname(patTaskRelevance.getLeavehospitaldistrictname());
                    // 离院维度(出院随访 = 出院科室 / 出院病区):
                    // 患者自带的出院科室/病区优先,患者侧没带(前端手动勾选很常见)时回退到任务配置,
                    // 不能无条件覆盖 —— 否则会把 toSubtaskFromTaskVO 按任务配置兜好的离院维度抹成 null,
                    // 结果「任务配了科室病区、生成出来的子任务离院科室/病区却是空」。
                    serviceSubtask.setLeaveldeptcode(firstNonBlank(patTaskRelevance.getDeptcode(),
                            firstValue(serviceTaskVO.getDeptcode())));
                    serviceSubtask.setLeaveldeptname(firstNonBlank(patTaskRelevance.getDeptName(),
                            firstValue(serviceTaskVO.getDeptname())));
                    serviceSubtask.setLeavehospitaldistrictcode(firstNonBlank(patTaskRelevance.getLeavehospitaldistrictcode(),
                            firstValue(serviceTaskVO.getLeavehospitaldistrictcode())));
                    serviceSubtask.setLeavehospitaldistrictname(firstNonBlank(patTaskRelevance.getLeavehospitaldistrictname(),
                            firstValue(serviceTaskVO.getLeavehospitaldistrictname())));
                    serviceSubtask.setType(serviceTaskVO.getType());
                    serviceSubtask.setHospType(patTaskRelevance.getHospType());
                    serviceSubtask.setOpenid(patTaskRelevance.getOpenid());
@@ -2190,6 +2268,27 @@
                    scheduleVO.setOplevelCodes(serviceTaskVO.getOplevelcode());
                    scheduleVO.setCreateBy(serviceTask.getUpdateBy());
                    serviceTaskScheduleService.saveScheduleWithDetail(scheduleVO);
                } else if (serviceTaskVO.getAppltype().equals("7")) {
                    // 按点位(同德需求):先软删该任务旧的 dept_type='3' 行,再按入参重建
                    //   (与科室/病区「先删后插」同口径;点位行 dept_code/dept_name 留空)
                    ServiceTaskdept oldPoint = new ServiceTaskdept();
                    oldPoint.setTaskId(serviceTask.getTaskid());
                    oldPoint.setDeptType("3");
                    serviceTaskdeptService.deleteServiceTaskdeptByTaskId(oldPoint);
                    String pointLocation = StringUtils.isNotEmpty(serviceTaskVO.getPointLocation())
                            ? serviceTaskVO.getPointLocation() : serviceTask.getPointLocation();
                    if (StringUtils.isNotEmpty(pointLocation)) {
                        ServiceTaskdept pointDept = new ServiceTaskdept();
                        pointDept.setTaskId(serviceTask.getTaskid());
                        pointDept.setTaskName(serviceTask.getTaskName());
                        pointDept.setOrgid(serviceTaskVO.getOrgid());
                        pointDept.setLongtask(serviceTask.getLongTask() == null ? null
                                : Long.valueOf(serviceTask.getLongTask()));
                        pointDept.setServiceType(serviceTaskVO.getServiceType());
                        pointDept.setDeptType("3");
                        pointDept.setPointLocation(pointLocation);
                        serviceTaskdeptService.insertServiceTaskdept(pointDept);
                    }
                }
            }
            if (ObjectUtils.isNotEmpty(serviceTaskVO.getIvrTaskTemplateVO())) {
@@ -2212,7 +2311,8 @@
            }
            if (CollectionUtils.isNotEmpty(serviceTaskVO.getPatTaskRelevances())) {
                for (PatTaskRelevance patTaskRelevance : serviceTaskVO.getPatTaskRelevances()) {
                    ServiceSubtask serviceSubtask = DtoConversionUtils.sourceToTarget(serviceTaskVO, ServiceSubtask.class);
                    // 科室/病区按「离院维度」归位(出院随访的科室病区就是离院科室/病区),见 toSubtaskFromTaskVO
                    ServiceSubtask serviceSubtask = toSubtaskFromTaskVO(serviceTaskVO);
                    serviceSubtask.setLibtemplateid(ObjectUtils.isEmpty(serviceTaskVO.getLibtemplateid()) ? null : Long.valueOf(serviceTaskVO.getLibtemplateid()));
                    serviceSubtask.setLibtemplatename(serviceTaskVO.getLibtemplatename());
                    serviceSubtask.setTemplateid(serviceTask.getTemplateid());
@@ -2224,10 +2324,16 @@
                    serviceSubtask.setAddr(patTaskRelevance.getAddr());
                    serviceSubtask.setPatid(patTaskRelevance.getPatid());
                    serviceSubtask.setOpenid(patTaskRelevance.getOpenid());
                    serviceSubtask.setLeaveldeptcode(patTaskRelevance.getDeptcode());
                    serviceSubtask.setLeavehospitaldistrictname(patTaskRelevance.getLeavehospitaldistrictname());
                    serviceSubtask.setLeavehospitaldistrictcode(patTaskRelevance.getLeavehospitaldistrictcode());
                    serviceSubtask.setLeaveldeptname(patTaskRelevance.getDeptName());
                    // 离院维度:患者自带的出院科室/病区优先,没带时回退到任务配置(否则会把
                    // toSubtaskFromTaskVO 按任务配置兜好的值抹成 null,出院随访就成「离院科室/病区为空」)
                    serviceSubtask.setLeaveldeptcode(firstNonBlank(patTaskRelevance.getDeptcode(),
                            firstValue(serviceTaskVO.getDeptcode())));
                    serviceSubtask.setLeaveldeptname(firstNonBlank(patTaskRelevance.getDeptName(),
                            firstValue(serviceTaskVO.getDeptname())));
                    serviceSubtask.setLeavehospitaldistrictcode(firstNonBlank(patTaskRelevance.getLeavehospitaldistrictcode(),
                            firstValue(serviceTaskVO.getLeavehospitaldistrictcode())));
                    serviceSubtask.setLeavehospitaldistrictname(firstNonBlank(patTaskRelevance.getLeavehospitaldistrictname(),
                            firstValue(serviceTaskVO.getLeavehospitaldistrictname())));
                    serviceSubtask.setType(serviceTaskVO.getType());
                    serviceSubtask.setCreateTime(DateUtils.getNowDate());
                    serviceSubtask.setDrname(patTaskRelevance.getDrname());
@@ -5390,16 +5496,46 @@
        String ip = localIP;
        ServiceTask serviceTask = new ServiceTask();
        serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceTaskVo.getTaskid());
        if (ObjectUtils.isEmpty(serviceTask)) {
            // 任务不存在 → 不可能有 data,直接抛业务异常(接口层会转成 code=500)
            throw new BaseException("扫码生成任务失败:任务不存在或已删除(taskid=" + serviceTaskVo.getTaskid() + ")");
        }
        if (ObjectUtils.isNotEmpty(serviceTask)) {
            try {
                ServiceTaskdept serviceTaskdept = new ServiceTaskdept();
                if (ObjectUtils.isNotEmpty(serviceTaskVo.getDeptcode())) {
                    serviceTaskdept.setDeptType("1");
                    serviceTaskdept.setDeptCode(serviceTaskVo.getDeptcode());
                }
                if (ObjectUtils.isNotEmpty(serviceTaskVo.getLeavehospitaldistrictcode())) {
                // 维度(科室/病区/点位)由任务的 appltype 决定:1=科室、2=病区、7=点位。
                //   三种维度在 service_taskdept 里是**各自独立的行**(各自的列),可以共存、也可以不共存;
                //   但查行时必须只带一个维度 —— 否则会用 A 维度的编码去撞 B 维度的行,
                //   而点位行的 dept_code 本来就是空的。
                String appltype = serviceTask.getAppltype();
                if ("7".equals(appltype)) {
                    serviceTaskdept.setDeptType("3");
                    serviceTaskdept.setPointLocation(firstNonBlank(serviceTaskVo.getPointLocation(),
                            serviceTask.getPointLocation()));
                } else if ("2".equals(appltype)) {
                    serviceTaskdept.setDeptType("2");
                    serviceTaskdept.setDeptCode(serviceTaskVo.getLeavehospitaldistrictcode());
                    // 任务上的科室/病区可能是逗号多选,查 service_taskdept 要用单值(取第一个)
                    serviceTaskdept.setDeptCode(firstNonBlank(serviceTaskVo.getLeavehospitaldistrictcode(),
                            firstValue(serviceTask.getLeavehospitaldistrictcode())));
                } else if ("1".equals(appltype)) {
                    serviceTaskdept.setDeptType("1");
                    serviceTaskdept.setDeptCode(firstNonBlank(serviceTaskVo.getDeptcode(),
                            firstValue(serviceTask.getDeptcode())));
                } else {
                    // appltype 为空的历史任务:沿用改造前的口径(科室 → 病区,后写的覆盖先写的),
                    // 点位作为第三维,只在传了点位时补上。
                    if (ObjectUtils.isNotEmpty(serviceTaskVo.getDeptcode())) {
                        serviceTaskdept.setDeptType("1");
                        serviceTaskdept.setDeptCode(serviceTaskVo.getDeptcode());
                    }
                    if (ObjectUtils.isNotEmpty(serviceTaskVo.getLeavehospitaldistrictcode())) {
                        serviceTaskdept.setDeptType("2");
                        serviceTaskdept.setDeptCode(serviceTaskVo.getLeavehospitaldistrictcode());
                    }
                    if (ObjectUtils.isNotEmpty(serviceTaskVo.getPointLocation())) {
                        serviceTaskdept.setDeptType("3");
                        serviceTaskdept.setPointLocation(serviceTaskVo.getPointLocation());
                    }
                }
                serviceTaskdept.setServiceType(serviceTask.getServiceType());
                serviceTaskdept.setOrgid(serviceTask.getOrgid());
@@ -5436,10 +5572,21 @@
                        iServiceOutPathService.updateServiceOutPath(serviceOutPath);
                    }
                } else {
                    // 配置缺失属「业务可预期」情况:带上排查信息抛出去(不吞),
                    // 接口层据此返回 code=500 + 明确 msg,避免「200 + 空 data」。
                    throw new BaseException("扫码生成任务失败:未匹配到任务维度配置(taskid=" + serviceTaskVo.getTaskid()
                            + ",appltype=" + appltype
                            + ",deptType=" + serviceTaskdept.getDeptType()
                            + ",deptCode=" + serviceTaskdept.getDeptCode()
                            + ",pointLocation=" + serviceTaskdept.getPointLocation() + ")");
                }
            } catch (BaseException be) {
                // ⚠️ 必须排在 catch (Exception) 之前:BaseException 是 RuntimeException,
                //    否则刚抛出的业务异常会被下面整段吞掉,接口又变成 200 + 空 data。
                throw be;
            } catch (Exception e) {
                e.printStackTrace();
                log.error("【扫码生成任务】处理异常:{}", e.getMessage());
                log.error("【扫码生成任务】处理异常:taskid={},{}", serviceTaskVo.getTaskid(), e.getMessage(), e);
            }
        }
        return content;
@@ -5457,6 +5604,9 @@
        if (StringUtils.isNotEmpty(serviceTaskdept.getDeptType()) && serviceTaskdept.getDeptType().equals("1")) {
            serviceSubtask.setLeaveldeptcode(serviceTaskdept.getDeptCode());
            serviceSubtask.setLeaveldeptname(serviceTaskdept.getDeptName());
        } else if (StringUtils.isNotEmpty(serviceTaskdept.getDeptType()) && serviceTaskdept.getDeptType().equals("3")) {
            // 点位行(dept_type='3'):dept_code/dept_name 为空,点位只落 point_location
            serviceSubtask.setPointLocation(serviceTaskdept.getPointLocation());
        } else {
            serviceSubtask.setLeavehospitaldistrictcode(serviceTaskdept.getDeptCode());
            serviceSubtask.setLeavehospitaldistrictname(serviceTaskdept.getDeptName());