liusheng
昨天 18f876d637cf1e9b0b264a4586e4a676a21f8166
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/support/VisitSubtaskFactory.java
@@ -49,12 +49,25 @@
 * 实际实现时把它收窄为"纯组装 + 纯工具",因为 {@code writeInSubTask}(232 行)里有大量
 * 写库与去重编排,混在一起会让本类的 A 档逐行等价探针失去意义。
 *
 * <h3>⚠️ 唯一的"非搬运"行:{@code hospno}</h3>
 * {@link #boxedServiceSubtask} 里多了一句 {@code d.setHospno(inhosp.getInhospno())}。
 * 原实现确实<b>没有</b>这一句,所以出院链路的 {@code service_subtask.hospno} 一直落空;
 * 而 {@code ServiceSubtask.hospno} 的语义就是"就诊编号(对应的 outhospno、inhospno)"。
 * 这是<b>有意补齐</b>,不是抄漏 —— 反向探针({@code verify_inhospvisit_boxed_fields.py})
 * 只校验"原 setter 是否都有对应调用",不会因此报错。
 * <h3>⚠️ 两处有意的"非搬运"行</h3>
 * <ol>
 *   <li><b>{@code hospno}</b>:{@link #boxedServiceSubtask} 里多了一句
 *       {@code d.setHospno(inhosp.getInhospno())}。原实现确实<b>没有</b>这一句,
 *       所以出院链路的 {@code service_subtask.hospno} 一直落空;
 *       而 {@code ServiceSubtask.hospno} 的语义就是"就诊编号(对应的 outhospno、inhospno)"。
 *       这是<b>有意补齐</b>,不是抄漏 —— 反向探针({@code verify_inhospvisit_boxed_fields.py})
 *       只校验"原 setter 是否都有对应调用",不会因此报错。</li>
 *   <li><b>campusid 不再被空值覆盖</b>(2026-10-29 修):原实现无条件
 *       {@code d.setCampusid(inhosp.getCampusid())},会把从长期任务搬来的院区抹成 {@code null}。
 *       后果是任务落库 {@code campusid=NULL},而随访列表接口
 *       ({@code ServiceSubtaskController#patItem})强制用登录用户的院区过滤
 *       ({@code and campusid = #{campusid}}),NULL 不匹配任何值 →
 *       <b>任务存在、页面永远查不到</b>。实测南华附一 2026-07 有 894 条这样"生成了但不显示"的
 *       出院随访任务(其中骨科中心一病区 25 条)。现改为"出院记录有院区才覆盖"。
 *       ⚠️ 这是<b>行为变更</b>(不是等价搬迁),触发条件只有"出院记录院区为空"这一种,
 *       其余情况仍与原实现完全一致。历史数据回填脚本:
 *       {@code sql/fix_nhfy_出院随访任务院区病区回填_20261029.sql}。</li>
 * </ol>
 */
@Slf4j
@Component
@@ -119,14 +132,34 @@
        d.setHospno(inhosp.getInhospno());
        d.setDrcode(inhosp.getDrcode());
        d.setDrname(inhosp.getDrname());
        d.setDeptcode(inhosp.getLeaveldeptcode());
        d.setDeptname(inhosp.getLeaveldeptname());
        d.setLeaveldeptcode(inhosp.getLeaveldeptcode());
        d.setLeaveldeptname(inhosp.getLeaveldeptname());
        d.setLeavehospitaldistrictcode(inhosp.getLeavehospitaldistrictcode());
        d.setLeavehospitaldistrictname(inhosp.getLeavehospitaldistrictname());
        // ---- 2026-11 新增:在院维度落「复用列」 ----
        //   背景:service_subtask 的 deptcode/deptname 改名 leaveldeptcode/leaveldeptname,
        //         原 sendlimitabegin/aend、sendlimitpbegin/pend 四个空列复用为
        //         hospitaldistrictcode/name(在院病区)、deptcode/name(在院科室)。
        //   口径:出院维度仍按原写法落 leavel*(下面 type 4/5 的覆盖也保持原样,落库位置不变),
        //         这里只是把 pat_med_inhosp 的「在院科室 / 在院病区」额外存一份,便于按在院维度统计。
        d.setDeptcode(inhosp.getDeptcode());
        d.setDeptname(inhosp.getDeptname());
        d.setHospitaldistrictcode(inhosp.getHospitaldistrictcode());
        d.setHospitaldistrictname(inhosp.getHospitaldistrictname());
        // ---- 2026-11 新增:重要性标记(含「日间」)原样带走 ----
        //   背景:判断日间就看 pat_med_inhosp.importance_flag_code(现场库存中文描述
        //         「日间手术」「日间诊疗」,也有库按 ImportFlagEnum 存 FH0165.01/02)。
        //   子任务落一份,随访列表就能直接按「日间」筛,不用每行回查住院登记表。
        //   口径:原样拷贝、不做值域归一;不随 type 4/5 改写(与科室/病区维度无关)。
        d.setImportanceFlagCode(inhosp.getImportanceFlagCode());
        // ---- 2026-11 新增:点位编码原样带走 ----
        //   来源是【任务】而不是住院登记表:service_task.point_location 建任务时由前端填,
        //   子任务落一份快照,之后任务改点位不回写已有子任务(与 importanceFlagCode 同口径)。
        d.setPointLocation(serviceTask.getPointLocation());
        // ---- 原 L1128-1134:type 4/5 改用入院维度 ----
        if (type != null && (type == 4 || type == 5)) {
            d.setDeptcode(inhosp.getDeptcode());
            d.setDeptname(inhosp.getDeptname());
            d.setLeaveldeptcode(inhosp.getDeptcode());
            d.setLeaveldeptname(inhosp.getDeptname());
            d.setLeavehospitaldistrictcode(inhosp.getHospitaldistrictcode());
            d.setLeavehospitaldistrictname(inhosp.getHospitaldistrictname());
        }
@@ -147,7 +180,17 @@
        d.setAge(archive.getAge());
        // ---- 原 L1147-1163 ----
        d.setSendstate(2L);
        d.setCampusid(inhosp.getCampusid());
        // ⚠️ 第二处【有意偏离原实现】(2026-10-29,见类注释"campusid 不再被空值覆盖"):
        //   原实现无条件 d.setCampusid(inhosp.getCampusid()),会把上面从长期任务搬来的院区抹成 null。
        //   线上后果:任务落库 campusid=NULL →
        //   ServiceSubtaskController#patItem 强制 setCampusid(登录用户院区),
        //   SQL 条件 `and campusid = #{campusid}` → 该任务对任何人都不显示("生成了但查不到")。
        //   实测南华附一 2026-07:894 条出院随访任务因此在页面上不可见
        //   (其中骨科中心一病区 25 条,正是"出院 475 条 / 随访 444 条"的来源)。
        //   这里改为"出院记录有院区才覆盖",出院记录取不到时保留任务自带的院区。
        if (StringUtils.isNotEmpty(inhosp.getCampusid())) {
            d.setCampusid(inhosp.getCampusid());
        }
        d.setManagementDoctor(inhosp.getManagementDoctor());
        d.setManagementDoctorCode(inhosp.getManagementDoctorCode());
        d.setStarttime(inhosp.getStarttime());
@@ -253,6 +296,9 @@
        // ---- 原 L1226:拷贝 ----
        applyCarriedOverFieldsFromTask(d, serviceTask);
        // ---- 2026-11 新增:点位编码原样带走(来源是任务,不是住院登记表) ----
        d.setPointLocation(serviceTask.getPointLocation());
        // ---- 原 L1227-1231 ----
        d.setTaskid(serviceTask.getTaskid());
        d.setOrgid(serviceTask.getOrgid());
@@ -266,10 +312,17 @@
        d.setInhospid(inhosp.getInhospid());
        d.setDrcode(inhosp.getDrcode());
        d.setDrname(inhosp.getDrname());
        d.setDeptcode(inhosp.getLeaveldeptcode());
        d.setDeptname(inhosp.getLeaveldeptname());
        d.setLeaveldeptcode(inhosp.getLeaveldeptcode());
        d.setLeaveldeptname(inhosp.getLeaveldeptname());
        // 2026-11:入院/在院维度落「复用列」(口径同 boxedServiceSubtask)
        d.setDeptcode(inhosp.getDeptcode());
        d.setDeptname(inhosp.getDeptname());
        d.setHospitaldistrictcode(inhosp.getHospitaldistrictcode());
        d.setHospitaldistrictname(inhosp.getHospitaldistrictname());
        d.setLeavehospitaldistrictcode(inhosp.getLeavehospitaldistrictcode());
        d.setLeavehospitaldistrictname(inhosp.getLeavehospitaldistrictname());
        // 2026-11:重要性标记(含「日间」)原样带走(口径同 boxedServiceSubtask)
        d.setImportanceFlagCode(inhosp.getImportanceFlagCode());
        // ---- 原 L1242-1251 ----
        d.setTemplateid(serviceTask.getTemplateid());
        d.setTemplatename(serviceTask.getTemplatename());