| | |
| | | import com.ruoyi.common.constant.UserConstants; |
| | | import com.ruoyi.common.core.domain.entity.*; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.exception.base.BaseException; |
| | | import com.ruoyi.common.utils.DtoConversionUtils; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.utils.TreeUtils; |
| | | import com.ruoyi.common.utils.bean.BeanValidators; |
| | | import com.ruoyi.common.utils.spring.SpringUtils; |
| | | import com.ruoyi.system.domain.SysPost; |
| | | import com.ruoyi.system.domain.SysUserPost; |
| | | import com.ruoyi.system.mapper.*; |
| | | import com.ruoyi.system.service.ISysConfigService; |
| | | import com.ruoyi.system.service.ISysDeptService; |
| | | import com.ruoyi.system.service.ISysUserService; |
| | | import com.smartor.mapper.SysUserDeptMapper; |
| | | 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; |
| | | import org.springframework.beans.factory.annotation.Qualifier; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.util.ObjectUtils; |
| | | |
| | | import javax.validation.Validator; |
| | | import java.util.*; |
| | | import java.util.concurrent.CompletableFuture; |
| | | import java.util.concurrent.Executor; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * 用户 业务层处理 |
| | | * 用户 业务层处�? |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | |
| | | 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; |
| | | |
| | | @Autowired |
| | | private ISysDeptService sysDeptService; |
| | | |
| | | @Autowired |
| | | private SysDeptMapper sysDeptMapper; |
| | | |
| | | @Autowired |
| | | @Qualifier("sysUserDeptSystemMapper") |
| | | private SysUserDeptMapper sysUserDeptMapper; |
| | | |
| | | |
| | | @Autowired |
| | | private SysRoleMapper roleMapper; |
| | |
| | | @Autowired |
| | | protected Validator validator; |
| | | |
| | | @Value("${isAdmin}") |
| | | private List<Long> isAdmin; |
| | | |
| | | /** |
| | | * 根据条件分页查询用户列表 |
| | | * |
| | |
| | | @DataScope(deptAlias = "d", userAlias = "u") |
| | | public List<SysUser> selectUserList(SysUser user) { |
| | | return userMapper.selectUserList(user); |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public List<SysUser> getUserList(SysUser user) { |
| | | return userMapper.getUserList(user); |
| | | } |
| | | |
| | | /** |
| | | * 根据条件分页查询已分配用户角色列表 |
| | | * 根据条件分页查询已分配用户角色列�? |
| | | * |
| | | * @param user 用户信息 |
| | | * @return 用户信息集合信息 |
| | |
| | | } |
| | | |
| | | /** |
| | | * 根据条件分页查询未分配用户角色列表 |
| | | * 根据条件分页查询未分配用户角色列�? |
| | | * |
| | | * @param user 用户信息 |
| | | * @return 用户信息集合信息 |
| | |
| | | } |
| | | |
| | | /** |
| | | * 通过用户名查询用户 |
| | | * 通过用户名查询用�? |
| | | * |
| | | * @param userName 用户名 |
| | | * @param userName 用户�? |
| | | * @return 用户对象信息 |
| | | */ |
| | | @Override |
| | |
| | | SysUserDept sysUserDept = new SysUserDept(); |
| | | sysUserDept.setUserId(sysUser.getUserId()); |
| | | sysUserDept.setDeptType(sysUser.getSearchscope()); |
| | | sysUserDept.setOrgid(sysUser.getOrgid()); |
| | | List<SysUserDept> sysUserDeptKSs = null; |
| | | List<SysUserDept> sysUserDeptBQs = null; |
| | | if (StringUtils.isNotEmpty(sysUser.getSearchscope()) && sysUser.getSearchscope().equals("0")) { |
| | | sysUserDept.setDeptType("1"); |
| | | sysUserDeptKSs = sysUserDeptMapper.selectSysUserDeptList(sysUserDept); |
| | | sysUserDeptKSs = selectLeafSysDeptList(sysUserDept); |
| | | sysUserDept.setDeptType("2"); |
| | | sysUserDeptBQs = sysUserDeptMapper.selectSysUserDeptList(sysUserDept); |
| | | sysUserDeptBQs = selectLeafSysDeptList(sysUserDept); |
| | | } else if (StringUtils.isNotEmpty(sysUser.getSearchscope()) && sysUser.getSearchscope().equals("1")) { |
| | | sysUserDept.setDeptType("1"); |
| | | sysUserDeptKSs = sysUserDeptMapper.selectSysUserDeptList(sysUserDept); |
| | | sysUserDeptKSs = selectLeafSysDeptList(sysUserDept); |
| | | } else if (StringUtils.isNotEmpty(sysUser.getSearchscope()) && sysUser.getSearchscope().equals("2")) { |
| | | sysUserDept.setDeptType("2"); |
| | | sysUserDeptBQs = sysUserDeptMapper.selectSysUserDeptList(sysUserDept); |
| | | sysUserDeptBQs = selectLeafSysDeptList(sysUserDept); |
| | | } |
| | | |
| | | sysUser.setDepts(deptIds); |
| | |
| | | return sysUser; |
| | | } |
| | | |
| | | @Override |
| | | public SysUser selectUserByUserName2(String userName) { |
| | | String[] split = userName.split("&"); |
| | | SysUser sysUser = userMapper.selectUserByUserNameByCondition(split[0], split[1], null); |
| | | if (Objects.isNull(sysUser)) return sysUser; |
| | | //这里只卡用户类型为“患者”的 |
| | | if (sysUser.getUserType().equals("患者") && StringUtils.isNotEmpty(split[3]) && !split[3].equals("null") && !sysUser.getUserType().equals(split[3].trim())) |
| | | throw new BaseException("当前用户类型与登录用户类型不一致"); |
| | | |
| | | boolean isAdmin = StringUtils.isNotEmpty(split[0]) && "admin".equals(split[0]); |
| | | IUserExtInfoService userExtInfoService = SpringUtils.getBean(IUserExtInfoService.class); |
| | | Executor executor = SpringUtils.getBean("threadPoolTaskExecutor"); |
| | | |
| | | // === 并行查询: 用户机构信息 + 部门列表 === |
| | | CompletableFuture<Map<String, String>> orgInfoFuture = CompletableFuture.supplyAsync(() -> userExtInfoService.getUserOrgInfo(sysUser.getUserId().toString(), split[1], split[2]), executor); |
| | | |
| | | CompletableFuture<List<SysDept>> deptsFuture; |
| | | if (isAdmin) { |
| | | SysDept dept = new SysDept(); |
| | | dept.setOrgid(split[1]); |
| | | deptsFuture = CompletableFuture.supplyAsync(() -> sysDeptMapper.selectDeptList(dept), executor); |
| | | } else { |
| | | deptsFuture = CompletableFuture.supplyAsync(() -> sysUserDeptMapper.selectDeptListByUserId(sysUser.getUserId()), executor); |
| | | } |
| | | |
| | | // 等待机构信息 |
| | | Map<String, String> userOrgInfo = new HashMap<>(); |
| | | try { |
| | | userOrgInfo = orgInfoFuture.get(5, TimeUnit.SECONDS); |
| | | } catch (Exception e) { |
| | | log.warn("获取用户机构信息超时或异常", e); |
| | | } |
| | | if (!userOrgInfo.isEmpty()) { |
| | | sysUser.setOrgid(userOrgInfo.get("orgid")); |
| | | sysUser.setCampusid(userOrgInfo.get("campusid")); |
| | | } |
| | | |
| | | // 等待部门列表 |
| | | List<SysDept> sysDepts = new ArrayList<>(); |
| | | try { |
| | | sysDepts = deptsFuture.get(5, TimeUnit.SECONDS); |
| | | } catch (Exception e) { |
| | | log.warn("获取部门列表超时或异常", e); |
| | | } |
| | | |
| | | List<Long> deptIds = new ArrayList<>(); |
| | | for (SysDept sysDept : sysDepts) { |
| | | deptIds.add(sysDept.getDeptId()); |
| | | } |
| | | |
| | | // === 查询科室和病区信息 === |
| | | List<SysUserDept> sysUserDeptKSs = null; |
| | | List<SysUserDept> sysUserDeptBQs = null; |
| | | if (StringUtils.isNotEmpty(sysUser.getSearchscope())) { |
| | | if ("0".equals(sysUser.getSearchscope())) { |
| | | if (isAdmin) { |
| | | // admin直接从sys_dept查询,跳过无意义的selectSysUserDeptList |
| | | SysDept deptKS = new SysDept(); |
| | | deptKS.setOrgid(split[1]); |
| | | deptKS.setDeptType("1"); |
| | | SysDept deptBQ = new SysDept(); |
| | | deptBQ.setOrgid(split[1]); |
| | | deptBQ.setDeptType("2"); |
| | | CompletableFuture<List<SysDept>> ksFuture = CompletableFuture.supplyAsync(() -> selectLeafSysDeptForAdmin(deptKS), executor); |
| | | CompletableFuture<List<SysDept>> bqFuture = CompletableFuture.supplyAsync(() -> selectLeafSysDeptForAdmin(deptBQ), executor); |
| | | try { |
| | | sysUserDeptKSs = distinctByDeptCode(DtoConversionUtils.sourceToTarget(ksFuture.get(5, TimeUnit.SECONDS), SysUserDept.class)); |
| | | sysUserDeptBQs = distinctByDeptCode(DtoConversionUtils.sourceToTarget(bqFuture.get(5, TimeUnit.SECONDS), SysUserDept.class)); |
| | | } catch (Exception e) { |
| | | log.warn("获取admin科室/病区信息超时或异常", e); |
| | | } |
| | | } else { |
| | | // 并行查询科室和病区 |
| | | SysUserDept queryKS = new SysUserDept(); |
| | | queryKS.setUserId(sysUser.getUserId()); |
| | | queryKS.setDeptType("1"); |
| | | queryKS.setOrgid(split[1]); |
| | | SysUserDept queryBQ = new SysUserDept(); |
| | | queryBQ.setUserId(sysUser.getUserId()); |
| | | queryBQ.setDeptType("2"); |
| | | queryBQ.setOrgid(split[1]); |
| | | CompletableFuture<List<SysUserDept>> ksFuture = CompletableFuture.supplyAsync(() -> selectLeafSysDeptList(queryKS), executor); |
| | | CompletableFuture<List<SysUserDept>> bqFuture = CompletableFuture.supplyAsync(() -> selectLeafSysDeptList(queryBQ), executor); |
| | | try { |
| | | sysUserDeptKSs = ksFuture.get(5, TimeUnit.SECONDS); |
| | | sysUserDeptBQs = bqFuture.get(5, TimeUnit.SECONDS); |
| | | } catch (Exception e) { |
| | | log.warn("获取科室/病区信息超时或异常", e); |
| | | } |
| | | } |
| | | } else if ("1".equals(sysUser.getSearchscope())) { |
| | | SysUserDept queryKS = new SysUserDept(); |
| | | queryKS.setUserId(sysUser.getUserId()); |
| | | queryKS.setDeptType("1"); |
| | | queryKS.setOrgid(split[1]); |
| | | sysUserDeptKSs = selectLeafSysDeptList(queryKS); |
| | | } else if ("2".equals(sysUser.getSearchscope())) { |
| | | SysUserDept queryBQ = new SysUserDept(); |
| | | queryBQ.setUserId(sysUser.getUserId()); |
| | | queryBQ.setDeptType("2"); |
| | | queryBQ.setOrgid(split[1]); |
| | | sysUserDeptBQs = selectLeafSysDeptList(queryBQ); |
| | | } |
| | | } |
| | | |
| | | sysUser.setDepts(deptIds); |
| | | if (!CollectionUtils.isEmpty(sysUserDeptBQs)) { |
| | | List<Map<String, Object>> list = new ArrayList<>(); |
| | | for (SysUserDept sud : sysUserDeptBQs) { |
| | | Map<String, Object> map = new HashMap<>(); |
| | | map.put("districtCode", ""); |
| | | if (StringUtils.isNotEmpty(sud.getDeptCode())) map.put("districtCode", sud.getDeptCode()); |
| | | else continue; |
| | | map.put("districtName", ""); |
| | | if (StringUtils.isNotEmpty(sud.getDeptName())) map.put("districtName", sud.getDeptName()); |
| | | list.add(map); |
| | | } |
| | | sysUser.setBelongWards(list); |
| | | } |
| | | if (!CollectionUtils.isEmpty(sysUserDeptKSs)) { |
| | | List<Map<String, Object>> list = new ArrayList<>(); |
| | | for (SysUserDept sud : sysUserDeptKSs) { |
| | | Map<String, Object> map = new HashMap<>(); |
| | | map.put("deptCode", ""); |
| | | if (StringUtils.isNotEmpty(sud.getDeptCode())) map.put("deptCode", sud.getDeptCode()); |
| | | else continue; |
| | | map.put("deptName", ""); |
| | | if (StringUtils.isNotEmpty(sud.getDeptName())) map.put("deptName", sud.getDeptName()); |
| | | list.add(map); |
| | | } |
| | | sysUser.setBelongDepts(list); |
| | | // 设置满意度分类(走缓存) |
| | | sysUser.setSatisfactionCategories(userExtInfoService.getSatisfactionCategories(sysUser.getOrgid())); |
| | | } |
| | | return sysUser; |
| | | } |
| | | |
| | | /** |
| | | * 按deptCode去重 |
| | | */ |
| | | private List<SysUserDept> distinctByDeptCode(List<SysUserDept> list) { |
| | | if (CollectionUtils.isEmpty(list)) { |
| | | return list; |
| | | } |
| | | List<SysUserDept> result = new ArrayList<>(); |
| | | Set<String> uniqueCodes = new HashSet<>(); |
| | | for (SysUserDept item : list) { |
| | | String deptCode = item.getDeptCode(); |
| | | if (StringUtils.isNotEmpty(deptCode) && !uniqueCodes.contains(deptCode)) { |
| | | result.add(item); |
| | | uniqueCodes.add(deptCode); |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | public SysUser selectUserByUserNameAndDeptId(String userName) { |
| | | String[] split = userName.split("&"); |
| | | if (split[2].contains("null")) { |
| | | split[2] = null; |
| | | } |
| | | SysUser sysUser = userMapper.selectUserByUserNameByCondition(split[0], split[1], split[2]); |
| | | |
| | | //获取当前登陆人的机构和院�? |
| | | IUserExtInfoService userExtInfoService2 = SpringUtils.getBean(IUserExtInfoService.class); |
| | | Map<String, String> userOrgInfo2 = userExtInfoService2.getUserOrgInfo(sysUser.getUserId().toString(), split[1], split[3]); |
| | | if (!userOrgInfo2.isEmpty()) { |
| | | sysUser.setOrgid(userOrgInfo2.get("orgid")); |
| | | sysUser.setCampusid(userOrgInfo2.get("campusid")); |
| | | } |
| | | |
| | | log.info("selectUserByUserNameAndDeptId的sysUser的值为:{}", sysUser); |
| | | if (Objects.isNull(sysUser)) return sysUser; |
| | | List<SysDept> sysDepts = null; |
| | | if (StringUtils.isNotEmpty(split[0]) && split[0].equals("admin")) { |
| | | SysDept dept = new SysDept(); |
| | | dept.setOrgid(split[1]); |
| | | sysDepts = sysDeptMapper.selectDeptList(dept); |
| | | } else { |
| | | sysDepts = sysUserDeptMapper.selectDeptListByUserId(sysUser.getUserId()); |
| | | } |
| | | List<Long> deptIds = new ArrayList<>(); |
| | | for (SysDept sysDept : sysDepts) { |
| | | deptIds.add(sysDept.getDeptId()); |
| | | } |
| | | SysUserDept sysUserDept = new SysUserDept(); |
| | | sysUserDept.setUserId(sysUser.getUserId()); |
| | | sysUserDept.setDeptType(sysUser.getSearchscope()); |
| | | sysUserDept.setOrgid(split[1]); |
| | | List<SysUserDept> sysUserDeptKSs = null; |
| | | List<SysUserDept> sysUserDeptBQs = null; |
| | | if (StringUtils.isNotEmpty(sysUser.getSearchscope()) && sysUser.getSearchscope().equals("0")) { |
| | | if (StringUtils.isNotEmpty(split[0]) && split[0].equals("admin")) { |
| | | sysUserDept.setUserId(null); |
| | | } |
| | | sysUserDept.setDeptType("1"); |
| | | sysUserDeptKSs = selectLeafSysDeptList(sysUserDept); |
| | | sysUserDept.setDeptType("2"); |
| | | sysUserDeptBQs = selectLeafSysDeptList(sysUserDept); |
| | | //如果是admin,需要对sysUserDeptBQs,sysUserDeptBQs去重 |
| | | if (StringUtils.isNotEmpty(split[0]) && split[0].equals("admin")) { |
| | | //如果是admin,只能去sys_dept去取数据 |
| | | SysDept dept = new SysDept(); |
| | | dept.setOrgid(split[1]); |
| | | dept.setDeptType("1"); |
| | | sysDepts = selectLeafSysDeptForAdmin(dept); |
| | | sysUserDeptKSs = DtoConversionUtils.sourceToTarget(sysDepts, SysUserDept.class); |
| | | |
| | | dept.setDeptType("2"); |
| | | sysDepts = selectLeafSysDeptForAdmin(dept); |
| | | sysUserDeptBQs = DtoConversionUtils.sourceToTarget(sysDepts, SysUserDept.class); |
| | | |
| | | List<SysUserDept> distinctUserDepts = new ArrayList<>(); |
| | | Set<String> uniqueDeptCodes = new HashSet<>(); |
| | | for (SysUserDept userDept : sysUserDeptKSs) { |
| | | String deptCode = userDept.getDeptCode(); |
| | | if (StringUtils.isNotEmpty(deptCode) && !uniqueDeptCodes.contains(deptCode)) { |
| | | distinctUserDepts.add(userDept); |
| | | uniqueDeptCodes.add(deptCode); |
| | | } |
| | | } |
| | | |
| | | List<SysUserDept> distinctUserBQs = new ArrayList<>(); |
| | | Set<String> uniqueBQCodes = new HashSet<>(); |
| | | for (SysUserDept userDept : sysUserDeptBQs) { |
| | | String deptCode = userDept.getDeptCode(); |
| | | if (StringUtils.isNotEmpty(deptCode) && !uniqueBQCodes.contains(deptCode)) { |
| | | distinctUserBQs.add(userDept); |
| | | uniqueBQCodes.add(deptCode); |
| | | } |
| | | } |
| | | sysUserDeptKSs = distinctUserDepts; |
| | | sysUserDeptBQs = distinctUserBQs; |
| | | } |
| | | |
| | | } else if (StringUtils.isNotEmpty(sysUser.getSearchscope()) && sysUser.getSearchscope().equals("1")) { |
| | | sysUserDept.setDeptType("1"); |
| | | sysUserDeptKSs = sysUserDeptMapper.selectSysUserDeptList(sysUserDept); |
| | | } else if (StringUtils.isNotEmpty(sysUser.getSearchscope()) && sysUser.getSearchscope().equals("2")) { |
| | | sysUserDept.setDeptType("2"); |
| | | sysUserDeptBQs = sysUserDeptMapper.selectSysUserDeptList(sysUserDept); |
| | | } |
| | | |
| | | sysUser.setDepts(deptIds); |
| | | if (!CollectionUtils.isEmpty(sysUserDeptBQs)) { |
| | | List<Map<String, Object>> list = new ArrayList<>(); |
| | | for (SysUserDept sud : sysUserDeptBQs) { |
| | | Map<String, Object> map = new HashMap<>(); |
| | | map.put("districtCode", ""); |
| | | if (StringUtils.isNotEmpty(sud.getDeptCode())) map.put("districtCode", sud.getDeptCode()); |
| | | else continue; |
| | | map.put("districtName", ""); |
| | | if (StringUtils.isNotEmpty(sud.getDeptName())) map.put("districtName", sud.getDeptName()); |
| | | list.add(map); |
| | | } |
| | | sysUser.setBelongWards(list); |
| | | } |
| | | if (!CollectionUtils.isEmpty(sysUserDeptKSs)) { |
| | | List<Map<String, Object>> list = new ArrayList<>(); |
| | | for (SysUserDept sud : sysUserDeptKSs) { |
| | | Map<String, Object> map = new HashMap<>(); |
| | | map.put("deptCode", ""); |
| | | if (StringUtils.isNotEmpty(sud.getDeptCode())) map.put("deptCode", sud.getDeptCode()); |
| | | else continue; |
| | | map.put("deptName", ""); |
| | | if (StringUtils.isNotEmpty(sud.getDeptName())) map.put("deptName", sud.getDeptName()); |
| | | list.add(map); |
| | | } |
| | | sysUser.setBelongDepts(list); |
| | | } |
| | | return sysUser; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 通过用户ID查询用户 |
| | | * |
| | |
| | | /** |
| | | * 查询用户所属角色组 |
| | | * |
| | | * @param userName 用户名 |
| | | * @param userName 用户�? |
| | | * @return 结果 |
| | | */ |
| | | @Override |
| | |
| | | /** |
| | | * 查询用户所属岗位组 |
| | | * |
| | | * @param userName 用户名 |
| | | * @param userName 用户�? |
| | | * @return 结果 |
| | | */ |
| | | @Override |
| | |
| | | @Override |
| | | public String checkUserNameUnique(SysUser user) { |
| | | Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); |
| | | SysUser info = userMapper.checkUserNameUnique(user.getUserName()); |
| | | SysUser info = userMapper.checkUserNameUnique(user); |
| | | if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) { |
| | | return UserConstants.NOT_UNIQUE; |
| | | } |
| | |
| | | */ |
| | | @Override |
| | | public void checkUserAllowed(SysUser user) { |
| | | if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) { |
| | | if (StringUtils.isNotNull(user.getUserId()) && isAdmin.contains(user.getUserId())) { |
| | | throw new ServiceException("不允许操作超级管理员用户"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 校验用户是否有数据权限 |
| | | * 校验用户是否有数据权�? |
| | | * |
| | | * @param userId 用户id |
| | | */ |
| | | @Override |
| | | public void checkUserDataScope(Long userId) { |
| | | if (!SysUser.isAdmin(SecurityUtils.getUserId())) { |
| | | if (!isAdmin.contains(SecurityUtils.getUserId())) { |
| | | SysUser user = new SysUser(); |
| | | user.setUserId(userId); |
| | | List<SysUser> users = SpringUtils.getAopProxy(this).selectUserList(user); |
| | | if (StringUtils.isEmpty(users)) { |
| | | throw new ServiceException("没有权限访问用户数据!"); |
| | | throw new ServiceException("没有权限访问用户数据"); |
| | | } |
| | | } |
| | | } |
| | |
| | | public int insertUser(SysUser user) { |
| | | // 新增用户信息 |
| | | int rows = userMapper.insertUser(user); |
| | | //新增用户与部门关联 |
| | | if (!CollectionUtils.isEmpty(user.getDepts())) { |
| | | for (Long deptId : user.getDepts()) { |
| | | SysUserDept sysUserDep = new SysUserDept(); |
| | | sysUserDep.setUserId(user.getUserId()); |
| | | sysUserDep.setDeptId(deptId); |
| | | sysUserDep.setCreateTime(new Date()); |
| | | sysUserDep.setUpdateTime(new Date()); |
| | | sysUserDep.setOrgid(user.getOrgid()); |
| | | sysUserDeptMapper.insertSysUserDept(sysUserDep); |
| | | //新增用户与部门关�? |
| | | // if (!CollectionUtils.isEmpty(user.getDepts())) { |
| | | // for (Long deptId : user.getDepts()) { |
| | | // SysUserDept sysUserDep = new SysUserDept(); |
| | | // sysUserDep.setUserId(user.getUserId()); |
| | | // sysUserDep.setDeptId(deptId); |
| | | // sysUserDep.setCreateTime(new Date()); |
| | | // sysUserDep.setUpdateTime(new Date()); |
| | | // sysUserDep.setOrgid(user.getOrgid()); |
| | | // sysUserDeptMapper.insertSysUserDept(sysUserDep); |
| | | // } |
| | | // } |
| | | if (!CollectionUtils.isEmpty(user.getDeptCodes())) { |
| | | for (String deptCode : user.getDeptCodes()) { |
| | | SysDept sysDept = sysDeptMapper.selectDeptByCode("1", user.getOrgid(), deptCode); |
| | | if (ObjectUtils.isEmpty(sysDept)) { |
| | | continue; |
| | | } |
| | | |
| | | SysUserDept sysUserDept = new SysUserDept(); |
| | | sysUserDept.setUserId(user.getUserId()); |
| | | sysUserDept.setOrgid(user.getOrgid()); |
| | | sysUserDept.setDeptCode(deptCode); |
| | | sysUserDept.setDeptId(sysDept.getDeptId()); |
| | | List<SysUserDept> sysUserDepts = sysUserDeptMapper.selectSysUserDeptList(sysUserDept); |
| | | sysUserDept.setDelFlag(0L); |
| | | sysUserDept.setDeptType("1"); |
| | | sysUserDept.setDeptName(sysDept.getDeptName()); |
| | | sysUserDept.setUpdateTime(new Date()); |
| | | if (!CollectionUtils.isEmpty(sysUserDepts)) { |
| | | sysUserDept.setId(sysUserDepts.get(0).getId()); |
| | | sysUserDeptMapper.updateSysUserDept(sysUserDept); |
| | | continue; |
| | | } |
| | | sysUserDept.setCreateTime(new Date()); |
| | | sysUserDeptMapper.insertSysUserDept(sysUserDept); |
| | | } |
| | | } |
| | | if (!CollectionUtils.isEmpty(user.getWardCodes())) { |
| | | for (String wardCode : user.getWardCodes()) { |
| | | SysDept sysDept = sysDeptMapper.selectDeptByCode("2", user.getOrgid(), wardCode); |
| | | if (ObjectUtils.isEmpty(sysDept)) { |
| | | continue; |
| | | } |
| | | SysUserDept sysUserDept = new SysUserDept(); |
| | | sysUserDept.setUserId(user.getUserId()); |
| | | sysUserDept.setDeptCode(wardCode); |
| | | sysUserDept.setDeptId(sysDept.getDeptId()); |
| | | sysUserDept.setOrgid(user.getOrgid()); |
| | | List<SysUserDept> sysUserDepts = sysUserDeptMapper.selectSysUserDeptList(sysUserDept); |
| | | sysUserDept.setDelFlag(0L); |
| | | sysUserDept.setDeptType("2"); |
| | | sysUserDept.setOrgid(user.getOrgid()); |
| | | sysUserDept.setDeptName(sysDept.getDeptName()); |
| | | sysUserDept.setUpdateTime(new Date()); |
| | | if (!CollectionUtils.isEmpty(sysUserDepts)) { |
| | | sysUserDeptMapper.updateSysUserDept(sysUserDept); |
| | | continue; |
| | | } |
| | | sysUserDept.setCreateTime(new Date()); |
| | | sysUserDeptMapper.insertSysUserDept(sysUserDept); |
| | | } |
| | | } |
| | | // 新增用户岗位关联 |
| | | insertUserPost(user); |
| | | // 新增用户与角色管理 |
| | | // 新增用户与角色管�? |
| | | insertUserRole(user); |
| | | return rows; |
| | | } |
| | |
| | | @Transactional |
| | | public int updateUser(SysUser user) { |
| | | Long userId = user.getUserId(); |
| | | // 删除用户与角色关联 |
| | | // 删除用户与角色关�? |
| | | userRoleMapper.deleteUserRoleByUserId(userId); |
| | | // 新增用户与角色管理 |
| | | // 新增用户与角色管�? |
| | | insertUserRole(user); |
| | | // 删除用户与岗位关联 |
| | | // 删除用户与岗位关�? |
| | | userPostMapper.deleteUserPostByUserId(userId); |
| | | // 新增用户与岗位管理 |
| | | // 新增用户与岗位管�? |
| | | insertUserPost(user); |
| | | //删除用户与部门的关联 |
| | | Long[] ids = {user.getUserId()}; |
| | | //新增用户与部门关联 |
| | | //新增用户与部门关�? |
| | | // sysUserDeptMapper.deleteSysUserDeptByIds(ids); |
| | | // if (!CollectionUtils.isEmpty(user.getDepts())) { |
| | | // for (Long deptId : user.getDepts()) { |
| | | // SysUserDept sysUserDep = new SysUserDept(); |
| | | // sysUserDep.setUserId(user.getUserId()); |
| | | // sysUserDep.setDeptId(deptId); |
| | | // //查询是否 已经关联该部门 |
| | | // //查询是否 已经关联该部�? |
| | | // List<SysUserDept> sysUserDepts = sysUserDeptMapper.selectSysUserDeptList(sysUserDep); |
| | | // if (!CollectionUtils.isEmpty(sysUserDepts)) { |
| | | // SysUserDept sysUserDept = sysUserDepts.get(0); |
| | |
| | | // sysUserDeptMapper.insertSysUserDept(sysUserDep); |
| | | // } |
| | | // } |
| | | //删除原有关系 |
| | | SysUserDept delSysUserDept = new SysUserDept(); |
| | | delSysUserDept.setUserId(user.getUserId()); |
| | | delSysUserDept.setOrgid(user.getOrgid()); |
| | | sysUserDeptMapper.deleteSysUserDeptByUserId(delSysUserDept); |
| | | |
| | | if (!CollectionUtils.isEmpty(user.getDeptCodes())) { |
| | | for (String deptCode : user.getDeptCodes()) { |
| | | SysDept sysDept = sysDeptMapper.selectDeptByCode(deptCode); |
| | | SysDept sysDept = sysDeptMapper.selectDeptByCode("1", user.getOrgid(), deptCode); |
| | | if (ObjectUtils.isEmpty(sysDept)) { |
| | | continue; |
| | | } |
| | | |
| | | SysUserDept sysUserDept = new SysUserDept(); |
| | | sysUserDept.setUserId(user.getUserId()); |
| | | sysUserDept.setOrgid(user.getOrgid()); |
| | | sysUserDept.setDeptCode(deptCode); |
| | | List<SysUserDept> sysUserDepts = sysUserDeptMapper.selectSysUserDeptList(sysUserDept); |
| | | sysUserDept.setDeptId(sysDept.getDeptId()); |
| | | sysUserDept.setDelFlag(0L); |
| | | sysUserDept.setDeptType("1"); |
| | | sysUserDept.setDeptName(sysDept.getDeptName()); |
| | | sysUserDept.setUpdateTime(new Date()); |
| | | if (!CollectionUtils.isEmpty(sysUserDepts)) { |
| | | sysUserDept.setId(sysUserDepts.get(0).getId()); |
| | | sysUserDeptMapper.updateSysUserDept(sysUserDept); |
| | | continue; |
| | | } |
| | | sysUserDept.setCreateTime(new Date()); |
| | | sysUserDeptMapper.insertSysUserDept(sysUserDept); |
| | | } |
| | | } |
| | | |
| | | if (!CollectionUtils.isEmpty(user.getWardCodes())) { |
| | | for (String wardCode : user.getWardCodes()) { |
| | | SysDept sysDept = sysDeptMapper.selectDeptByCode(wardCode); |
| | | |
| | | SysDept sysDept = sysDeptMapper.selectDeptByCode("2", user.getOrgid(), wardCode); |
| | | if (ObjectUtils.isEmpty(sysDept)) { |
| | | continue; |
| | | } |
| | | SysUserDept sysUserDept = new SysUserDept(); |
| | | sysUserDept.setUserId(user.getUserId()); |
| | | sysUserDept.setDeptCode(wardCode); |
| | | List<SysUserDept> sysUserDepts = sysUserDeptMapper.selectSysUserDeptList(sysUserDept); |
| | | sysUserDept.setDeptId(sysDept.getDeptId()); |
| | | sysUserDept.setOrgid(user.getOrgid()); |
| | | sysUserDept.setDelFlag(0L); |
| | | sysUserDept.setDeptType("2"); |
| | | sysUserDept.setOrgid(user.getOrgid()); |
| | | sysUserDept.setDeptName(sysDept.getDeptName()); |
| | | sysUserDept.setUpdateTime(new Date()); |
| | | if (!CollectionUtils.isEmpty(sysUserDepts)) { |
| | | sysUserDeptMapper.updateSysUserDept(sysUserDept); |
| | | continue; |
| | | } |
| | | sysUserDept.setCreateTime(new Date()); |
| | | sysUserDeptMapper.insertSysUserDept(sysUserDept); |
| | | } |
| | |
| | | * 用户授权角色 |
| | | * |
| | | * @param userId 用户ID |
| | | * @param roleIds 角色组 |
| | | * @param roleIds 角色�? |
| | | */ |
| | | @Override |
| | | @Transactional |
| | |
| | | } |
| | | |
| | | /** |
| | | * 修改用户状态 |
| | | * 修改用户状�? |
| | | * |
| | | * @param user 用户信息 |
| | | * @return 结果 |
| | |
| | | /** |
| | | * 修改用户头像 |
| | | * |
| | | * @param userName 用户名 |
| | | * @param userName 用户�? |
| | | * @param avatar 头像地址 |
| | | * @return 结果 |
| | | */ |
| | |
| | | /** |
| | | * 重置用户密码 |
| | | * |
| | | * @param userName 用户名 |
| | | * @param userName 用户�? |
| | | * @param password 密码 |
| | | * @return 结果 |
| | | */ |
| | |
| | | public void insertUserPost(SysUser user) { |
| | | Long[] posts = user.getPostIds(); |
| | | if (StringUtils.isNotEmpty(posts)) { |
| | | // 新增用户与岗位管理 |
| | | // 新增用户与岗位管�? |
| | | List<SysUserPost> list = new ArrayList<SysUserPost>(posts.length); |
| | | for (Long postId : posts) { |
| | | SysUserPost up = new SysUserPost(); |
| | |
| | | * 新增用户角色信息 |
| | | * |
| | | * @param userId 用户ID |
| | | * @param roleIds 角色组 |
| | | * @param roleIds 角色�? |
| | | */ |
| | | public void insertUserRole(Long userId, Long[] roleIds, String orgid) { |
| | | if (StringUtils.isNotEmpty(roleIds)) { |
| | | // 新增用户与角色管理 |
| | | // 新增用户与角色管�? |
| | | List<SysUserRole> list = new ArrayList<SysUserRole>(roleIds.length); |
| | | for (Long roleId : roleIds) { |
| | | SysUserRole ur = new SysUserRole(); |
| | |
| | | @Override |
| | | @Transactional |
| | | public int deleteUserById(Long userId) { |
| | | // 删除用户与角色关联 |
| | | // 删除用户与角色关�? |
| | | userRoleMapper.deleteUserRoleByUserId(userId); |
| | | // 删除用户与岗位表 |
| | | userPostMapper.deleteUserPostByUserId(userId); |
| | |
| | | checkUserAllowed(new SysUser(userId)); |
| | | checkUserDataScope(userId); |
| | | } |
| | | // 删除用户与角色关联 |
| | | // 删除用户与角色关�? |
| | | userRoleMapper.deleteUserRole(userIds); |
| | | // 删除用户与岗位关联 |
| | | // 删除用户与岗位关�? |
| | | userPostMapper.deleteUserPost(userIds); |
| | | return userMapper.deleteUserByIds(userIds); |
| | | } |
| | |
| | | @Override |
| | | public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName) { |
| | | if (StringUtils.isNull(userList) || userList.size() == 0) { |
| | | throw new ServiceException("导入用户数据不能为空!"); |
| | | throw new ServiceException("导入用户数据不能为空"); |
| | | } |
| | | int successNum = 0; |
| | | int failureNum = 0; |
| | |
| | | user.setCreateBy(operName); |
| | | this.insertUser(user); |
| | | successNum++; |
| | | successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 导入成功"); |
| | | successMsg.append("<br/>" + successNum + "、账�?" + user.getUserName() + " 导入成功"); |
| | | } else if (isUpdateSupport) { |
| | | BeanValidators.validateWithException(validator, user); |
| | | checkUserAllowed(user); |
| | |
| | | user.setUpdateBy(operName); |
| | | this.updateUser(user); |
| | | successNum++; |
| | | successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 更新成功"); |
| | | successMsg.append("<br/>" + successNum + "账号?" + user.getUserName() + " 更新成功"); |
| | | } else { |
| | | failureNum++; |
| | | failureMsg.append("<br/>" + failureNum + "、账号 " + user.getUserName() + " 已存在"); |
| | | failureMsg.append("<br/>" + failureNum + "账号" + user.getUserName() + " 已存在"); |
| | | } |
| | | } catch (Exception e) { |
| | | failureNum++; |
| | | String msg = "<br/>" + failureNum + "、账号 " + user.getUserName() + " 导入失败:"; |
| | | String msg = "<br/>" + failureNum + "账号" + user.getUserName() + " 导入失败"; |
| | | failureMsg.append(msg + e.getMessage()); |
| | | log.error(msg, e); |
| | | } |
| | |
| | | failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); |
| | | throw new ServiceException(failureMsg.toString()); |
| | | } else { |
| | | successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); |
| | | successMsg.insert(0, "恭喜您,数据已全部导入成功!共" + successNum + "条,数据如下:"); |
| | | } |
| | | return successMsg.toString(); |
| | | } |
| | | |
| | | public List<SysUserDept> selectLeafSysDeptList(SysUserDept sysUserDept){ |
| | | List<SysDept> list = sysDeptMapper.selectSysDeptListBySysUserDept(sysUserDept); |
| | | if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(list)){ |
| | | list = TreeUtils.getLeafDepts(list); |
| | | } |
| | | List<SysUserDept> list1 = DtoConversionUtils.sourceToTarget(list, SysUserDept.class); |
| | | return list1; |
| | | } |
| | | |
| | | public List<SysDept> selectLeafSysDeptForAdmin(SysDept sysDept){ |
| | | SysUserDept sysUserDept = DtoConversionUtils.sourceToTarget(sysDept, SysUserDept.class); |
| | | List<SysDept> list = sysDeptMapper.selectSysDeptListBySysUserDept(sysUserDept); |
| | | if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(list)){ |
| | | list = TreeUtils.getLeafDepts(list); |
| | | } |
| | | 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<List<String>> 生成, |
| | | * 每个元素形如 [["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<List<String>>,格式如 [["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); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |