This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
package com.yunhai.userbase.common.converter;
|
||||
|
||||
import com.yunhai.userbase.enums.ClientEnum;
|
||||
import com.yunhai.platform.utils.enums.ClientEnum;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.yunhai.userbase.common.converter;
|
||||
|
||||
import com.yunhai.userbase.enums.IEnum;
|
||||
import com.yunhai.platform.utils.enums.IEnum;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.yunhai.userbase.controller.api;
|
||||
|
||||
import com.yunhai.platform.utils.model.BoaUser;
|
||||
import com.yunhai.platform.utils.enums.ClientEnum;
|
||||
import com.yunhai.platform.utils.enums.IEnum;
|
||||
import com.yunhai.userbase.config.Config;
|
||||
import com.yunhai.userbase.enums.*;
|
||||
import com.yunhai.userbase.utils.*;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.yunhai.userbase.controller.oapi;
|
||||
|
||||
import com.yunhai.platform.utils.enums.ClientEnum;
|
||||
import com.yunhai.platform.utils.enums.IEnum;
|
||||
import com.yunhai.platform.utils.model.BoaAppType;
|
||||
import com.yunhai.userbase.config.Config;
|
||||
import com.yunhai.userbase.service.IAppCategoryService;
|
||||
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.List;
|
||||
|
||||
/**
|
||||
* 应用域开放接口:供其他业务服务经 yh-app-auth 签名调用({@code /oapi/app/...})。<br>
|
||||
* 接口本身不做验签——签名由 {@code OpenApiAuthEnforceConfiguration} 在过滤器层强制要求
|
||||
* (URL pattern {@code /oapi/*} 已覆盖本控制器),未带 {@code X-App-Key/X-Timestamp/X-Signature} 的请求直接 401。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(Config.OPEN_API_PATH + "app/")
|
||||
public class AppController {
|
||||
|
||||
@Autowired
|
||||
private IAppCategoryService appCategoryService;
|
||||
|
||||
/**
|
||||
* 查询用户在指定客户端类型下有权访问的应用列表(按应用类型分组)。<br>
|
||||
* 权限过滤复用 {@code IGroupAuthzService.findUserAuthorizedAppIds}(组开通 ∪ 启用角色模块所属应用),
|
||||
* 超级管理员返回全部;不透出 appSecret。<br>
|
||||
* clientType 传枚举 key(2 的幂值,如 1=PC 2=移动App 4=系统模块…);非法/缺失按不限制端处理。
|
||||
*
|
||||
* @param userId 用户ID(必填)
|
||||
* @param clientType 客户端类型 key(可选)
|
||||
* @return 应用类型分组列表;无权限时各类型 apps 为空数组
|
||||
*/
|
||||
@GetMapping("list")
|
||||
public List<BoaAppType> list(@RequestParam("userId") Integer userId,
|
||||
@RequestParam(value = "clientType", required = false) Integer clientType) {
|
||||
ClientEnum clientEnum = (clientType == null) ? null
|
||||
: IEnum.getByKey(ClientEnum.class, clientType);
|
||||
return appCategoryService.listBoaAppType(userId, clientEnum);
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,14 @@ 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.BoaRoleInfo;
|
||||
import com.yunhai.platform.utils.model.BoaUser;
|
||||
import com.yunhai.userbase.common.validation.ValidationGroups;
|
||||
import com.yunhai.userbase.config.Config;
|
||||
import com.yunhai.userbase.entity.ApplicationInfo;
|
||||
import com.yunhai.userbase.model.sysuser.SysUserGroupDeleteItem;
|
||||
import com.yunhai.userbase.model.sysuser.SysUserMoveRequest;
|
||||
import com.yunhai.userbase.model.sysuser.SysUserPasswordRequest;
|
||||
import com.yunhai.userbase.model.sysuser.SysUserRequest;
|
||||
import com.yunhai.userbase.repository.ApplicationInfoRepository;
|
||||
import com.yunhai.userbase.service.IGroupAuthzService;
|
||||
@@ -209,4 +211,22 @@ public class UserController {
|
||||
sysUserService.moveUserWithinSchool(request.getUserId(), request.getGroupId(), request.getSchoolId());
|
||||
return userCenterService.getUserInfoWithDeptById(request.getUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户的业务角色身份列表(角色 + 主归属学校组上下文)。<br>
|
||||
* 鉴权由 OpenApiAuthEnforceConfiguration 在过滤器层强制签名。
|
||||
*/
|
||||
@GetMapping("roles")
|
||||
public List<BoaRoleInfo> roles(@RequestParam("userId") Integer userId) {
|
||||
return sysUserService.findUserRoles(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户密码:先校验旧密码,再写入新密码(BCrypt)。<br>
|
||||
* 鉴权由 OpenApiAuthEnforceConfiguration 在过滤器层强制签名;此处不记操作日志(避免明文密码写入 request_params)。
|
||||
*/
|
||||
@PostMapping("password")
|
||||
public void password(@RequestBody @Validated SysUserPasswordRequest request) {
|
||||
sysUserService.changePassword(request.getUserId(), request.getOldPassword(), request.getNewPassword());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package com.yunhai.userbase.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author chenx
|
||||
* @date 2026/07/13
|
||||
* @description 客户端来源
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ClientEnum implements IEnum<Integer> {
|
||||
|
||||
PC_BROWSER(1, "Pc浏览器"),
|
||||
MOBILE_APP(2, "移动App"),
|
||||
SYSTEM_MODULE(4, "系统模块"),
|
||||
WECHAT_OFFICIAL(8, "微信公众号"),
|
||||
WORK_WECHAT(16, "企业微信"),
|
||||
DING_TALK(32, "钉钉"),
|
||||
STUDENT_DEVICE(64, "学伴机");
|
||||
|
||||
private Integer key;
|
||||
private String value;
|
||||
|
||||
@Override
|
||||
public Integer getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.yunhai.userbase.enums;
|
||||
|
||||
import com.yunhai.platform.utils.enums.IEnum;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.yunhai.userbase.enums;
|
||||
|
||||
import com.yunhai.platform.utils.enums.IEnum;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
package com.yunhai.userbase.enums;
|
||||
|
||||
import com.yunhai.userbase.utils.EnumUtils;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 统一枚举规范接口,仅定义契约getKey/getValue
|
||||
* 内置静态工具方法,业务直接 IEnum.xxx(枚举类) 调用
|
||||
* @param <T> key类型
|
||||
*/
|
||||
public interface IEnum<T> {
|
||||
|
||||
/** 获取编码key */
|
||||
T getKey();
|
||||
|
||||
/** 获取中文展示value */
|
||||
String getValue();
|
||||
|
||||
// ====================== 静态工具入口(统一对外调用) ======================
|
||||
/** 获取枚举全部实例列表 */
|
||||
static <E extends Enum<E> & IEnum<?>> List<E> toList(Class<E> clazz) {
|
||||
return EnumUtils.toList(clazz);
|
||||
}
|
||||
|
||||
/** 根据key获取枚举,无匹配返回null */
|
||||
static <E extends Enum<E> & IEnum<?>> E getByKey(Class<E> clazz, Object key) {
|
||||
return EnumUtils.getEnumByKey(clazz, key);
|
||||
}
|
||||
|
||||
/** 根据key严格获取,不存在抛异常(对应fromKey) */
|
||||
static <E extends Enum<E> & IEnum<?>> E fromKey(Class<E> clazz, Object key) {
|
||||
return EnumUtils.fromKey(clazz, key);
|
||||
}
|
||||
|
||||
/** 根据key获取中文value,无匹配返回空串 */
|
||||
static <E extends Enum<E> & IEnum<?>> String getValueByKey(Class<E> clazz, Object key) {
|
||||
return EnumUtils.getValue(clazz, key);
|
||||
}
|
||||
|
||||
/** 根据中文value获取key字符串,无匹配返回空串 */
|
||||
static <E extends Enum<E> & IEnum<?>> String getKeyByValue(Class<E> clazz, String value) {
|
||||
return EnumUtils.getKey(clazz, value);
|
||||
}
|
||||
|
||||
/** 判断key是否存在 */
|
||||
static <E extends Enum<E> & IEnum<?>> boolean existsKey(Class<E> clazz, Object key) {
|
||||
return EnumUtils.isExistKey(clazz, key);
|
||||
}
|
||||
|
||||
/** 判断value是否存在 */
|
||||
static <E extends Enum<E> & IEnum<?>> boolean existsValue(Class<E> clazz, String value) {
|
||||
return EnumUtils.isExistValue(clazz, value);
|
||||
}
|
||||
|
||||
/** 转完整Map列表(包含enum实例、name、key、value) */
|
||||
static <E extends Enum<E> & IEnum<?>> List<Map<String, Object>> toFullMapList(Class<E> clazz) {
|
||||
return EnumUtils.toMapList(clazz);
|
||||
}
|
||||
|
||||
/** 转下拉简易Map列表,自定义key/value字段名 */
|
||||
static <E extends Enum<E> & IEnum<?>> List<Map<String, Object>> toSimpleMapList(Class<E> clazz, String keyMapName, String valueMapName) {
|
||||
return EnumUtils.toMapList(clazz, keyMapName, valueMapName);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.yunhai.userbase.enums;
|
||||
|
||||
import com.yunhai.platform.utils.enums.IEnum;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.yunhai.userbase.enums;
|
||||
|
||||
import com.yunhai.platform.utils.enums.IEnum;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.yunhai.userbase.enums;
|
||||
|
||||
import com.yunhai.platform.utils.enums.IEnum;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.yunhai.userbase.enums;
|
||||
|
||||
import com.yunhai.platform.utils.enums.IEnum;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.yunhai.userbase.enums;
|
||||
|
||||
import com.yunhai.platform.utils.enums.IEnum;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.yunhai.userbase.model.other;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @program: UserProfile
|
||||
* @description: 用户当前角色信息
|
||||
* @author: chenx
|
||||
* @create: 2024-07-09 17:30
|
||||
**/
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class RoleInfoModel {
|
||||
|
||||
private Integer roleId;
|
||||
private String roleName;
|
||||
private Integer schId;
|
||||
private String schName;
|
||||
private Integer childId;
|
||||
private String childName;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.yunhai.userbase.model.sysuser;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 修改用户密码请求(oapi 开放接口专用)。
|
||||
*
|
||||
* @author chenx
|
||||
*/
|
||||
@Data
|
||||
public class SysUserPasswordRequest {
|
||||
|
||||
/**
|
||||
* 用户ID(必填)。
|
||||
*/
|
||||
@NotNull
|
||||
private Integer userId;
|
||||
|
||||
/**
|
||||
* 旧密码(必填,须与库中 BCrypt 密文匹配)。
|
||||
*/
|
||||
@NotBlank
|
||||
private String oldPassword;
|
||||
|
||||
/**
|
||||
* 新密码(必填,走弱密码规则校验后 BCrypt 加密写入)。
|
||||
*/
|
||||
@NotBlank
|
||||
private String newPassword;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.yunhai.userbase.service;
|
||||
|
||||
import com.yunhai.platform.utils.enums.ClientEnum;
|
||||
import com.yunhai.platform.utils.model.BoaAppType;
|
||||
import com.yunhai.userbase.entity.AppCategory;
|
||||
import com.yunhai.userbase.model.appauthz.AppAuthzCategoryVo;
|
||||
import com.yunhai.userbase.model.appcategory.AppCategoryQuery;
|
||||
@@ -33,4 +35,18 @@ public interface IAppCategoryService {
|
||||
*/
|
||||
List<AppAuthzCategoryVo> listWithApps();
|
||||
|
||||
/**
|
||||
* 开放接口专用:按用户 + 客户端类型查询其有权访问的应用列表(按应用类型分组)。<br>
|
||||
* 规则:<br>
|
||||
* ① 权限过滤:仅返回该用户有权访问的应用(复用 {@code IGroupAuthzService.findUserAuthorizedAppIds});<br>
|
||||
* ② 客户端过滤:仅返回支持该客户端类型的应用({@code app.clients} 位掩码与 clientType 有交集;clients 为空视为不限端);<br>
|
||||
* ③ 不透出 appSecret。<br>
|
||||
* 类型按 sortNo 升序,类型内应用按 sortNo、id 升序;无有权应用的类型 apps 为空数组。
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param clientType 客户端类型(null=不按端过滤)
|
||||
* @return 应用类型 + 有权应用列表
|
||||
*/
|
||||
List<BoaAppType> listBoaAppType(Integer userId, ClientEnum clientType);
|
||||
|
||||
}
|
||||
|
||||
@@ -176,4 +176,15 @@ public interface IGroupAuthzService {
|
||||
* @param items 模块授权条目列表(groupId/appId/moduleId/mode/isAll)
|
||||
*/
|
||||
void setGroupModulePerms(List<GroupModuleAuthzItem> items);
|
||||
|
||||
/**
|
||||
* 查询用户有权访问的应用ID集合(oapi 开放接口 getUserApp 权限过滤用)。<br>
|
||||
* 规则与 {@link #hasModulePermission(Integer, Integer)} 对齐:<br>
|
||||
* - 超级管理员:全部应用;<br>
|
||||
* - 普通用户:用户所属组(沿祖先链)开通的应用 ∪ 启用角色模块所属应用。<br>
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 有权应用ID列表(无则空列表)
|
||||
*/
|
||||
List<Integer> findUserAuthorizedAppIds(Integer userId);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.yunhai.userbase.service;
|
||||
|
||||
import com.yunhai.platform.utils.model.BoaRoleInfo;
|
||||
import com.yunhai.userbase.model.sysuser.BatchImportResult;
|
||||
import com.yunhai.userbase.model.sysuser.SysUserGroupDeleteItem;
|
||||
import com.yunhai.userbase.model.sysuser.SysUserQuery;
|
||||
@@ -160,4 +161,22 @@ public interface ISysUserService {
|
||||
* @param response 直接写入 Excel 流
|
||||
*/
|
||||
void exportUsers(Integer groupId, Integer withSub, String name, HttpServletResponse response) throws IOException;
|
||||
|
||||
/**
|
||||
* 查询用户的业务角色身份列表(oapi 开放接口专用)。
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 角色身份列表(roleId/roleName + 主归属学校组上下文)
|
||||
*/
|
||||
List<BoaRoleInfo> findUserRoles(Integer userId);
|
||||
|
||||
/**
|
||||
* 修改用户密码:先校验旧密码,再写入新密码(BCrypt)。<br>
|
||||
* oapi 开放接口专用;不加操作日志(避免明文密码入日志)。
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param oldPassword 旧密码(须与库中 BCrypt 密文匹配)
|
||||
* @param newPassword 新密码(弱密码规则校验后加密写入)
|
||||
*/
|
||||
void changePassword(Integer userId, String oldPassword, String newPassword);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package com.yunhai.userbase.service.impl;
|
||||
|
||||
|
||||
import com.yunhai.platform.utils.enums.ClientEnum;
|
||||
import com.yunhai.platform.utils.exception.DException;
|
||||
import com.yunhai.platform.utils.model.BoaApp;
|
||||
import com.yunhai.platform.utils.model.BoaAppType;
|
||||
import com.yunhai.userbase.entity.AppCategory;
|
||||
import com.yunhai.userbase.entity.ApplicationInfo;
|
||||
import com.yunhai.userbase.model.appauthz.AppAuthzAppRow;
|
||||
import com.yunhai.userbase.model.appauthz.AppAuthzCategoryVo;
|
||||
import com.yunhai.userbase.model.appcategory.AppCategoryQuery;
|
||||
@@ -11,6 +15,7 @@ import com.yunhai.userbase.model.appcategory.AppCategoryVo;
|
||||
import com.yunhai.userbase.repository.AppCategoryRepository;
|
||||
import com.yunhai.userbase.repository.ApplicationInfoRepository;
|
||||
import com.yunhai.userbase.service.IAppCategoryService;
|
||||
import com.yunhai.userbase.service.IGroupAuthzService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -18,9 +23,14 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -36,6 +46,9 @@ public class AppCategoryService implements IAppCategoryService {
|
||||
@Autowired
|
||||
private ApplicationInfoRepository applicationInfoRepository;
|
||||
|
||||
@Autowired
|
||||
private IGroupAuthzService groupAuthzService;
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@@ -133,6 +146,97 @@ public class AppCategoryService implements IAppCategoryService {
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BoaAppType> listBoaAppType(Integer userId, ClientEnum clientType) {
|
||||
Set<Integer> authorizedAppIds = new HashSet<>(groupAuthzService.findUserAuthorizedAppIds(userId));
|
||||
List<AppCategory> categories = appCategoryRepository.findAllByOrderBySortNoAsc();
|
||||
List<ApplicationInfo> apps = applicationInfoRepository.findAll();
|
||||
apps.sort(Comparator.comparing(ApplicationInfo::getSortNo, Comparator.nullsLast(Integer::compareTo))
|
||||
.thenComparing(ApplicationInfo::getId, Comparator.nullsLast(Integer::compareTo)));
|
||||
// categoryId -> 该类型下应用列表(插入顺序即排序)
|
||||
Map<Integer, List<ApplicationInfo>> appMap = new LinkedHashMap<>();
|
||||
for (ApplicationInfo app : apps) {
|
||||
if (app.getCategoryId() == null) {
|
||||
continue;
|
||||
}
|
||||
appMap.computeIfAbsent(app.getCategoryId(), k -> new ArrayList<>()).add(app);
|
||||
}
|
||||
List<BoaAppType> result = new ArrayList<>();
|
||||
for (AppCategory category : categories) {
|
||||
BoaAppType type = new BoaAppType();
|
||||
type.setId(category.getId());
|
||||
type.setName(category.getName());
|
||||
List<BoaApp> boaApps = new ArrayList<>();
|
||||
for (ApplicationInfo app : appMap.getOrDefault(category.getId(), Collections.emptyList())) {
|
||||
// 用户权限过滤
|
||||
if (!authorizedAppIds.contains(app.getId())) {
|
||||
continue;
|
||||
}
|
||||
// 客户端类型位掩码过滤:clients 为空视为不限端
|
||||
if (clientType != null && !containsClient(app.getClients(), clientType.getKey())) {
|
||||
continue;
|
||||
}
|
||||
boaApps.add(convertToBoaApp(app));
|
||||
}
|
||||
type.setApps(boaApps);
|
||||
result.add(type);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* ApplicationInfo -> BoaApp 映射。不透出 appSecret;模块列表留空(由调用方按需填充)。
|
||||
*/
|
||||
private BoaApp convertToBoaApp(ApplicationInfo app) {
|
||||
BoaApp boa = new BoaApp();
|
||||
boa.setId(app.getId());
|
||||
boa.setName(app.getName());
|
||||
boa.setIcon(app.getPcIcon());
|
||||
boa.setType(app.getCategoryId());
|
||||
boa.setWebAddr(app.getPcUrl());
|
||||
boa.setWebAddress(app.getPcUrl());
|
||||
boa.setAppKey(app.getAppKey());
|
||||
boa.setOrderNumber(app.getSortNo());
|
||||
boa.setCreateUser(app.getCreateUser());
|
||||
boa.setCreateTime(app.getCreateTime());
|
||||
boa.setMobiIcon(app.getAppIcon());
|
||||
boa.setClients(toClientMask(app.getClients()));
|
||||
boa.setWxgzhAddress(app.getOfficialAccountUrl());
|
||||
boa.setDingAddress(app.getDingtalkUrl());
|
||||
return boa;
|
||||
}
|
||||
|
||||
/**
|
||||
* clients 列表([1,2,4])折叠为位掩码叠加值(7)。
|
||||
*/
|
||||
private Integer toClientMask(List<Integer> clients) {
|
||||
if (clients == null || clients.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
int mask = 0;
|
||||
for (Integer c : clients) {
|
||||
if (c != null) {
|
||||
mask |= c;
|
||||
}
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断客户端类型位掩码是否与 app.clients 有交集;clients 为空视为不限端。
|
||||
*/
|
||||
private boolean containsClient(List<Integer> clients, Integer clientKey) {
|
||||
if (clients == null || clients.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
for (Integer c : clients) {
|
||||
if (c != null && (c & clientKey) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.yunhai.platform.utils.model.BoaModule;
|
||||
import com.yunhai.platform.utils.model.BoaUser;
|
||||
import com.yunhai.userbase.entity.*;
|
||||
import com.yunhai.userbase.enums.GroupTypeEnum;
|
||||
import com.yunhai.userbase.enums.IEnum;
|
||||
import com.yunhai.platform.utils.enums.IEnum;
|
||||
import com.yunhai.userbase.enums.SysModeEnum;
|
||||
import com.yunhai.userbase.enums.UserTypeEnum;
|
||||
import com.yunhai.userbase.model.groupauthz.GroupAppAssignRequest;
|
||||
@@ -1599,4 +1599,40 @@ public class GroupAuthzService implements IGroupAuthzService {
|
||||
}
|
||||
return moduleIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Integer> findUserAuthorizedAppIds(Integer userId) {
|
||||
Objects.requireNonNull(userId, "用户ID不能为空");
|
||||
SysUser user = sysUserRepository.findById(userId).orElse(null);
|
||||
if (user == null) {
|
||||
throw new DException("用户不存在");
|
||||
}
|
||||
// 超级管理员:全部应用
|
||||
if (isSuperAdmin(user.getType(), userId)) {
|
||||
List<Integer> allIds = new ArrayList<>();
|
||||
for (ApplicationInfo app : applicationInfoRepository.findAll()) {
|
||||
allIds.add(app.getId());
|
||||
}
|
||||
return allIds;
|
||||
}
|
||||
Set<Integer> appIds = new LinkedHashSet<>();
|
||||
// 1. 组开通应用(用户所属组沿祖先链的开通池并集)
|
||||
List<SysUserGroupRel> userRels = sysUserGroupRelRepository.findByUserId(userId);
|
||||
for (SysUserGroupRel rel : userRels) {
|
||||
if (rel.getGroupId() == null) {
|
||||
continue;
|
||||
}
|
||||
appIds.addAll(collectAppIdsForAuthzScope(rel.getGroupId()));
|
||||
}
|
||||
// 2. 启用角色模块所属应用(角色授权不要求组先开通应用)
|
||||
Set<Integer> roleModuleIds = collectEnabledRoleModuleIds(userId);
|
||||
if (!roleModuleIds.isEmpty()) {
|
||||
for (AppModule m : appModuleRepository.findAllById(roleModuleIds)) {
|
||||
if (m.getAppId() != null) {
|
||||
appIds.add(m.getAppId());
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(appIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.yunhai.platform.utils.exception.DException;
|
||||
import com.yunhai.userbase.entity.SysGroup;
|
||||
import com.yunhai.userbase.entity.SysUserCurrentRole;
|
||||
import com.yunhai.userbase.entity.SysUserRoleRel;
|
||||
import com.yunhai.userbase.enums.IEnum;
|
||||
import com.yunhai.platform.utils.enums.IEnum;
|
||||
import com.yunhai.userbase.enums.StatusEnum;
|
||||
import com.yunhai.userbase.enums.SysRoleEnum;
|
||||
import com.yunhai.userbase.model.identity.IdentityCurrentVo;
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.yunhai.userbase.entity.SysGroup;
|
||||
import com.yunhai.platform.utils.exception.DException;
|
||||
import com.yunhai.platform.utils.model.BoaGroup;
|
||||
import com.yunhai.userbase.enums.GroupTypeEnum;
|
||||
import com.yunhai.userbase.enums.IEnum;
|
||||
import com.yunhai.platform.utils.enums.IEnum;
|
||||
import com.yunhai.userbase.event.DataSyncEvent;
|
||||
import com.yunhai.userbase.common.context.CallerAppContext;
|
||||
import com.yunhai.userbase.model.push.PushInterfaceType;
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
package com.yunhai.userbase.service.impl;
|
||||
|
||||
import com.yunhai.platform.utils.exception.DException;
|
||||
import com.yunhai.platform.utils.model.BoaRoleInfo;
|
||||
import com.yunhai.userbase.entity.SysAppRole;
|
||||
import com.yunhai.userbase.entity.SysGroup;
|
||||
import com.yunhai.userbase.enums.IEnum;
|
||||
import com.yunhai.platform.utils.enums.IEnum;
|
||||
import com.yunhai.userbase.enums.StatusEnum;
|
||||
import com.yunhai.userbase.enums.SysRoleEnum;
|
||||
import com.yunhai.userbase.enums.UserTypeEnum;
|
||||
import com.yunhai.userbase.entity.SysUser;
|
||||
import com.yunhai.userbase.entity.SysUserAppRoleRel;
|
||||
import com.yunhai.userbase.entity.SysUserGroupRel;
|
||||
import com.yunhai.userbase.model.sysuser.BatchImportResult;
|
||||
import com.yunhai.userbase.model.sysuser.SysUserGroupDeleteItem;
|
||||
import com.yunhai.userbase.model.sysuser.SysUserQuery;
|
||||
import com.yunhai.userbase.model.sysuser.SysUserRequest;
|
||||
import com.yunhai.userbase.model.sysuser.SysUserVo;
|
||||
import com.yunhai.userbase.repository.SysAppRoleRepository;
|
||||
import com.yunhai.userbase.repository.SysGroupRepository;
|
||||
import com.yunhai.userbase.repository.SysUserAppRoleRelRepository;
|
||||
import com.yunhai.userbase.repository.SysUserGroupRelRepository;
|
||||
@@ -96,6 +100,9 @@ public class SysUserService implements ISysUserService {
|
||||
@Autowired
|
||||
private ISysGroupService sysGroupService;
|
||||
|
||||
@Autowired
|
||||
private SysAppRoleRepository sysAppRoleRepository;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@@ -799,6 +806,68 @@ public class SysUserService implements ISysUserService {
|
||||
return single;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BoaRoleInfo> findUserRoles(Integer userId) {
|
||||
if (userId == null) {
|
||||
throw new DException("用户ID不能为空");
|
||||
}
|
||||
List<BoaRoleInfo> result = new ArrayList<>();
|
||||
// 主归属组(学校/部门)上下文:schId/schName
|
||||
Integer mainGroupId = null;
|
||||
String mainGroupName = null;
|
||||
List<SysUserGroupRel> groupRels = sysUserGroupRelRepository.findByUserId(userId);
|
||||
for (SysUserGroupRel gr : groupRels) {
|
||||
if (gr.getIsMain() != null && gr.getIsMain() == IS_MAIN) {
|
||||
mainGroupId = gr.getGroupId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mainGroupId != null) {
|
||||
SysGroup mainGroup = sysGroupRepository.findById(mainGroupId).orElse(null);
|
||||
if (mainGroup != null) {
|
||||
mainGroupName = mainGroup.getName();
|
||||
}
|
||||
}
|
||||
// 业务角色:用户-角色关系 → 角色名称
|
||||
List<SysUserAppRoleRel> rels = sysUserAppRoleRelRepository.findByUserId(userId);
|
||||
for (SysUserAppRoleRel rel : rels) {
|
||||
if (rel.getRoleId() == null) {
|
||||
continue;
|
||||
}
|
||||
SysAppRole role = sysAppRoleRepository.findById(rel.getRoleId()).orElse(null);
|
||||
if (role == null) {
|
||||
continue;
|
||||
}
|
||||
BoaRoleInfo info = new BoaRoleInfo();
|
||||
info.setRoleId(role.getId());
|
||||
info.setRoleName(role.getName());
|
||||
info.setSchId(mainGroupId);
|
||||
info.setSchName(mainGroupName);
|
||||
result.add(info);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void changePassword(Integer userId, String oldPassword, String newPassword) {
|
||||
SysUser entity = sysUserRepository.findById(userId)
|
||||
.orElseThrow(() -> new DException("系统用户不存在"));
|
||||
if (StringUtils.isBlank(oldPassword) || StringUtils.isBlank(newPassword)) {
|
||||
throw new DException("新旧密码不能为空");
|
||||
}
|
||||
if (!passwordEncoder.matches(oldPassword, entity.getPassword())) {
|
||||
throw new DException("原密码不正确");
|
||||
}
|
||||
String weakReason = ValidatorUtil.checkPasswordStrength(newPassword);
|
||||
if (weakReason != null) {
|
||||
throw new DException(weakReason);
|
||||
}
|
||||
entity.setPassword(passwordEncoder.encode(newPassword));
|
||||
entity.setUpdateUser(SYSTEM_USER_ID);
|
||||
sysUserRepository.save(entity);
|
||||
}
|
||||
|
||||
/** 校验手机号与身份证(有值才校验)。 */
|
||||
private void validateProfile(SysUserRequest request) {
|
||||
if (StringUtils.isNotBlank(request.getPhone()) && !ValidatorUtil.isPhone(request.getPhone())) {
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
package com.yunhai.userbase.utils;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yunhai.userbase.enums.IEnum;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 枚举底层工具实现,适配 Hutool 5.8.36
|
||||
* 对外统一通过 IEnum.xxx() 静态调用,不直接使用本类
|
||||
*/
|
||||
public final class EnumUtils {
|
||||
|
||||
private EnumUtils() {
|
||||
throw new AssertionError("工具类禁止实例化");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部枚举实例(依赖Hutool5.8.36支持的EnumUtil.toList)
|
||||
*/
|
||||
public static <T extends Enum<T> & IEnum<?>> List<T> toList(Class<T> targetEnumClazz) {
|
||||
return new ArrayList<>(Arrays.asList(targetEnumClazz.getEnumConstants()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 枚举转完整Map列表(包含enum、name、key、value)
|
||||
*/
|
||||
public static <T extends Enum<T> & IEnum<?>> List<Map<String, Object>> toMapList(Class<T> targetEnumClazz) {
|
||||
List<T> enumList = toList(targetEnumClazz);
|
||||
List<Map<String, Object>> resultList = new ArrayList<>(enumList.size());
|
||||
|
||||
for (T enumItem : enumList) {
|
||||
Map<String, Object> map = new HashMap<>(4);
|
||||
map.put("enum", enumItem);
|
||||
map.put("name", enumItem.name());
|
||||
map.put("key", enumItem.getKey());
|
||||
map.put("value", enumItem.getValue());
|
||||
resultList.add(map);
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过key获取对应value文本
|
||||
*/
|
||||
public static <T extends Enum<T> & IEnum<?>> String getValue(Class<T> targetEnumClazz, Object key) {
|
||||
if (key == null || StrUtil.isBlank(String.valueOf(key))) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
Optional<T> matchEnum = findByKey(targetEnumClazz, key);
|
||||
return matchEnum.map(IEnum::getValue).orElse(StrUtil.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过value文本获取对应key字符串
|
||||
*/
|
||||
public static <T extends Enum<T> & IEnum<?>> String getKey(Class<T> targetEnumClazz, String value) {
|
||||
if (StrUtil.isBlank(value)) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
Optional<T> matchEnum = findByValue(targetEnumClazz, value);
|
||||
return matchEnum.map(item -> String.valueOf(item.getKey())).orElse(StrUtil.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断key是否存在
|
||||
*/
|
||||
public static <T extends Enum<T> & IEnum<?>> boolean isExistKey(Class<T> targetEnumClazz, Object key) {
|
||||
return findByKey(targetEnumClazz, key).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断value是否存在
|
||||
*/
|
||||
public static <T extends Enum<T> & IEnum<?>> boolean isExistValue(Class<T> targetEnumClazz, String value) {
|
||||
return findByValue(targetEnumClazz, value).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key获取枚举实例,无匹配返回null
|
||||
*/
|
||||
public static <T extends Enum<T> & IEnum<?>> T getEnumByKey(Class<T> targetEnumClazz, Object key) {
|
||||
return findByKey(targetEnumClazz, key).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key严格获取枚举,不存在抛出异常(fromKey)
|
||||
*/
|
||||
public static <T extends Enum<T> & IEnum<?>> T fromKey(Class<T> targetEnumClazz, Object key) {
|
||||
T enumItem = getEnumByKey(targetEnumClazz, key);
|
||||
if (enumItem == null) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("枚举不存在,key=%s,枚举类=%s", key, targetEnumClazz.getName())
|
||||
);
|
||||
}
|
||||
return enumItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义返回字段名,生成下拉框简易列表
|
||||
*/
|
||||
public static <T extends Enum<T> & IEnum<?>> List<Map<String, Object>> toMapList(
|
||||
Class<T> targetEnumClazz, String keyMapName, String valueMapName
|
||||
) {
|
||||
List<T> enumList = toList(targetEnumClazz);
|
||||
List<Map<String, Object>> result = new ArrayList<>(enumList.size());
|
||||
for (T enumItem : enumList) {
|
||||
Map<String, Object> map = new HashMap<>(2);
|
||||
map.put(keyMapName, enumItem.getKey());
|
||||
map.put(valueMapName, enumItem.getValue());
|
||||
result.add(map);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// ====================== 私有底层查询方法(基于接口方法,无反射硬编码) ======================
|
||||
private static <T extends Enum<T> & IEnum<?>> Optional<T> findByKey(Class<T> clazz, Object key) {
|
||||
String targetKey = String.valueOf(key);
|
||||
return toList(clazz).stream()
|
||||
.filter(item -> targetKey.equals(String.valueOf(item.getKey())))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
private static <T extends Enum<T> & IEnum<?>> Optional<T> findByValue(Class<T> clazz, String value) {
|
||||
return toList(clazz).stream()
|
||||
.filter(item -> value.equals(item.getValue()))
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user