package com.yunhai.userbase.controller.oapi; 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.service.IGroupAuthzService; import com.yunhai.userbase.service.UserCenterService; import org.springframework.beans.BeanUtils; 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 java.util.Collections; import java.util.List; /** * 用户中心开放接口:供其他业务服务经 yh-app-auth 签名调用({@code /oapi/user/...})。
*
* 路径复用 {@link Config#OPEN_API_PATH} 统一前缀,与基础平台 {@code DefaultUserCenterClient} * 硬编码的契约严格一致: * {@code GET /oapi/user/byId?id=}、{@code /byLoginName?loginName=}、{@code /byPhone?phone=}、 * {@code /oapi/user/modules?userId=&schId=}。
* 接口本身不做验签——签名由 {@code OpenApiAuthEnforceConfiguration} 在过滤器层强制要求, * 未带 {@code X-App-Key/X-Timestamp/X-Signature} 的请求直接 401。 */ @RestController @RequestMapping(Config.OPEN_API_PATH + "user/") public class UserController { @Autowired private UserCenterService userCenterService; @Autowired private IGroupAuthzService groupAuthzService; @GetMapping("byId") public BoaUser byId(@RequestParam("id") Integer id) { return userCenterService.getUserInfoWithDeptById(id); } @GetMapping("byLoginName") public BoaUser byLoginName(@RequestParam("loginName") String loginName) { if (loginName == null || loginName.trim().isEmpty()) { return null; } return userCenterService.getUserInfoWithDeptByAccount(loginName); } @GetMapping("byPhone") public BoaUser byPhone(@RequestParam("phone") String phone) { if (phone == null || phone.trim().isEmpty()) { return null; } return userCenterService.getUserInfoWithDeptByPhone(phone); } /** * 查询用户在指定学校下的已授权模块列表。
* 若 {@code schId} 为 null 时,自动取用户的主学校;若仍无主学校信息,退化为取用户主归属组(groups 第 0 个)。 * * @param userId 用户ID(必填) * @param schId 学校ID/组ID(可选;为 null 时按上述规则推断) * @return 扁平化的已授权模块列表(含基础平台统一 BoaModule 模型);无权限时返回空列表 */ @GetMapping("modules") public List modules(@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 tree = groupAuthzService.findUserPermissionTree(effectiveGroupId, userId); if (tree == null || tree.isEmpty()) { return Collections.emptyList(); } List 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; } /** * 递归收集已授权模块树到扁平列表,并把应用 PC 图标作为模块的 icon 兜底(BoaModule.icon)。 */ private void collectModules(List modules, String appIcon, List collector) { if (modules == null || modules.isEmpty()) { return; } for (UserPermissionModuleVo m : modules) { BoaModule bm = new BoaModule(); BeanUtils.copyProperties(m, bm); bm.setIcon(appIcon); collector.add(bm); collectModules(m.getChildren(), appIcon, collector); } } /** * 按「schId 为空时」解析出用户主学校/主组作为模块查询的上下文组ID。 */ private Integer resolveEffectiveGroupId(Integer userId, Integer schId) { if (schId != null) { return schId; } BoaUser boa = userCenterService.getUserInfoWithDeptById(userId); if (boa == null) { return null; } BoaGroup school = boa.getSchool(); if (school != null && school.getId() != null) { return school.getId(); } List groups = boa.getGroups(); if (groups != null && !groups.isEmpty()) { BoaGroup main = groups.get(0); return main == null ? null : main.getId(); } return null; } }