Compare commits
2
Commits
e4d6e1da11
...
0b0be6b8e5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b0be6b8e5 | ||
|
|
f182fe44ce |
@@ -4,6 +4,7 @@ import com.yunhai.platform.utils.model.BoaGroup;
|
|||||||
import com.yunhai.userbase.common.validation.ValidationGroups;
|
import com.yunhai.userbase.common.validation.ValidationGroups;
|
||||||
import com.yunhai.userbase.config.Config;
|
import com.yunhai.userbase.config.Config;
|
||||||
import com.yunhai.userbase.model.sysgroup.SysGroupBatchRequest;
|
import com.yunhai.userbase.model.sysgroup.SysGroupBatchRequest;
|
||||||
|
import com.yunhai.userbase.model.sysgroup.SysGroupMoveRequest;
|
||||||
import com.yunhai.userbase.model.sysgroup.SysGroupRequest;
|
import com.yunhai.userbase.model.sysgroup.SysGroupRequest;
|
||||||
import com.yunhai.userbase.service.ISysGroupService;
|
import com.yunhai.userbase.service.ISysGroupService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -102,4 +103,14 @@ public class GroupController {
|
|||||||
public List<BoaGroup> batchAdd(@RequestBody @Validated SysGroupBatchRequest request) {
|
public List<BoaGroup> batchAdd(@RequestBody @Validated SysGroupBatchRequest request) {
|
||||||
return sysGroupService.batchAdd(request);
|
return sysGroupService.batchAdd(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动组(学校限定):仅允许在同一学校(组)子树内移动——moveId、pid 都必须落在 schoolId 子树内。<br>
|
||||||
|
* 语义复用后台 Move(环检测/类型约束/重排),额外叠加学校边界。鉴权由 OpenApiAuthEnforceConfiguration 在过滤器层强制签名。
|
||||||
|
*/
|
||||||
|
@PostMapping("move/{schoolId}")
|
||||||
|
public void move(@PathVariable Integer schoolId,
|
||||||
|
@RequestBody @Validated SysGroupMoveRequest request) {
|
||||||
|
sysGroupService.move(request, schoolId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,14 @@ public interface ISysGroupService {
|
|||||||
*/
|
*/
|
||||||
void delete(Integer id, Integer schoolId);
|
void delete(Integer id, Integer schoolId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开放接口通用:校验学校锚点 schoolId 合法——非空、存在、且必须为学校组(SCHOOL_GROUP)。<br>
|
||||||
|
* 供 delete/move/用户组关系等带 schoolId 边界的开放接口统一调用。
|
||||||
|
*
|
||||||
|
* @param schoolId 学校组ID(限定范围)
|
||||||
|
*/
|
||||||
|
void assertSchoolId(Integer schoolId);
|
||||||
|
|
||||||
List<SysGroupVo> list(SysGroupQuery query);
|
List<SysGroupVo> list(SysGroupQuery query);
|
||||||
|
|
||||||
/** 获取组织架构树;表为空时自动创建根节点「教育云中心」(pid=0)。 */
|
/** 获取组织架构树;表为空时自动创建根节点「教育云中心」(pid=0)。 */
|
||||||
@@ -56,6 +64,18 @@ public interface ISysGroupService {
|
|||||||
/** 将 moveId 移动到 pid 下(环检测)。 */
|
/** 将 moveId 移动到 pid 下(环检测)。 */
|
||||||
void move(SysGroupMoveRequest request);
|
void move(SysGroupMoveRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开放接口专用:在学校(组)范围内移动组。<br>
|
||||||
|
* 与 {@link #move(SysGroupMoveRequest)} 移动逻辑一致,但额外强制边界:<br>
|
||||||
|
* ① moveId 不能等于 schoolId(禁止移动学校根组);<br>
|
||||||
|
* ② pid 不能是平台根(0)(禁止移出学校范围);<br>
|
||||||
|
* ③ moveId 与 pid 都必须属于 schoolId 子树(禁止跨校移动)。<br>
|
||||||
|
*
|
||||||
|
* @param request 待移动节点 moveId + 目标父节点 pid
|
||||||
|
* @param schoolId 学校组ID(限定移动范围)
|
||||||
|
*/
|
||||||
|
void move(SysGroupMoveRequest request, Integer schoolId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 收集指定组自身及其所有子孙组的ID(沿 parentId 向下遍历)。<br>
|
* 收集指定组自身及其所有子孙组的ID(沿 parentId 向下遍历)。<br>
|
||||||
* 用于「查询该节点及其子组下所有用户」等子树聚合场景。<br>
|
* 用于「查询该节点及其子组下所有用户」等子树聚合场景。<br>
|
||||||
|
|||||||
@@ -216,6 +216,22 @@ public class SysGroupService implements ISysGroupService {
|
|||||||
deleteInternal(id);
|
deleteInternal(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公共学校锚点校验:schoolId 非空、存在、且必须为学校组(SCHOOL_GROUP)。<br>
|
||||||
|
* 供本服务 delete/move 及 {@code SysUserService} 等带 schoolId 边界的开放接口统一调用。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void assertSchoolId(Integer schoolId) {
|
||||||
|
if (schoolId == null) {
|
||||||
|
throw new DException("学校ID不能为空");
|
||||||
|
}
|
||||||
|
SysGroup school = sysGroupRepository.findById(schoolId)
|
||||||
|
.orElseThrow(() -> new DException("学校分组不存在"));
|
||||||
|
if (school.getType() != GroupTypeEnum.SCHOOL_GROUP) {
|
||||||
|
throw new DException("学校ID必须为学校分组");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void deleteInternal(Integer id) {
|
private void deleteInternal(Integer id) {
|
||||||
SysGroup entity = sysGroupRepository.findById(id)
|
SysGroup entity = sysGroupRepository.findById(id)
|
||||||
.orElseThrow(() -> new DException("系统组不存在"));
|
.orElseThrow(() -> new DException("系统组不存在"));
|
||||||
@@ -243,15 +259,11 @@ public class SysGroupService implements ISysGroupService {
|
|||||||
* @param schoolId 学校组ID(限定范围)
|
* @param schoolId 学校组ID(限定范围)
|
||||||
*/
|
*/
|
||||||
private void assertWithinSchool(Integer groupId, Integer schoolId) {
|
private void assertWithinSchool(Integer groupId, Integer schoolId) {
|
||||||
if (schoolId == null) {
|
// 公共锚点校验:schoolId 非空、存在、必须为学校组
|
||||||
throw new DException("学校ID不能为空");
|
assertSchoolId(schoolId);
|
||||||
}
|
|
||||||
if (groupId == null) {
|
if (groupId == null) {
|
||||||
throw new DException("组ID不能为空");
|
throw new DException("组ID不能为空");
|
||||||
}
|
}
|
||||||
if (!sysGroupRepository.existsById(schoolId)) {
|
|
||||||
throw new DException("学校分组不存在");
|
|
||||||
}
|
|
||||||
if (groupId.equals(schoolId)) {
|
if (groupId.equals(schoolId)) {
|
||||||
throw new DException("禁止删除学校根组,请指定具体子组");
|
throw new DException("禁止删除学校根组,请指定具体子组");
|
||||||
}
|
}
|
||||||
@@ -562,6 +574,45 @@ public class SysGroupService implements ISysGroupService {
|
|||||||
publishGroupEvent(PushInterfaceType.UPDATE_GROUP, moveId);
|
publishGroupEvent(PushInterfaceType.UPDATE_GROUP, moveId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void move(SysGroupMoveRequest request, Integer schoolId) {
|
||||||
|
// 开放接口边界:moveId/pid 必须落在 schoolId 子树内(禁止跨校/移学校根组/移出平台根)
|
||||||
|
assertMoveWithinSchool(request.getMoveId(), request.getPid(), schoolId);
|
||||||
|
move(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 学校边界校验:确保待移动节点与目标父节点都属于指定学校范围,且不越界。
|
||||||
|
*
|
||||||
|
* @param moveId 待移动节点ID
|
||||||
|
* @param pid 目标父节点ID
|
||||||
|
* @param schoolId 学校组ID(限定移动范围)
|
||||||
|
*/
|
||||||
|
private void assertMoveWithinSchool(Integer moveId, Integer pid, Integer schoolId) {
|
||||||
|
// 公共锚点校验:schoolId 非空、存在、必须为学校组
|
||||||
|
assertSchoolId(schoolId);
|
||||||
|
if (moveId == null) {
|
||||||
|
throw new DException("待移动节点ID不能为空");
|
||||||
|
}
|
||||||
|
if (pid == null) {
|
||||||
|
throw new DException("目标父节点ID不能为空");
|
||||||
|
}
|
||||||
|
if (moveId.equals(schoolId)) {
|
||||||
|
throw new DException("禁止移动学校根组,请指定具体子组");
|
||||||
|
}
|
||||||
|
if (pid == ROOT_PARENT_ID) {
|
||||||
|
throw new DException("禁止移动到学校范围之外(根节点)");
|
||||||
|
}
|
||||||
|
List<Integer> schoolScope = listSelfAndDescendantIds(schoolId);
|
||||||
|
if (!schoolScope.contains(moveId)) {
|
||||||
|
throw new DException("待移动节点不属于该学校范围,禁止跨校移动");
|
||||||
|
}
|
||||||
|
if (!schoolScope.contains(pid)) {
|
||||||
|
throw new DException("目标父节点不属于该学校范围,禁止跨校移动");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断 ancestorCandidate 是否为 nodeId 的后代(沿 parentId 向上回溯)。<br>
|
* 判断 ancestorCandidate 是否为 nodeId 的后代(沿 parentId 向上回溯)。<br>
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -279,15 +279,11 @@ public class SysUserService implements ISysUserService {
|
|||||||
* @param schoolId 学校组ID(限定范围)
|
* @param schoolId 学校组ID(限定范围)
|
||||||
*/
|
*/
|
||||||
private void assertGroupWithinSchool(Integer groupId, Integer schoolId) {
|
private void assertGroupWithinSchool(Integer groupId, Integer schoolId) {
|
||||||
if (schoolId == null) {
|
// 公共锚点校验:schoolId 非空、存在、必须为学校组(复用 SysGroupService.assertSchoolId)
|
||||||
throw new DException("学校ID不能为空");
|
sysGroupService.assertSchoolId(schoolId);
|
||||||
}
|
|
||||||
if (groupId == null) {
|
if (groupId == null) {
|
||||||
throw new DException("组ID不能为空");
|
throw new DException("组ID不能为空");
|
||||||
}
|
}
|
||||||
if (!sysGroupRepository.existsById(schoolId)) {
|
|
||||||
throw new DException("学校分组不存在");
|
|
||||||
}
|
|
||||||
// 拿学校根组当 groupId = 把用户从整所学校移除,禁止
|
// 拿学校根组当 groupId = 把用户从整所学校移除,禁止
|
||||||
if (groupId.equals(schoolId)) {
|
if (groupId.equals(schoolId)) {
|
||||||
throw new DException("禁止对学校根组执行该操作,请指定具体子组");
|
throw new DException("禁止对学校根组执行该操作,请指定具体子组");
|
||||||
@@ -454,18 +450,14 @@ public class SysUserService implements ISysUserService {
|
|||||||
if (groupId == null) {
|
if (groupId == null) {
|
||||||
throw new DException("目标组ID不能为空");
|
throw new DException("目标组ID不能为空");
|
||||||
}
|
}
|
||||||
if (schoolId == null) {
|
// 公共锚点校验:schoolId 非空、存在、必须为学校组(复用 SysGroupService.assertSchoolId)
|
||||||
throw new DException("学校ID不能为空");
|
sysGroupService.assertSchoolId(schoolId);
|
||||||
}
|
|
||||||
if (!sysUserRepository.existsById(userId)) {
|
if (!sysUserRepository.existsById(userId)) {
|
||||||
throw new DException("系统用户不存在");
|
throw new DException("系统用户不存在");
|
||||||
}
|
}
|
||||||
if (!sysGroupRepository.existsById(groupId)) {
|
if (!sysGroupRepository.existsById(groupId)) {
|
||||||
throw new DException("目标分组不存在");
|
throw new DException("目标分组不存在");
|
||||||
}
|
}
|
||||||
if (!sysGroupRepository.existsById(schoolId)) {
|
|
||||||
throw new DException("学校分组不存在");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 学校子树范围(含学校自身):用户与目标组都必须落在此范围内,否则视为跨校移动
|
// 学校子树范围(含学校自身):用户与目标组都必须落在此范围内,否则视为跨校移动
|
||||||
List<Integer> schoolScope = sysGroupService.listSelfAndDescendantIds(schoolId);
|
List<Integer> schoolScope = sysGroupService.listSelfAndDescendantIds(schoolId);
|
||||||
|
|||||||
Reference in New Issue
Block a user