已修改12个文件
194 ■■■■■ 文件已修改
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskController.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeUtils.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PatMaternityArchive.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/VO/ServiceSubtaskVO.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/entity/ServiceSubtaskEntity.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/mapper/ServiceSubtaskMapper.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IServiceSubtaskService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/ServiceTaskMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskController.java
@@ -302,8 +302,7 @@
    public AjaxResult querySubtaskList(@RequestBody ServiceSubtaskVO serviceSubtaskVO) {
        ServiceSubtaskEntity serviceSubtaskEntity = DtoConversionUtils.sourceToTarget(serviceSubtaskVO, ServiceSubtaskEntity.class);
        serviceSubtaskEntity.setContinueContent(ObjectUtils.isNotEmpty(serviceSubtaskVO.getContinueContent()) ? serviceSubtaskVO.getContinueContent().toString() : null);
        PageUtils.startPageByPost(serviceSubtaskEntity.getPageNum(), serviceSubtaskEntity.getPageSize());
        serviceSubtaskService.selectServiceSubtaskList(serviceSubtaskEntity);
        // 该 SQL 已内置 limit ${pageSize} OFFSET ${pageNum},不再走 PageHelper,避免两套分页叠加产生两个 LIMIT
        return success(serviceSubtaskService.selectServiceSubtaskList(serviceSubtaskEntity));
    }
@@ -317,8 +316,8 @@
    public TableDataInfo querySubtaskListAgain(@RequestBody ServiceSubtaskVO serviceSubtaskVO) {
        ServiceSubtaskEntity serviceSubtaskEntity = DtoConversionUtils.sourceToTarget(serviceSubtaskVO, ServiceSubtaskEntity.class);
        serviceSubtaskEntity.setContinueContent(ObjectUtils.isNotEmpty(serviceSubtaskVO.getContinueContent()) ? serviceSubtaskVO.getContinueContent().toString() : null);
        // 该 SQL 未内置 limit,使用 PageHelper 分页;只调用一次,getDataTable 可基于 Page 取到正确 total
        PageUtils.startPageByPost(serviceSubtaskEntity.getPageNum(), serviceSubtaskEntity.getPageSize());
        serviceSubtaskService.selectServiceSubtaskListAgain(serviceSubtaskEntity);
        return getDataTable(serviceSubtaskService.selectServiceSubtaskListAgain(serviceSubtaskEntity));
    }
