在写区域工作人员端任务管理时需要获取登陆人员所管理的居民,这时就出现了一个问题——后台要怎么实现这些功能呢?
我们可以发现前台并没有把当前登录用户传给后台,所以理论上应该是直接从后台获取当前登录用户。
之前项目中在居民管理中也是这种情况,所以就先总结了一下。
把相应的分页查询数据传给后台C层后,后台就直接调用了residentService.page
方法。
public Page page(String name,
. . .
Pageable pageable) {
District district = this.filterDistrictOfCurrentUserAccess(districtId);
Specification specification = this.getSpec(name,
. . .
beVaccinated);
Page residents = this.residentRepository.findAll(specification, pageable);
return residents;
}
我们可以发现这里通过了filterDistrictOfCurrentUserAccess
方法获取了当前登陆人员的区域ID,并把它加入到综合查询条件中。
其中对于综合查询中查询属于某个地区的居民是这样做的。
public static Specification 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 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);
}
};
}
看起来很多其实逻辑很简单,先是判断传入的区域信息是否完整,若不完整则返回null。
之后因为查询的最小单位是楼所以直接构造了一个从building起始的Join对象
Join buildingJoin = root.join("houses")
.join("building", JoinType.LEFT);
其中的root代表的就是resident,之后进入houses属性再进入building属性。
criteriaBuilder.equal(buildingJoin.get("id").as(Long.class), districtId);
再根据传入的districtId查询等于buildingId的resident;
之后我们要做的就是根据传入的district.getType()进行分类讨论即如果是小区的话那么就在此building基础上获取parent再进行查询。
之后我们再来看一下在实际项目中是如何获取当前登陆用户并获取其区域ID的。
先是过滤一下传入的district,如果传入了districtId,则看当前登录用户是否拥有传入的区域ID的管理权限。有权限,返回对应区域;无权限,返回当前登录用户所在区域;如果未传入districtId直接返回登录用户所在区域。
在此情况下我们先讨论如何获取当前登陆用户。
public Optional getCurrentLoginWebUser() {
return this.webUserRepository.findById(this.getCurrentLoginWebUserId()
.orElseThrow(() -> new AccessDeniedException("当前登录类型不正确或未登录")));
}
/**
获取当前登陆用户ID
**/
public Optional 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 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();
}
根据上述代码我们可以发现SpringBoot已经对登陆进行了一部分的封装,我们在登陆后只需下面这部分代码就可获取当前登陆用户的信息
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();