✨ 小程序用户支持组织与数据权限,调度端按项目业务部门收口运单
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+2
-1
@@ -71,7 +71,8 @@ public class MpUserController extends BladeController {
|
||||
@ApiOperationSupport(order = 1)
|
||||
@Operation(summary = "详情", description = "传入id")
|
||||
public R<MpUserVO> detail(MpUser mpUser) {
|
||||
MpUser detail = mpUserService.getOne(Condition.getQueryWrapper(mpUser));
|
||||
Long id = mpUser == null ? null : mpUser.getId();
|
||||
MpUser detail = id == null ? null : mpUserService.detail(id);
|
||||
if (detail == null) {
|
||||
return R.fail("小程序用户不存在");
|
||||
}
|
||||
|
||||
+8
@@ -28,10 +28,12 @@ import org.springblade.core.tenant.annotation.NonDS;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.system.pojo.entity.MpUser;
|
||||
import org.springblade.system.pojo.entity.MpUserInfo;
|
||||
import org.springblade.system.pojo.vo.MpUserDataScopeVO;
|
||||
import org.springblade.system.service.IMpUserService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
@@ -53,6 +55,12 @@ public class MpUserClient implements IMpUserClient {
|
||||
return R.data(mpUserService.getById(userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping(RESOLVE_DATA_SCOPE)
|
||||
public R<MpUserDataScopeVO> resolveDataScope(@RequestParam("userId") Long userId) {
|
||||
return R.data(mpUserService.resolveDataScope(userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping(USER_BY_PHONE)
|
||||
public R<MpUser> userByPhone(String tenantId, String phone) {
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.system.pojo.entity.MpUserDataScope;
|
||||
|
||||
/**
|
||||
* 小程序用户组织数据权限 Mapper
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface MpUserDataScopeMapper extends BaseMapper<MpUserDataScope> {
|
||||
}
|
||||
+13
@@ -26,6 +26,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springblade.core.mp.base.BaseService;
|
||||
import org.springblade.system.pojo.entity.MpUser;
|
||||
import org.springblade.system.pojo.entity.MpUserInfo;
|
||||
import org.springblade.system.pojo.vo.MpUserDataScopeVO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -67,6 +68,18 @@ public interface IMpUserService extends BaseService<MpUser> {
|
||||
*/
|
||||
IPage<MpUser> selectMpUserPage(IPage<MpUser> page, MpUser query);
|
||||
|
||||
/**
|
||||
* 详情(含自定义数据权限组织)
|
||||
*/
|
||||
MpUser detail(Long id);
|
||||
|
||||
/**
|
||||
* 解析小程序用户可见组织(所属组织 + 数据权限范围 + 数据层级范围)
|
||||
* <p>
|
||||
* allDept=true 表示全部组织;否则 deptIds 为可见组织,空列表表示无权限。
|
||||
*/
|
||||
MpUserDataScopeVO resolveDataScope(Long userId);
|
||||
|
||||
/**
|
||||
* 保存小程序用户
|
||||
*/
|
||||
|
||||
+152
-2
@@ -35,15 +35,20 @@ import org.springblade.core.tool.utils.DigestUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.system.cache.MpUserCache;
|
||||
import org.springblade.system.cache.SysCache;
|
||||
import org.springblade.system.mapper.MpUserDataScopeMapper;
|
||||
import org.springblade.system.mapper.MpUserMapper;
|
||||
import org.springblade.system.pojo.entity.MpUser;
|
||||
import org.springblade.system.pojo.entity.MpUserDataScope;
|
||||
import org.springblade.system.pojo.entity.MpUserInfo;
|
||||
import org.springblade.system.pojo.enums.MpUserIdentity;
|
||||
import org.springblade.system.pojo.vo.MpUserDataScopeVO;
|
||||
import org.springblade.system.service.IMpUserService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -57,6 +62,19 @@ import java.util.Objects;
|
||||
@RequiredArgsConstructor
|
||||
public class MpUserServiceImpl extends BaseServiceImpl<MpUserMapper, MpUser> implements IMpUserService {
|
||||
|
||||
/** 数据权限范围:仅所属组织 */
|
||||
private static final int DATA_SCOPE_OWN = 1;
|
||||
/** 数据权限范围:全部组织 */
|
||||
private static final int DATA_SCOPE_ALL = 2;
|
||||
/** 数据权限范围:自定义 */
|
||||
private static final int DATA_SCOPE_CUSTOM = 3;
|
||||
/** 数据层级范围:包含下级 */
|
||||
private static final int DATA_LEVEL_INCLUDE_CHILDREN = 1;
|
||||
/** 数据层级范围:仅本级 */
|
||||
private static final int DATA_LEVEL_CURRENT_ONLY = 2;
|
||||
|
||||
private final MpUserDataScopeMapper mpUserDataScopeMapper;
|
||||
|
||||
@Override
|
||||
public MpUser getByPhone(String tenantId, String phone) {
|
||||
if (Func.isBlank(tenantId) || Func.isBlank(phone)) {
|
||||
@@ -112,6 +130,64 @@ public class MpUserServiceImpl extends BaseServiceImpl<MpUserMapper, MpUser> imp
|
||||
.orderByDesc(MpUser::getCreateTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpUser detail(Long id) {
|
||||
if (Func.isEmpty(id)) {
|
||||
return null;
|
||||
}
|
||||
return fillScopes(getById(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpUserDataScopeVO resolveDataScope(Long userId) {
|
||||
if (Func.isEmpty(userId) || userId <= 0) {
|
||||
return MpUserDataScopeVO.empty();
|
||||
}
|
||||
MpUser user = getById(userId);
|
||||
if (user == null || Func.isEmpty(user.getId())) {
|
||||
return MpUserDataScopeVO.empty();
|
||||
}
|
||||
Integer dataScopeRange = user.getDataScopeRange() == null ? DATA_SCOPE_OWN : user.getDataScopeRange();
|
||||
Integer dataLevelRange = user.getDataLevelRange() == null ? DATA_LEVEL_INCLUDE_CHILDREN : user.getDataLevelRange();
|
||||
if (Objects.equals(dataScopeRange, DATA_SCOPE_ALL)) {
|
||||
return MpUserDataScopeVO.all();
|
||||
}
|
||||
List<Long> baseDeptIds;
|
||||
if (Objects.equals(dataScopeRange, DATA_SCOPE_CUSTOM)) {
|
||||
baseDeptIds = mpUserDataScopeMapper.selectList(Wrappers.<MpUserDataScope>lambdaQuery()
|
||||
.eq(MpUserDataScope::getUserId, userId))
|
||||
.stream()
|
||||
.map(MpUserDataScope::getDeptId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.toList();
|
||||
} else {
|
||||
// 仅所属组织
|
||||
baseDeptIds = Func.toLongList(user.getDeptId());
|
||||
}
|
||||
if (Func.isEmpty(baseDeptIds)) {
|
||||
return MpUserDataScopeVO.empty();
|
||||
}
|
||||
boolean includeChildren = !Objects.equals(dataLevelRange, DATA_LEVEL_CURRENT_ONLY);
|
||||
LinkedHashSet<Long> allowedDeptIds = new LinkedHashSet<>();
|
||||
for (Long deptId : baseDeptIds) {
|
||||
if (Func.isEmpty(deptId) || deptId <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (includeChildren) {
|
||||
List<Long> childIds = SysCache.getDeptChildIds(deptId);
|
||||
if (Func.isNotEmpty(childIds)) {
|
||||
allowedDeptIds.addAll(childIds);
|
||||
} else {
|
||||
allowedDeptIds.add(deptId);
|
||||
}
|
||||
} else {
|
||||
allowedDeptIds.add(deptId);
|
||||
}
|
||||
}
|
||||
return MpUserDataScopeVO.of(List.copyOf(allowedDeptIds));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean submit(MpUser user) {
|
||||
@@ -122,7 +198,7 @@ public class MpUserServiceImpl extends BaseServiceImpl<MpUserMapper, MpUser> imp
|
||||
throw new ServiceException("该手机号已存在小程序用户");
|
||||
}
|
||||
encryptPasswordIfPresent(user);
|
||||
boolean saved = save(user);
|
||||
boolean saved = save(user) && submitScopes(user);
|
||||
if (saved) {
|
||||
MpUserCache.clear();
|
||||
}
|
||||
@@ -135,7 +211,18 @@ public class MpUserServiceImpl extends BaseServiceImpl<MpUserMapper, MpUser> imp
|
||||
if (user == null || Func.isEmpty(user.getId())) {
|
||||
throw new ServiceException("小程序用户主键不能为空");
|
||||
}
|
||||
MpUser dbUser = getById(user.getId());
|
||||
if (dbUser == null || Func.isEmpty(dbUser.getId())) {
|
||||
throw new ServiceException("小程序用户不存在");
|
||||
}
|
||||
prepare(user);
|
||||
// 未改手机号(前端脱敏未提交 / 仍为脱敏值)时沿用库中原值,避免把 138****8000 写回
|
||||
if (Func.isBlank(user.getPhone()) || isMaskedPhone(user.getPhone())) {
|
||||
user.setPhone(dbUser.getPhone());
|
||||
}
|
||||
if (Func.isBlank(user.getEmail()) || isMaskedEmail(user.getEmail())) {
|
||||
user.setEmail(dbUser.getEmail());
|
||||
}
|
||||
validate(user);
|
||||
MpUser existed = getByPhone(user.getTenantId(), user.getPhone());
|
||||
if (existed != null && Func.isNotEmpty(existed.getId()) && !existed.getId().equals(user.getId())) {
|
||||
@@ -143,7 +230,7 @@ public class MpUserServiceImpl extends BaseServiceImpl<MpUserMapper, MpUser> imp
|
||||
}
|
||||
// 后台编辑不改密码,避免空串覆盖
|
||||
user.setPassword(null);
|
||||
boolean updated = updateById(user);
|
||||
boolean updated = updateById(user) && submitScopes(user);
|
||||
if (updated) {
|
||||
MpUserCache.clear();
|
||||
}
|
||||
@@ -167,6 +254,7 @@ public class MpUserServiceImpl extends BaseServiceImpl<MpUserMapper, MpUser> imp
|
||||
}
|
||||
boolean removed = deleteLogic(ids);
|
||||
if (removed) {
|
||||
mpUserDataScopeMapper.delete(Wrappers.<MpUserDataScope>lambdaQuery().in(MpUserDataScope::getUserId, ids));
|
||||
MpUserCache.clear();
|
||||
}
|
||||
return removed;
|
||||
@@ -314,6 +402,15 @@ public class MpUserServiceImpl extends BaseServiceImpl<MpUserMapper, MpUser> imp
|
||||
user.setPostId(trimToNull(user.getPostId()));
|
||||
user.setRemark(trimToNull(user.getRemark()));
|
||||
user.setIdentities(normalizeIdentities(user.getIdentities()));
|
||||
if (user.getDataScopeRange() == null) {
|
||||
user.setDataScopeRange(1);
|
||||
}
|
||||
if (user.getDataLevelRange() == null) {
|
||||
user.setDataLevelRange(1);
|
||||
}
|
||||
if (!Objects.equals(user.getDataScopeRange(), 3)) {
|
||||
user.setDataScopeDeptIds(List.of());
|
||||
}
|
||||
if (Func.isBlank(user.getAccount()) && Func.isNotBlank(user.getPhone())) {
|
||||
user.setAccount(user.getPhone());
|
||||
}
|
||||
@@ -347,6 +444,9 @@ public class MpUserServiceImpl extends BaseServiceImpl<MpUserMapper, MpUser> imp
|
||||
if (Func.isBlank(user.getPhone())) {
|
||||
throw new ServiceException("手机号不能为空");
|
||||
}
|
||||
if (Func.isBlank(user.getDeptId())) {
|
||||
throw new ServiceException("请选择所属组织");
|
||||
}
|
||||
if (Func.isBlank(user.getIdentities())) {
|
||||
throw new ServiceException("小程序身份不能为空");
|
||||
}
|
||||
@@ -359,6 +459,42 @@ public class MpUserServiceImpl extends BaseServiceImpl<MpUserMapper, MpUser> imp
|
||||
throw new ServiceException("不支持的小程序身份:" + code);
|
||||
}
|
||||
}
|
||||
Integer dataScopeRange = user.getDataScopeRange();
|
||||
if (dataScopeRange == null || (dataScopeRange != 1 && dataScopeRange != 2 && dataScopeRange != 3)) {
|
||||
throw new ServiceException("数据权限范围不正确");
|
||||
}
|
||||
Integer dataLevelRange = user.getDataLevelRange();
|
||||
if (dataLevelRange == null || (dataLevelRange != 1 && dataLevelRange != 2)) {
|
||||
throw new ServiceException("数据层级范围不正确");
|
||||
}
|
||||
if (Objects.equals(dataScopeRange, 3) && Func.isEmpty(user.getDataScopeDeptIds())) {
|
||||
throw new ServiceException("请选择自定义数据权限组织");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean submitScopes(MpUser user) {
|
||||
if (user == null || Func.isEmpty(user.getId())) {
|
||||
return false;
|
||||
}
|
||||
mpUserDataScopeMapper.delete(Wrappers.<MpUserDataScope>lambdaQuery().eq(MpUserDataScope::getUserId, user.getId()));
|
||||
if (Objects.equals(user.getDataScopeRange(), 3) && Func.isNotEmpty(user.getDataScopeDeptIds())) {
|
||||
user.getDataScopeDeptIds().stream().filter(Objects::nonNull).distinct().forEach(deptId -> {
|
||||
MpUserDataScope scope = new MpUserDataScope();
|
||||
scope.setUserId(user.getId());
|
||||
scope.setDeptId(deptId);
|
||||
mpUserDataScopeMapper.insert(scope);
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private MpUser fillScopes(MpUser user) {
|
||||
if (user == null || Func.isEmpty(user.getId())) {
|
||||
return user;
|
||||
}
|
||||
user.setDataScopeDeptIds(mpUserDataScopeMapper.selectList(Wrappers.<MpUserDataScope>lambdaQuery()
|
||||
.eq(MpUserDataScope::getUserId, user.getId())).stream().map(MpUserDataScope::getDeptId).toList());
|
||||
return user;
|
||||
}
|
||||
|
||||
private String normalizeIdentities(String identities) {
|
||||
@@ -377,4 +513,18 @@ public class MpUserServiceImpl extends BaseServiceImpl<MpUserMapper, MpUser> imp
|
||||
return trimValue.isEmpty() ? null : trimValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为前端脱敏手机号(如 138****8000),不可直接落库。
|
||||
*/
|
||||
private boolean isMaskedPhone(String phone) {
|
||||
return Func.isNotBlank(phone) && phone.contains("*");
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为前端脱敏邮箱。
|
||||
*/
|
||||
private boolean isMaskedEmail(String email) {
|
||||
return Func.isNotBlank(email) && email.contains("*");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
@@ -26,6 +26,7 @@ import org.springblade.core.mp.support.BaseEntityWrapper;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.cache.DictCache;
|
||||
import org.springblade.system.cache.SysCache;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.system.pojo.entity.MpUser;
|
||||
import org.springblade.system.pojo.enums.DictEnum;
|
||||
@@ -53,6 +54,9 @@ public class MpUserWrapper extends BaseEntityWrapper<MpUser, MpUserVO> {
|
||||
// 密码哈希不下发前端
|
||||
mpUserVO.setPassword(null);
|
||||
mpUserVO.setIdentitiesName(resolveIdentitiesName(mpUser.getIdentities()));
|
||||
if (Func.isNotBlank(mpUser.getDeptId())) {
|
||||
mpUserVO.setDeptName(Func.join(SysCache.getDeptNames(mpUser.getDeptId())));
|
||||
}
|
||||
if (mpUser.getSex() != null) {
|
||||
mpUserVO.setSexName(DictCache.getValue(DictEnum.SEX, mpUser.getSex()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user