陈昶聿
5 天以前 1c82e21ac936241bf56109e7fff87446b0b6a131
【丽水】新增接口 修复Sys_User_Dept数据缺失问题
已修改8个文件
267 ■■■■■ 文件已修改
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserDeptMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysUserDeptMapper.xml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
@@ -283,6 +283,18 @@
    }
    /**
     * 根据 sys_user.hosp_info(科室)和 sys_user.dept_info(病区)补全 sys_user_dept 数据
     */
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @AddOrgId
    @PostMapping("/repairUserDept")
    public AjaxResult repairUserDept(@RequestBody(required = false) SysUser user) {
        String orgid = user == null ? null : user.getOrgid();
        Long userId = user == null ? null : user.getUserId();
        return success(userService.repairUserDeptByHospAndDeptInfo(orgid, userId));
    }
    /**
     * 获取部门树列表
     */
    //@PreAuthorize("@ss.hasPermi('system:user:list')")
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserDeptMapper.java
@@ -3,6 +3,7 @@
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysUserDept;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Component;
import java.util.List;
@@ -77,4 +78,15 @@
    public int deleteSysUserDeptByUserId(SysUserDept sysUserDept);
    public int updateSysUserDeptForSync(SysUserDept sysUserDept);
    /**
     * 根据用户ID、部门编码查询用户部门关系(包含已逻辑删除的数据)
     * 与 hosp_info / dept_info 的生成逻辑保持一致,部门编码是用户部门关系的业务唯一键
     *
     * @param userId   用户ID
     * @param deptCode 部门编码
     * @param orgid    机构ID,可为空
     * @return 用户部门关系
     */
    public SysUserDept selectSysUserDeptByUserAndCode(@Param("userId") Long userId, @Param("deptCode") String deptCode, @Param("orgid") String orgid);
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
@@ -24,6 +24,15 @@
    public List<SysUser> getUserList(SysUser sysUser);
    /**
     * 查询需要根据 hosp_info / dept_info 补全部门关系的用户
     *
     * @param orgid  机构ID,可为空
     * @param userId 用户ID,可为空
     * @return 用户信息集合
     */
    public List<SysUser> selectUsersForRepairUserDept(@Param("orgid") String orgid, @Param("userId") Long userId);
    /**
     * 根据条件分页查询已配用户角色列表
     *
     * @param user 用户信息
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
@@ -1,6 +1,7 @@
package com.ruoyi.system.service;
import java.util.List;
import java.util.Map;
import com.ruoyi.common.core.domain.entity.SysUser;
/**
@@ -214,4 +215,13 @@
     * @return 结果
     */
    public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName);
    /**
     * 根据 sys_user.hosp_info(科室)和 sys_user.dept_info(病区)补全 sys_user_dept 数据
     *
     * @param orgid  机构ID,为空时处理全部机构
     * @param userId 用户ID,为空时处理全部用户
     * @return 处理结果统计
     */
    Map<String, Object> repairUserDeptByHospAndDeptInfo(String orgid, Long userId);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -19,6 +19,8 @@
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.common.core.service.IUserExtInfoService;
import com.ruoyi.system.mapper.SysUserDeptMapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -44,6 +46,8 @@
@Service
public class SysUserServiceImpl implements ISysUserService {
    private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class);
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    @Autowired
    private SysUserMapper userMapper;
