5

When writing the task management of the regional staff, it is necessary to obtain the residents managed by the login personnel. At this time, a question arises - how to realize these functions in the background?
We can find that the foreground does not pass the currently logged-in user to the background, so in theory, the current logged-in user should be obtained directly from the background.
This was also the case in the resident management in the previous project, so I will summarize it first.
After passing the corresponding paging query data to the background C layer, the background directly calls the residentService.page method.

 public Page<Resident> page(String name,
                           . . .
                             Pageable pageable) {
    District district = this.filterDistrictOfCurrentUserAccess(districtId);
    Specification<Resident> specification = this.getSpec(name,
       . . .
        beVaccinated);
    Page<Resident> residents = this.residentRepository.findAll(specification, pageable);

    return residents;
  }

We can find that the filterDistrictOfCurrentUserAccess method is used to obtain the area ID of the currently logged in person and add it to the comprehensive query conditions.
Among them, this is done for the residents who belong to a certain area in the comprehensive query.

 public static Specification<Resident> belongDistrict(District district) {
    if (district == null ||
        district.getId() == null ||
        district.getType() == null ||
        TYPE_COUNTY.equals(district.getType())) {
      logger.debug("未传入区域信息或区域ID为null或传入了区域为根区域,忽略查询条件");
      return Specification.where(null);
    }

    return (root, criteriaQuery, criteriaBuilder) -> {
      logger.debug("分别按楼、小区、社区、乡镇(默认)进行查询");
      Join<Resident, Building> buildingJoin = root.join("houses")
          .join("building", JoinType.LEFT);
      Long districtId = district.getId();
      switch (district.getType()) {
        case TYPE_BUILDING:
          return criteriaBuilder.equal(buildingJoin.get("id").as(Long.class), districtId);
        case TYPE_VILLAGE:
          return criteriaBuilder.equal(buildingJoin.join("parent").get("id").as(Long.class),
              districtId);
        case TYPE_COMMUNITY:
          return criteriaBuilder.equal(buildingJoin
                  .join("parent", JoinType.LEFT)
                  .join("parent", JoinType.LEFT)
                  .get("id").as(Long.class),
              districtId);
        default:
          return criteriaBuilder.equal(buildingJoin
                  .join("parent", JoinType.LEFT)
                  .join("parent", JoinType.LEFT)
                  .join("parent", JoinType.LEFT)
                  .get("id").as(Long.class),
              districtId);
      }
    };
  }

It seems that a lot of logic is actually very simple, first determine whether the incoming area information is complete, if not, return null.
After that, because the smallest unit of the query is the building, a Join object starting from the building is directly constructed.

 Join<Resident, Building> buildingJoin = root.join("houses")
          .join("building", JoinType.LEFT);

The root represents the resident, and then enters the houses attribute and then enters the building attribute.

 criteriaBuilder.equal(buildingJoin.get("id").as(Long.class), districtId);

Then query the resident equal to buildingId according to the incoming districtId;
What we have to do after that is to classify and discuss according to the incoming district.getType(), that is, if it is a community, then get the parent on the basis of this building and then query.

After that, let's take a look at how to get the current logged-in user and get its area ID in the actual project.
First, filter the incoming district. If districtId is passed in, check whether the current logged-in user has the management authority of the incoming district ID. If you have permission, return to the corresponding area; if you have no permission, return to the area where the currently logged-in user is located; if no districtId is passed in, return to the area where the logged-in user is located.

In this case we first discuss how to get the currently logged in user.

 public Optional<WebUser> getCurrentLoginWebUser() {
    return this.webUserRepository.findById(this.getCurrentLoginWebUserId()
        .orElseThrow(() -> new AccessDeniedException("当前登录类型不正确或未登录")));
  }

/**
获取当前登陆用户ID
**/
public Optional<Long> getCurrentLoginWebUserId() {
    AuthUserDetails authUserDetails = this.userService.getAuthUserDetailWithoutTransaction()
        .orElseThrow(() -> new AccessDeniedException("当前登录类型不正确或未登录"));
    if (authUserDetails instanceof WebUser) {
      return Optional.of(((WebUser) authUserDetails).getId());
    } else {
      return Optional.empty();
    }
  }
 public Optional<AuthUserDetails> getAuthUserDetailWithoutTransaction() {
    logger.debug("根据认证获取当前登录用户名,并获取该用户");
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication != null) {
      AuthUserDetails userDetail;
      if (authentication instanceof UsernamePasswordAuthenticationToken) {
        userDetail = (AuthUserDetails) authentication.getPrincipal();
      } else if (authentication instanceof AuthUserDetails) {
        userDetail = (AuthUserDetails) authentication;
      } else if (authentication instanceof AnonymousAuthenticationToken) {
        return Optional.empty();
      } else {
        throw new RuntimeException("获取类型不正确");
      }
      return Optional.of(userDetail);
    }

    logger.debug("认证用户在数据库中不存在");
    return Optional.empty();
  }

According to the above code, we can find that SpringBoot has encapsulated a part of the login. After logging in, we only need the following part of the code to obtain the information of the currently logged in user

 Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

After that, we will classify and discuss according to the type of the returned object and convert it into AuthUserDetails.

Question two

After using the front-end and back-end login, it is found that the menu interface disappears, but manually entering the address can enter the desired page.

图片.png
So the first thing to do is to understand how the menu is obtained, first test it with the front desk, and find that the menu can be displayed normally.
After looking for it in the front-end code, I found that each menu is equipped with a role option corresponding to the user.

 {
          name: '就业人员',
          url: 'employed-persons',
          icon: 'fa fa-user',
          roles: [ROLE_TYPE.volunteer.value, ROLE_TYPE.admin.value]
        }

Here's how to get the menu:

 this.userService.currentLoginUser$.subscribe(
        user => {
          console.log(user);
          const roleKeys = user ? user.roles.map(role => role.value) : [];
          subscribe.next(
            MenuService.menus.filter(menu => {
              const menuRoleKeys = menu.roles;
              let found = false;
              menuRoleKeys.forEach(roleKey => {
                if (!found && (roleKeys.indexOf(roleKey) !== -1)) {
                  found = true;
                }
              });
              return found;
            })
          );
        }
      );

First get the role array of the currently logged in user, then judge whether it matches the role array in the menu and then return the search result, so try to print the acquired role:
图片.png , found that roles are indeed empty, and then went to the database to check and found that the association table of roles_users was indeed empty, and the above relationship was re-added and returned to normal.


李明
441 声望18 粉丝