This commit is contained in:
@@ -1,22 +1,24 @@
|
||||
package com.yunhai.userbase.controller.oapi;
|
||||
|
||||
import com.yunhai.platform.appauth.AppAuthFilter;
|
||||
import com.yunhai.platform.utils.exception.DException;
|
||||
import com.yunhai.platform.utils.model.BoaGroup;
|
||||
import com.yunhai.platform.utils.model.BoaModule;
|
||||
import com.yunhai.platform.utils.model.BoaUser;
|
||||
import com.yunhai.userbase.config.Config;
|
||||
import com.yunhai.userbase.model.groupauthz.UserPermissionAppVo;
|
||||
import com.yunhai.userbase.model.groupauthz.UserPermissionCategoryVo;
|
||||
import com.yunhai.userbase.model.groupauthz.UserPermissionModuleVo;
|
||||
import com.yunhai.userbase.entity.ApplicationInfo;
|
||||
import com.yunhai.userbase.repository.ApplicationInfoRepository;
|
||||
import com.yunhai.userbase.service.IGroupAuthzService;
|
||||
import com.yunhai.userbase.service.impl.UserCenterService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -34,12 +36,17 @@ import java.util.List;
|
||||
@RequestMapping(Config.OPEN_API_PATH + "user/")
|
||||
public class UserController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(UserController.class);
|
||||
|
||||
@Autowired
|
||||
private UserCenterService userCenterService;
|
||||
|
||||
@Autowired
|
||||
private IGroupAuthzService groupAuthzService;
|
||||
|
||||
@Autowired
|
||||
private ApplicationInfoRepository applicationInfoRepository;
|
||||
|
||||
@GetMapping("byId")
|
||||
public BoaUser byId(@RequestParam("id") Integer id) {
|
||||
return userCenterService.getUserInfoWithDeptById(id);
|
||||
@@ -62,60 +69,72 @@ public class UserController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户在指定学校下的已授权模块列表。<br>
|
||||
* 若 {@code schId} 为 null 时,自动取用户的主学校;若仍无主学校信息,退化为取用户主归属组(groups 第 0 个)。
|
||||
* 查询用户在<b>调用方应用</b>下、指定学校(上下文)里的已授权模块列表。<br>
|
||||
* <br>
|
||||
* 与管理端「权限一览」返回<b>全应用聚合</b>不同,本方法严格按调用方应用维度过滤:<br>
|
||||
* ① 通过 {@link AppAuthFilter#APP_AUTH_ATTR} 拿到验签时的 appKey(RequireAppSignFilter
|
||||
* 已保证非空);<br>
|
||||
* ② 查 application_info 映射到具体 appId;<br>
|
||||
* ③ 调 Service 专用方法:所有 SQL/集合构建均限定在该 appId 范围内(数据库层面过滤)。<br>
|
||||
* <br>
|
||||
* 边界:调用方 appKey 查不到对应应用 → 直接返回空列表(安全优先,禁止意外返回其他应用的模块)。<br>
|
||||
*
|
||||
* @param userId 用户ID(必填)
|
||||
* @param schId 学校ID/组ID(可选;为 null 时按上述规则推断)
|
||||
* @return 扁平化的已授权模块列表(含基础平台统一 BoaModule 模型);无权限时返回空列表
|
||||
* @param request 用于取 AppAuthFilter 设置的 appKey
|
||||
* @param userId 用户ID(必填)
|
||||
* @param schId 学校ID/组ID(可选;为 null 时按"主学校→主归属组"规则推断)
|
||||
* @return 该应用下用户已授权模块(扁平化;无权限时返回空列表)
|
||||
*/
|
||||
@GetMapping("modules")
|
||||
public List<BoaModule> modules(@RequestParam("userId") Integer userId,
|
||||
public List<BoaModule> modules(HttpServletRequest request,
|
||||
@RequestParam("userId") Integer userId,
|
||||
@RequestParam(value = "schId", required = false) Integer schId) {
|
||||
if (userId == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Integer effectiveGroupId = resolveEffectiveGroupId(userId, schId);
|
||||
// 注意:超管即使没有学校/归属组,service.findUserPermissionTree 内部也会走超管短路返回全部权限,
|
||||
// 因此这里不能因 effectiveGroupId 为 null 直接返回空,否则超管在 oapi/user/modules 永远拿不到权限;
|
||||
// 普通用户无组/无学校时,findUserPermissionTree 同样会返回空列表,行为一致。
|
||||
List<UserPermissionCategoryVo> tree = groupAuthzService.findUserPermissionTree(effectiveGroupId, userId);
|
||||
if (tree == null || tree.isEmpty()) {
|
||||
Integer appId = resolveCallerAppId(request);
|
||||
if (appId == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<BoaModule> result = new ArrayList<>();
|
||||
for (UserPermissionCategoryVo category : tree) {
|
||||
if (category.getApps() == null)
|
||||
continue;
|
||||
for (UserPermissionAppVo appVo : category.getApps()) {
|
||||
String icon = appVo.getPcIcon();
|
||||
collectModules(appVo.getModules(), icon, result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return groupAuthzService.findUserModulesForApp(appId, schId, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归收集已授权模块树到扁平列表,并把应用 PC 图标作为模块的 icon 兜底(BoaModule.icon)。
|
||||
* 从请求 attribute 中解析调用方 appKey,并映射为 application_info 的主键 id。<br>
|
||||
* 映射失败返回 null(Controller 层统一返回空列表;异常只打 warn 日志,不给前端 500)。
|
||||
*/
|
||||
private void collectModules(List<UserPermissionModuleVo> modules,
|
||||
String appIcon,
|
||||
List<BoaModule> collector) {
|
||||
if (modules == null || modules.isEmpty()) {
|
||||
return;
|
||||
private Integer resolveCallerAppId(HttpServletRequest request) {
|
||||
Object attr = request.getAttribute(AppAuthFilter.APP_AUTH_ATTR);
|
||||
if (attr == null) {
|
||||
log.warn("[oapi:user:modules] no AppAuth attr (should be blocked by RequireAppSignFilter), uri={}",
|
||||
request.getRequestURI());
|
||||
return null;
|
||||
}
|
||||
for (UserPermissionModuleVo m : modules) {
|
||||
BoaModule bm = new BoaModule();
|
||||
BeanUtils.copyProperties(m, bm);
|
||||
bm.setIcon(appIcon);
|
||||
collector.add(bm);
|
||||
collectModules(m.getChildren(), appIcon, collector);
|
||||
String appKeyStr = (attr instanceof String) ? (String) attr : String.valueOf(attr);
|
||||
if (appKeyStr.trim().isEmpty()) {
|
||||
log.warn("[oapi:user:modules] AppAuth attr is blank, uri={}", request.getRequestURI());
|
||||
return null;
|
||||
}
|
||||
Integer appKey;
|
||||
try {
|
||||
appKey = Integer.parseInt(appKeyStr.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("[oapi:user:modules] invalid appKey format={}, uri={}", appKeyStr, request.getRequestURI());
|
||||
return null;
|
||||
}
|
||||
ApplicationInfo app = applicationInfoRepository.findByAppKey(appKey);
|
||||
if (app == null) {
|
||||
log.warn("[oapi:user:modules] appKey={} not found in application_info, uri={}", appKey,
|
||||
request.getRequestURI());
|
||||
return null;
|
||||
}
|
||||
return app.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 按「schId 为空时」解析出用户主学校/主组作为模块查询的上下文组ID。
|
||||
* 按「schId 为空时」解析出用户主学校/主组作为模块查询的上下文组ID。<br>
|
||||
* 保留方法(逻辑已下沉到 Service.resolveEffectiveGroupId 复用),不再被 modules() 使用。
|
||||
*/
|
||||
@Deprecated
|
||||
private Integer resolveEffectiveGroupId(Integer userId, Integer schId) {
|
||||
if (schId != null) {
|
||||
return schId;
|
||||
|
||||
Reference in New Issue
Block a user