@@ -996,4 +1000,192 @@
        }
        return list;
    }
    /**
     * 根据 sys_user.hosp_info(科室)和 sys_user.dept_info(病区)补全 sys_user_dept 数据
     *
     * @param orgid  机构ID,为空时处理全部机构
     * @param userId 用户ID,为空时处理全部用户
     * @return 处理结果统计
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> repairUserDeptByHospAndDeptInfo(String orgid, Long userId) {
        List<SysUser> users = userMapper.selectUsersForRepairUserDept(orgid, userId);
        int processedUsers = 0;
        int inserted = 0;
        int updated = 0;
        int restored = 0;
        int deptNotFound = 0;
        Set<String> missingDeptCodes = new LinkedHashSet<>();
        for (SysUser user : users) {
            if (user.getUserId() == null) {
                continue;
            }
            List<String> hospCodes = parseDeptCodes(user.getHospInfo());
            List<String> wardCodes = parseDeptCodes(user.getDeptInfo());
            if (hospCodes.isEmpty() && wardCodes.isEmpty()) {
                continue;
            }
            processedUsers++;
            int[] hospStat = repairUserDept(user, hospCodes, "1", missingDeptCodes);
            int[] wardStat = repairUserDept(user, wardCodes, "2", missingDeptCodes);
            inserted += hospStat[0] + wardStat[0];
            updated += hospStat[1] + wardStat[1];
            restored += hospStat[2] + wardStat[2];
            deptNotFound += hospStat[3] + wardStat[3];
        }
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("scannedUsers", users.size());
        result.put("processedUsers", processedUsers);
        result.put("inserted", inserted);
        result.put("updated", updated);
        result.put("restored", restored);
        result.put("deptMetaNotFound", deptNotFound);
        result.put("missingDeptCodes", missingDeptCodes);
        log.info("根据hosp_info/dept_info补全用户部门关系完成,结果:{}", result);
        return result;
    }
    /**
     * 补全单个用户指定类型的部门关系。
     * hosp_info / dept_info 由 Gson 序列化 List&lt;List&lt;String&gt;&gt; 生成,
     * 每个元素形如 [["code"]],因此以部门编码作为补全依据,与原始采集逻辑一致。
     *
     * @param user             用户
     * @param deptCodes        部门编码集合
     * @param expectedDeptType 部门类型(1科室 2病区,对应 hosp_info / dept_info)
     * @param missingDeptCodes sys_dept 中未匹配到的部门编码
     * @return [新增数, 更新数, 恢复数, sys_dept未匹配数]
     */
    private int[] repairUserDept(SysUser user, List<String> deptCodes, String expectedDeptType, Set<String> missingDeptCodes) {
        int[] stat = new int[4];
        for (String deptCode : deptCodes) {
            // sys_dept 只用于补充 dept_id、dept_name 等信息,查不到也继续按编码补全
            SysDept sysDept = sysDeptMapper.selectDeptByCode(expectedDeptType, user.getOrgid(), deptCode);
            if (ObjectUtils.isEmpty(sysDept)) {
                // dept_type 可能存在历史脏数据,按编码兜底再查一次
                sysDept = sysDeptMapper.selectDeptByCode(null, user.getOrgid(), deptCode);
            }
            if (ObjectUtils.isEmpty(sysDept)) {
                stat[3]++;
                if (missingDeptCodes.size() < 100) {
                    missingDeptCodes.add(deptCode);
                }
            }
            String userDeptOrgid = StringUtils.isNotEmpty(user.getOrgid()) ? user.getOrgid()
                    : (sysDept == null ? null : sysDept.getOrgid());
            String campusid = sysDept != null && StringUtils.isNotEmpty(sysDept.getCampusid())
                    ? sysDept.getCampusid() : user.getCampusid();
            Date now = new Date();
            // 与原始生成逻辑保持一致,user_id + dept_code + orgid 作为业务唯一键
            SysUserDept sysUserDept = sysUserDeptMapper.selectSysUserDeptByUserAndCode(user.getUserId(), deptCode, user.getOrgid());
            if (ObjectUtils.isEmpty(sysUserDept)) {
                sysUserDept = new SysUserDept();
                sysUserDept.setUserId(user.getUserId());
                sysUserDept.setDeptCode(deptCode);
                sysUserDept.setDeptType(expectedDeptType);
                sysUserDept.setOrgid(userDeptOrgid);
                sysUserDept.setCampusid(campusid);
                if (sysDept != null) {
                    sysUserDept.setDeptId(sysDept.getDeptId());
                    sysUserDept.setDeptName(sysDept.getDeptName());
                }
                sysUserDept.setDelFlag(0L);
                sysUserDept.setCreateTime(now);
                sysUserDept.setUpdateTime(now);
                sysUserDeptMapper.insertSysUserDept(sysUserDept);
                stat[0]++;
                continue;
            }
            boolean wasDeleted = !Long.valueOf(0L).equals(sysUserDept.getDelFlag());
            sysUserDept.setDeptCode(deptCode);
            sysUserDept.setDeptType(expectedDeptType);
            sysUserDept.setOrgid(userDeptOrgid);
            sysUserDept.setCampusid(campusid);
            if (sysDept != null) {
                sysUserDept.setDeptId(sysDept.getDeptId());
                sysUserDept.setDeptName(sysDept.getDeptName());
            }
            sysUserDept.setDelFlag(0L);
            sysUserDept.setUpdateTime(now);
            sysUserDeptMapper.updateSysUserDept(sysUserDept);
            if (wasDeleted) {
                stat[2]++;
            } else {
                stat[1]++;
            }
        }
        return stat;
    }
    /**
     * 解析 hosp_info / dept_info 中的部门编码。
     * 原始生成逻辑使用 Gson 序列化 List&lt;List&lt;String&gt;&gt;,格式如 [["code1"],["code2"]];
     * 这里兼容该格式,同时兼容 ["code"]、[{"deptCode":"code"}] 等历史格式。
     */
    private List<String> parseDeptCodes(String json) {
        if (StringUtils.isEmpty(json)) {
            return Collections.emptyList();
        }
        List<String> codes = new ArrayList<>();
        try {
            collectDeptCodes(OBJECT_MAPPER.readTree(json), codes);
        } catch (Exception e) {
            log.warn("解析用户科室/病区信息失败,内容:{}", json, e);
        }
        return codes.stream().filter(StringUtils::isNotEmpty).distinct().collect(Collectors.toList());
    }
    private void collectDeptCodes(JsonNode node, List<String> codes) {
        if (node == null || node.isNull()) {
            return;
        }
        if (node.isArray()) {
            for (JsonNode item : node) {
                collectDeptCodes(item, codes);
            }
            return;
        }
        if (node.isObject()) {
            JsonNode codeNode = node.get("deptCode");
            if (codeNode == null) {
                codeNode = node.get("code");
            }
            if (codeNode == null) {
                codeNode = node.get("deptId");
            }
            if (codeNode == null) {
                codeNode = node.get("hisDeptId");
            }
            if (codeNode != null) {
                collectDeptCodes(codeNode, codes);
                return;
            }
            Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
            while (fields.hasNext()) {
                JsonNode value = fields.next().getValue();
                if (value != null && value.isValueNode() && !value.isNull()) {
                    collectDeptCodes(value, codes);
                    return;
                }
            }
            return;
        }
        if (node.isValueNode()) {
            String code = node.asText();
            if (StringUtils.isNotEmpty(code)) {
                code = code.trim();
                if (StringUtils.isNotEmpty(code) && !"null".equalsIgnoreCase(code)) {
                    codes.add(code);
                }
            }
        }
    }
}
ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml
@@ -135,6 +135,7 @@
        select d.dept_id,
               d.parent_id,
               d.campusid,
               d.orgid,
               d.dept_code,
               d.dept_type,
               d.ancestors,
