feat(dept): sys_group 增列 include_children,支持「包含子组」落库
build / build-push (push) Successful in 1m12s

- 新增 sys_group.include_children(1=部门列表含下级部门 / 0=仅本级,默认 1),仅学校节点使用
- 实体 / Vo / Repository / Service / Controller 全链路打通:单列 UPDATE + 发布 GroupChangedEvent 驱逐 groupSnapshot 缓存
- 新端点 POST /api/sysgroup/SetIncludeChildren/{id},权限只用 requireGroupInScope
  (不能用 requireGroupModifiable:后者禁止 SCHOOL 级操作学校组本身,而本属性正是学校管理员要改的本校属性)
- deploy/sql/20261021_add_sys_group_include_children.sql 提供幂等 DDL,需在 160 手工执行
This commit is contained in:
chenxing
2026-10-21 15:12:31 +08:00
parent 996eed3caf
commit bf3f3344c1
7 changed files with 79 additions and 0 deletions
@@ -4,6 +4,7 @@ import com.yunhai.userbase.common.annotation.OperationLog;
import com.yunhai.userbase.common.validation.ValidationGroups;
import com.yunhai.userbase.config.Config;
import com.yunhai.userbase.model.sysgroup.SysGroupBatchRequest;
import com.yunhai.userbase.model.sysgroup.SysGroupIncludeChildrenRequest;
import com.yunhai.userbase.model.sysgroup.SysGroupMoveRequest;
import com.yunhai.userbase.model.sysgroup.SysGroupQuery;
import com.yunhai.userbase.model.sysgroup.SysGroupRequest;
@@ -90,6 +91,21 @@ public class SysGroupController {
sysGroupService.changeStatus(id, false);
}
/**
* 设置「包含子组」(2026-10-21 部门线):该学校在部门管理页是否连同下级部门一起列出。<br>
* 单列 UPDATE sys_group.include_children,不碰其他列与表。<br>
* 权限<b>只用</b> {@code requireGroupInScope}:不能加 {@code requireGroupModifiable}——
* 后者禁止 SCHOOL 级操作学校组本身,而本属性恰恰是学校管理员必须能改的本校属性。
*/
@OperationLog("设置包含子组")
@Operation(operationId = "sysGroupSetIncludeChildren")
@PostMapping("SetIncludeChildren/{id}")
public void setIncludeChildren(@PathVariable Integer id,
@RequestBody @Validated SysGroupIncludeChildrenRequest request) {
accessScopeService.requireGroupInScope(id);
sysGroupService.changeIncludeChildren(id, Boolean.TRUE.equals(request.getIncludeChildren()));
}
@Operation(operationId = "sysGroupList")
@GetMapping("/List")
public List<SysGroupVo> list(SysGroupQuery query) {
@@ -41,6 +41,13 @@ public class SysGroup extends BaseEntity {
@Column(name = "status")
private Integer status = 0;
/**
* 包含子组(2026-10-21 部门线):1=部门列表含下级部门 / 0=仅一级部门,仅学校节点使用。<br>
* 与 status 同为节点自身属性,翻转只允许走 {@code updateIncludeChildren} 单列 UPDATE。
*/
@Column(name = "include_children")
private Integer includeChildren = 1;
// 一对多反向关联中间表
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@@ -0,0 +1,19 @@
package com.yunhai.userbase.model.sysgroup;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* @author chenx
* @date 2026/10/21
* @description 设置「包含子组」请求:该校在部门管理页是否连同下级部门一起列出。<br>
* 仅学校节点使用;true=含子组(1)/ false=仅本级(0)。
*/
@Data
public class SysGroupIncludeChildrenRequest {
/** 是否包含子组。 */
@NotNull(message = "includeChildren 不能为空")
private Boolean includeChildren;
}
@@ -18,6 +18,8 @@ public class SysGroupVo {
private Integer sort;
/** 启停状态(2026-10-20 部门线 P0):0=启用 / 1=停用;BeanUtils 同名拷贝,null 视为 0 由前端兜底 */
private Integer status;
/** 包含子组(2026-10-21):1=部门列表含下级部门 / 0=仅本级;BeanUtils 同名拷贝,null 视为 1 由前端兜底 */
private Integer includeChildren;
/** 子组(递归);默认空列表,避免 JSON 序列化出现 null。 */
private List<SysGroupVo> children = new ArrayList<>();
}
@@ -25,6 +25,14 @@ public interface SysGroupRepository extends JpaRepository<SysGroup, Integer>, Jp
@Query("UPDATE SysGroup g SET g.status = :status WHERE g.id = :id")
int updateStatus(@Param("id") Integer id, @Param("status") Integer status);
/**
* 「包含子组」单列 UPDATE(2026-10-21 部门线):与 {@link #updateStatus} 同语义,
* 严格只动 sys_group.include_children 一列,不触发实体监听器与审计列。
*/
@Modifying
@Query("UPDATE SysGroup g SET g.includeChildren = :includeChildren WHERE g.id = :id")
int updateIncludeChildren(@Param("id") Integer id, @Param("includeChildren") Integer includeChildren);
@Query("SELECT MAX(g.sort) FROM SysGroup g")
Integer findMaxSort();
@@ -37,6 +37,15 @@ public interface ISysGroupService {
*/
void changeStatus(Integer id, boolean disabled);
/**
* 设置「包含子组」(2026-10-21 部门线):单列 UPDATE sys_group.include_children,不碰其他任何表与列。<br>
* 仅学校节点使用,语义是「部门管理页的部门列表是否连同下级部门一起展示」。
*
* @param id 学校组ID
* @param include true=含子组(1)/ false=仅本级(0)
*/
void changeIncludeChildren(Integer id, boolean include);
/**
* 开放接口专用:在学校(组)范围内删除组。<br>
* 与 {@link #delete(Integer)} 删除逻辑一致,但额外强制边界:<br>
@@ -249,6 +249,24 @@ public class SysGroupService implements ISysGroupService {
eventPublisher.publishEvent(new GroupChangedEvent(this, "changeStatus"));
}
/**
* 「包含子组」翻转(2026-10-21 部门线):严格单列 UPDATE,范式与 {@link #changeStatus} 完全一致。<br>
* 必须发 {@link GroupChangedEvent}:GetTree / oapi 组树都吃 groupSnapshot 缓存,
* 不发则页面读到的 includeChildren 最多滞后 30min。
*/
@Override
@Transactional
public void changeIncludeChildren(Integer id, boolean include) {
if (id == null || sysGroupRepository.findById(id).orElse(null) == null) {
throw new DException("系统组不存在");
}
int updated = sysGroupRepository.updateIncludeChildren(id, include ? 1 : 0);
if (updated == 0) {
throw new DException("系统组不存在");
}
eventPublisher.publishEvent(new GroupChangedEvent(this, "changeIncludeChildren"));
}
@Override
@Transactional
public void delete(Integer id, Integer schoolId) {