@@ -833,17 +832,20 @@
    }
    /**
     * 统计任务各种状态的数量(优化版)
     * 统计任务各种状态的数量(优化版)-【随访工作台】
     *
     * @param soonTimeoutHours 即将超时时间(小时),默认 48
     * @return Map<String, Object> 包含各状态统计数据
     * pendingVisitCount: 当前登录人截止今日待随访总量
     * failedVisitCount: 当前登录人随访任务失败总量
     * abnormalVisitVount: 当前登录人随访任务异常总量
     * allVisitCount: 当前登录人随访任务总量
     * soonTimeoutCount: 当前登录人即将超时数量(超时截止时间在 soonTimeoutHours 小时内)
     */
    @ApiOperation("获取当前用户随访数量统计")
    @PostMapping("/getCurrentUserServiceSubtaskCount")
    public Map<String, Object> getCurrentUserServiceSubtaskCount() {
    public Map<String, Object> getCurrentUserServiceSubtaskCount(
            @RequestParam(value = "soonTimeoutHours", required = false, defaultValue = "48") Integer soonTimeoutHours) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (loginUser == null || loginUser.getUser() == null) {
            return new HashMap<>();
@@ -863,6 +865,7 @@
        entity.setOrgid(sysUser.getOrgid());
        entity.setLeaveldeptcodes(deptCodes);
        entity.setLeavehospitaldistrictcodes(wardCodes);
        entity.setSoonTimeoutHours(soonTimeoutHours);
        // admin 查看全院;非 admin 只要配置了科室或病区任一维度即可查询(SQL 中为 OR 关系)
        boolean isAdmin = "admin".equals(sysUser.getUserName());
        if (isAdmin || CollectionUtils.isNotEmpty(deptCodes) || CollectionUtils.isNotEmpty(wardCodes)) {
@@ -875,6 +878,7 @@
        empty.put("abnormalVisitCount", 0);
        empty.put("warnningVisitCount", 0);
        empty.put("allVisitCount", 0);
        empty.put("soonTimeoutCount", 0);
        return empty;
    }
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java
@@ -33,7 +33,7 @@
    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd[ HH:mm:ss]", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;
    /**
@@ -44,7 +44,7 @@
    /**
     * 更新时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd[ HH:mm:ss]", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date updateTime;
    /**
ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeUtils.java
@@ -76,13 +76,41 @@
     * 根据 SysDept.parentId 构造部门树
     */
    public static List<SysDept> buildDeptTree(List<SysDept> depts) {
        // 按 deptId 去重,防止 sys_user_dept 一对多关联产生重复的父节点,导致构建部门树时出现问题
        depts = distinctByDeptId(depts);
        return buildTree(depts, SysDept::getDeptId, SysDept::getParentId, SysDept::setChildren);
    }
    /**
     * 一次性获取所有部门叶子节点
     * 从扁平部门列表一次性获取所有叶子部门:即「deptId 不出现在任何节点的 parentId 中」的节点。
     * 入参应为扁平列表(不是 buildDeptTree 的返回值),无需先建树。
     * 会先按 deptId 去重,避免 join 产生的重复部门变成重复叶子。
     */
    public static List<SysDept> getLeafDepts(List<SysDept> depts) {
        return getLeafNodes(depts, SysDept::getHisDeptId, SysDept::getHisParentId);
        return getLeafNodes(distinctByDeptId(depts), SysDept::getDeptId, SysDept::getParentId);
    }
    /**
     * 按 deptId 对部门列表去重,保留首次出现的节点。
     * 查询结果通过 dept_code 关联 sys_user_dept,同一部门可能出现多条记录(重复的父节点),
     * 构建部门树前需去重,避免重复节点导致树结构异常。
     *
     * @param list 部门列表,允许为 null
     * @return 去重后的部门列表;入参为 null 或空时原样返回
     */
    private static List<SysDept> distinctByDeptId(List<SysDept> list) {
        if (list == null || list.isEmpty()) {
            return list;
        }
        Set<Long> deptIdSet = new HashSet<>();
        List<SysDept> distinctList = new ArrayList<>(list.size());
        for (SysDept dept : list) {
            Long deptId = dept.getDeptId();
            // deptId 为空时保留,避免误合并不同部门
            if (deptId == null || deptIdSet.add(deptId)) {
                distinctList.add(dept);
            }
        }
        return distinctList;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
@@ -335,8 +335,7 @@
    public List<SysDept> selectLeafSysDeptListBySysUserDept(SysUserDept sysUserDept){
        List<SysDept> list =  deptMapper.selectSysDeptListBySysUserDept(sysUserDept);
        if(CollectionUtils.isNotEmpty(list)){
            List<SysDept> tree = TreeUtils.buildDeptTree(list);
            list = TreeUtils.getLeafDepts(tree);
            list = TreeUtils.getLeafDepts(list);
        }
        return list;
    }
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -982,8 +982,7 @@
    public List<SysUserDept> selectLeafSysDeptList(SysUserDept sysUserDept){
        List<SysDept> list =  sysDeptMapper.selectSysDeptListBySysUserDept(sysUserDept);
        if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(list)){
            List<SysDept> tree = TreeUtils.buildDeptTree(list);
            list = TreeUtils.getLeafDepts(tree);
            list = TreeUtils.getLeafDepts(list);
        }
        List<SysUserDept> list1 = DtoConversionUtils.sourceToTarget(list, SysUserDept.class);
        return list1;
@@ -993,8 +992,7 @@
        SysUserDept sysUserDept = DtoConversionUtils.sourceToTarget(sysDept, SysUserDept.class);
        List<SysDept> list =  sysDeptMapper.selectSysDeptListBySysUserDept(sysUserDept);
        if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(list)){
            List<SysDept> tree = TreeUtils.buildDeptTree(list);
            list = TreeUtils.getLeafDepts(tree);
            list = TreeUtils.getLeafDepts(list);
        }
        return list;
    }
smartor/src/main/java/com/smartor/domain/PatMaternityArchive.java
@@ -60,7 +60,7 @@
    private String parentname;
    /** 出生日期 */
    @JsonFormat(pattern = "yyyy-MM-dd[ HH:mm:ss]", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @Excel(name = "出生日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty(value = "出生日期")
    private Date birthdate;
@@ -76,19 +76,19 @@
    private String phone;
    /** 诊断时间 */
    @JsonFormat(pattern = "yyyy-MM-dd[ HH:mm:ss]", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @Excel(name = "诊断时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty(value = "诊断时间")
    private Date diagTime;
    /** 建档时间 */
    @JsonFormat(pattern = "yyyy-MM-dd[ HH:mm:ss]", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @Excel(name = "建档时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty(value = "建档时间")
    private Date archiveTime;
    /** 拟随访时间 */
    @JsonFormat(pattern = "yyyy-MM-dd[ HH:mm:ss]", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @Excel(name = "拟随访时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty(value = "拟随访时间")
    private Date plannedFollowTime;
smartor/src/main/java/com/smartor/domain/VO/ServiceSubtaskVO.java
@@ -806,6 +806,18 @@
    @ApiModelProperty(value = "页面展示状态 :1:待随访(2 待发送);2:随访中(3 已发送、1 被领取)、3:未完成(5 发送失败、7、超时)、4:已完成( 6 已完成)、5:无需随访(4 不执行)")
    private Long sendstateView;
    /**
     * 是否即将超时:1 是(配合 soonTimeoutHours 使用,用于查询即将超时列表)
     */
    @ApiModelProperty(value = "是否即将超时:1是")
    private Integer soonTimeout;
    /**
     * 即将超时时间(小时):截止时间在 N 小时内
     */
    @ApiModelProperty(value = "即将超时时间(小时)")
    private Integer soonTimeoutHours;
//    /**
//     * 日期限制 目前由前端控制
//     */
smartor/src/main/java/com/smartor/domain/entity/ServiceSubtaskEntity.java
@@ -114,6 +114,18 @@
    private Integer rateDay;
    /**
     * 是否即将超时:1 是(配合 soonTimeoutHours 使用)
     */
    @ApiModelProperty(value = "是否即将超时:1是")
    private Integer soonTimeout;
    /**
     * 即将超时时间(小时):统计距超时截止时间在 N 小时内的待随访数量
     */
    @ApiModelProperty(value = "即将超时时间(小时)")
    private Integer soonTimeoutHours;
    /**
     * 性别
     */
    @Excel(name = "  性别")
smartor/src/main/java/com/smartor/mapper/ServiceSubtaskMapper.java
@@ -38,6 +38,9 @@
    /**
     * 查询单一任务(随访)列表
     * <p>
     * 入参 soonTimeout=1 且 soonTimeoutHours=N 时,仅返回“尚未超时、且将在 N 小时内超时”的数据,
     * 截止时间口径与 getCurrentUserServiceSubtaskCount 的 soonTimeoutCount 一致。
     *
     * @param serviceSubtaskVO 单一任务(随访)
     * @return 单一任务(随访)集合
@@ -227,6 +230,7 @@
     * failedVisitCount: 当前登录人随访任务失败总量
     * abnormalVisitVount: 当前登录人随访任务异常总量
     * allVisitCount: 当前登录人随访任务总量
     * soonTimeoutCount: 当前登录人即将超时数量(超时截止时间在 soonTimeoutHours 小时内,仅统计尚未超时的数据)
     */
    public Map<String, Object> getCurrentUserServiceSubtaskCount(ServiceSubtaskEntity serviceSubtaskVO);
smartor/src/main/java/com/smartor/service/IServiceSubtaskService.java
@@ -196,6 +196,13 @@
    public List<ServiceSubtaskDiagname> convertToDiagnameList(List<ServiceSubtask> subtaskList);
    /**
     * 统计任务各种状态的数量 -【随访工作台】
     *
     * @param entity 查询条件(科室/病区权限、soonTimeoutHours 即将超时小时数等)
     * @return 含 pendingVisitCount、failedVisitCount、abnormalVisitCount、warnningVisitCount、
     * allVisitCount、soonTimeoutCount 的统计结果
     */
    public Map<String, Object> getCurrentUserServiceSubtaskCount(ServiceSubtaskEntity entity);
    /**
smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml
@@ -291,6 +291,36 @@
        <if test="continueTimeNext != null ">and continue_time_next = #{continueTimeNext}</if>
    </select>
    <!-- 即将超时过滤:是否即将超时(soonTimeout=1) + 即将超时时间(soonTimeoutHours,单位小时)。
         截止时间口径与 RyTask/RedisMqReceiver 的 isSubtaskDeadline 保持一致:
         service_subtask.endtime + service_task.send_day(缺省1) + (Σ service_subtask_preachform.compensate_time + 1) 天;
         仅当存在有效补偿记录时才计算截止时间(无补偿记录不参与即将超时);
         仅统计“尚未超时、且在 N 小时内将要超时”的数据。 -->
    <sql id="soonTimeoutDeadlineExpr">
        (
        SELECT DATE_ADD(
        DATE_ADD(ss.endtime, INTERVAL COALESCE(st.send_day, 1) DAY),
        INTERVAL (COALESCE(pf.compensate_days, 0) + 1) DAY
        )
        FROM service_subtask ss
        LEFT JOIN service_task st ON st.taskid = ss.taskid AND st.del_flag = '0'
        INNER JOIN (
        SELECT subid,
        SUM(CAST(COALESCE(NULLIF(TRIM(compensate_time), ''), '0') AS SIGNED)) AS compensate_days
        FROM service_subtask_preachform
        WHERE del_flag = 0
        GROUP BY subid
        ) pf ON pf.subid = ss.id
        WHERE ss.id = service_subtask.id
        )
    </sql>
    <sql id="soonTimeoutFilter">
        <if test="soonTimeout != null and soonTimeout == 1">
            AND <include refid="soonTimeoutDeadlineExpr"/> &gt; NOW()
            AND <include refid="soonTimeoutDeadlineExpr"/> &lt;= DATE_ADD(NOW(), INTERVAL COALESCE(#{soonTimeoutHours}, 0) HOUR)
        </if>
    </sql>
    <select id="selectServiceSubtaskList" parameterType="com.smartor.domain.entity.ServiceSubtaskEntity"
            resultMap="ServiceSubtaskResult">
@@ -529,6 +559,7 @@
                #{visitDeptCode}
            </foreach>
        </if>
        <include refid="soonTimeoutFilter"/>
        <if test="sort != null  and sort==0">order by endtime asc</if>
        <if test="sort != null  and sort==1">order by endtime desc</if>
        <if test="sort != null  and sort==2">order by visit_time asc</if>
@@ -538,7 +569,9 @@
        <if test="sort != null  and sort==7">order by visit_time asc</if>
        <if test="sort != null  and sort==8">order by visit_time desc</if>
        <if test="sort != null  and sort==9">order by sendname asc</if>
        <if test="pageSize != null  and pageNum != null">limit ${pageSize} OFFSET ${pageNum}</if>
        <if test="pageSize != null  and pageNum != null">
            LIMIT ${pageSize} OFFSET ${pageNum}
        </if>
    </select>
    <select id="selectSatisfactionSubtaskList" parameterType="com.smartor.domain.VO.PatSatisfactionReqVO"
@@ -696,6 +729,7 @@
        <if test="continueTimeNow != null ">and continue_time_now = #{continueTimeNow,jdbcType=TIMESTAMP}</if>
        <if test="continueCount != null ">and continue_count = #{continueCount}</if>
        <if test="continueTimeNext != null ">and continue_time_next = #{continueTimeNext}</if>
        <include refid="soonTimeoutFilter"/>
    </select>
    <select id="selectSendstateByCondition" parameterType="com.smartor.domain.ServiceSubtask" resultType="Integer">
@@ -2846,22 +2880,54 @@
        COUNT(CASE WHEN sendstate = 5 THEN 1 END) AS failedVisitCount,
        SUM(CASE WHEN excep IS NOT NULL AND excep = '1' THEN 1 ELSE 0 END) AS abnormalVisitCount,
        SUM(CASE WHEN excep IS NOT NULL AND excep = '2' THEN 1 ELSE 0 END) AS warnningVisitCount,
        COUNT(*) AS allVisitCount
        FROM service_subtask
        WHERE del_flag = '0'
        AND service_type = '2'
        AND visit_count = 1
        COUNT(*) AS allVisitCount,
        <choose>
            <when test="soonTimeoutHours != null and soonTimeoutHours > 0">
                COUNT(CASE WHEN t.soon_deadline &gt; NOW()
                AND t.soon_deadline &lt;= DATE_ADD(NOW(), INTERVAL #{soonTimeoutHours} HOUR)
                THEN 1 END) AS soonTimeoutCount
            </when>
            <otherwise>
                0 AS soonTimeoutCount
            </otherwise>
        </choose>
        FROM (
        SELECT ss.*,
        <!-- 即将超时截止时间 = 出院时间(endtime) + 发送日(send_day, 缺省1) + 补偿天数(compensate_time 求和后再 +1 天),
             与 RyTask/RedisMqReceiver 的 isSubtaskDeadline 口径保持一致;
             无有效补偿记录(cp.subid 为空)时截止时间为 NULL,不参与即将超时统计 -->
        CASE
        WHEN cp.subid IS NULL THEN NULL
        ELSE DATE_ADD(
        DATE_ADD(ss.endtime, INTERVAL COALESCE(st.send_day, 1) DAY),
        INTERVAL (COALESCE(cp.compensate_days, 0) + 1) DAY
        )
        END AS soon_deadline
        FROM service_subtask ss
        LEFT JOIN service_task st ON st.taskid = ss.taskid AND st.del_flag = '0'
        <!-- 补偿时间:先按子任务聚合成一行再关联,避免一对多放大计数;
             compensate_time 是 varchar 且可能存成空串,需先置空再转数字 -->
        LEFT JOIN (
        SELECT subid,
        SUM(CAST(COALESCE(NULLIF(TRIM(compensate_time), ''), '0') AS SIGNED)) AS compensate_days
        FROM service_subtask_preachform
        WHERE del_flag = 0
        GROUP BY subid
        ) cp ON cp.subid = ss.id
        WHERE ss.del_flag = '0'
        AND ss.service_type = '2'
        AND ss.visit_count = 1
        <if test="orgid != null and orgid != ''">
            AND orgid = #{orgid}
            AND ss.orgid = #{orgid}
        </if>
        <if test="campusid != null and campusid != ''">
            and campusid = #{campusid}
            and ss.campusid = #{campusid}
        </if>
        <if test="(leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0)
                or (leaveldeptcodes != null and leaveldeptcodes.size() > 0)">
            AND (
            <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
                leavehospitaldistrictcode IN
                ss.leavehospitaldistrictcode IN
                <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="("
                         separator=","
                         close=")">
@@ -2873,13 +2939,14 @@
                OR
            </if>
            <if test=" leaveldeptcodes != null and leaveldeptcodes.size()>0">
                deptcode IN
                ss.deptcode IN
                <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
                         close=")">
                    #{leaveldeptcode}
                </foreach>
            </if>)
        </if>
        ) t
    </select>
    <select id="querySendCount" parameterType="com.smartor.domain.entity.ServiceSubtaskSatisfactionEntity"
smartor/src/main/resources/mapper/smartor/ServiceTaskMapper.xml
@@ -462,6 +462,9 @@
            <if test="uploadTime != null">upload_time,</if>
            <if test="orgid != null">orgid,</if>
            <if test="campusid != null and campusid != ''">campusid,</if>
            <if test="nexttaskflag != null">nexttaskflag,</if>
            <if test="nexttaskid != null">nexttaskid,</if>
            <if test="nexttaskname != null">nexttaskname,</if>
            <if test="pid != null">pid,</if>
            <if test="guid != null">guid,</if>
            <if test="preachform != null">preachform,</if>
@@ -542,7 +545,7 @@
            <if test="jsy != null  and jsy != ''">#{jsy},</if>
            <if test="leavehospitaldistrictname != null  ">#{leavehospitaldistrictname},</if>
            <if test="leavehospitaldistrictcode != null  ">#{leavehospitaldistrictcode},</if>
            <if test="compensateDate != null  and compensateDate != ''">#{compensateDate}</if>
            <if test="compensateDate != null  and compensateDate != ''">#{compensateDate},</if>
            <if test="sendDay != null  ">#{sendDay},</if>
            <if test="appltype != null  ">#{appltype},</if>
            <if test="patCycle != null  ">#{patCycle},</if>