推送订阅添加批量删除功能

This commit is contained in:
chenxing
2026-08-20 09:31:50 +08:00
parent d472902957
commit 99414e2887
25 changed files with 375 additions and 265 deletions
@@ -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();
}
}