ruoyi-system/src/main/resources/mapper/system/SysUserDeptMapper.xml
@@ -82,6 +82,17 @@
        where id = #{id}
    </select>
    <select id="selectSysUserDeptByUserAndCode" resultMap="SysUserDeptResult">
        <include refid="selectSysUserDeptVo"/>
        where user_id = #{userId}
          and dept_code = #{deptCode}
        <if test="orgid != null and orgid != ''">
            and orgid = #{orgid}
        </if>
        order by del_flag asc, id asc
        limit 1
    </select>
    <insert id="insertSysUserDept" parameterType="com.ruoyi.common.core.domain.entity.SysUserDept"
            useGeneratedKeys="true" keyProperty="id">
        insert into sys_user_dept
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -245,6 +245,26 @@
        <if test="pageSize != null  and pageNum != null">limit ${pageSize} OFFSET ${pageNum}</if>
    </select>
    <select id="selectUsersForRepairUserDept" resultMap="SysUserResult">
        select u.user_id,
               u.user_name,
               u.orgid,
               u.campusid,
               u.hosp_info,
               u.dept_info
        from sys_user u
        where u.del_flag = '0'
          and ((u.hosp_info is not null and u.hosp_info != '' and u.hosp_info != '[]')
            or (u.dept_info is not null and u.dept_info != '' and u.dept_info != '[]'))
        <if test="orgid != null and orgid != ''">
            AND u.orgid = #{orgid}
        </if>
        <if test="userId != null">
            AND u.user_id = #{userId}
        </if>
        order by u.user_id
    </select>
    <select id="selectAllocatedList" parameterType="com.ruoyi.common.core.domain.entity.SysUser"
            resultMap="SysUserResult">
        select distinct u.user_id,u.his_user_id,u.title,u.hosp_info,u.dept_info, u.searchscope, u.dept_id, u.user_name,