package com.yunhai.userbase.service;
import com.yunhai.platform.utils.model.BoaModule;
import com.yunhai.userbase.model.groupauthz.*;
import java.util.List;
/**
* @author chenx
* @date 2026/7/23
* @description 组应用权限服务(应用开通 + 组授权 + 权限判定)。
*/
public interface IGroupAuthzService {
/**
* 功能1回显:查询系统全量应用列表,并标记该组已开通的应用(checked)。
*
* @param groupId 组ID
* @return 全量应用列表(含开通勾选状态)
*/
List listGroupApps(Integer groupId);
/**
* 功能1保存:全量覆盖式更新组的应用开通记录(先删旧记录再批量插入)。
* 被取消开通的应用,同步清理该组在其模块上的权限记录,避免残留脏数据。
*
* @param request 组ID + 勾选的应用ID全量列表
*/
void assignAppsToGroup(GroupAppAssignRequest request);
/**
* 功能2数据加载:查询该组已开通的应用及其模块树,并回显勾选状态与子组继承开关。
* 仅返回 sys_group_app_rel 中有记录的应用;未开通的应用不显示。
*
* @param groupId 组ID
* @return 已开通应用列表(含模块权限树)
*/
List findGroupAppsWithModules(Integer groupId);
/**
* 功能2数据加载(按应用分类分组版):与 {@link #findGroupAppsWithModules(Integer)} 数据一致,
* 但按应用类型分类聚合,便于组授权页面按分类面板展示。
* 返回全部分类(含空数组),保证前端分类筛选下拉完整。
*
* @param groupId 组ID
* @return 按应用分类分组的已开通应用列表
*/
List findGroupAppsWithModulesByCategory(Integer groupId);
/**
* 功能2保存:全量覆盖式更新组的模块权限。
* 有旧记录则更新 inheritFlag,无则新增,不在传入列表中的旧记录物理删除。
*
* @param request 组ID + 勾选的模块权限全量列表(moduleId + inheritFlag)
*/
void saveModulePermissions(GroupModulePermRequest request);
/**
* 权限判定(供登录/拦截器使用):判断用户对某模块是否有权限。
* 规则:用户任一所属组,沿祖先链回溯——自身显式勾选即通过;祖先勾选且 inheritFlag=true 即通过。
* 前置校验:该所属组必须已开通模块所属应用(最终权限 = 继承的模块权限 ∩ 该组已开通的应用)。
* 角色授权并集:用户经「启用状态」业务角色获得该模块授权也通过,且不要求组已开通模块所属应用;禁用角色不参与判定。
*
* @param userId 用户ID
* @param moduleId 模块ID
* @return true=有权限
*/
boolean hasModulePermission(Integer userId, Integer moduleId);
/**
* 权限一览:查询指定组下的直接用户列表(不含子组,不分页)。
*
* @param groupId 组ID
* @return 用户列表
*/
List findGroupUsers(Integer groupId);
/**
* 权限一览:全局搜索用户(按账号/姓名/身份证模糊、手机号精确匹配),不分页。
* 返回用户及其主归属部门/组织路径。
*
* @param keyword 搜索关键字
* @return 用户列表
*/
List searchUsers(String keyword);
/**
* 权限一览:根据用户ID聚合该用户的全部已授权应用模块树。
* 只返回有授权模块的应用,并按应用类型分组;无授权的应用类型和应用不显示。
* 权限判定复用 {@link #hasModulePermission(Integer, Integer)} 的祖先链继承逻辑。
* 角色授权并集:最终模块权限 = 组授权模块 ∪ 启用角色授权模块;
* 最终应用范围 = 组开通应用 ∪ 角色模块所属应用(角色模块不要求组必须先开通应用)。
*
* @param groupId 组ID(上下文,可选;目前仅用于校验用户存在性,实际聚合以 userId 为准)
* @param userId 用户ID
* @return 按应用类型分组的已授权应用模块树
*/
List findUserPermissionTree(Integer groupId, Integer userId);
/**
* 权限一览:查询用户所属的业务角色列表(中间下方「所属角色」区域展示用)。
* 返回该用户绑定的全部角色(含禁用角色,disabled 字段标记),普通角色回填所属应用名称。
*
* @param userId 用户ID
* @return 用户所属角色列表
*/
List findUserRoles(Integer userId);
/**
* 权限一览:查询用户所属部门树(左侧上方「所属部门」区域展示用)。
* 树仅由「用户直接归属的组 + 其全部祖先组」构成(如:云平台-学校中学-语文组),
* 多个归属组时合并为一棵树(公共祖先只出现一次)。
*
* @param userId 用户ID
* @return 所属部门树(根节点列表);用户无组归属时返回空列表
*/
List findUserGroupTree(Integer userId);
/**
* 开放接口专用:查询用户在指定调用方应用下的已授权模块(扁平化 BoaModule 列表)。
* 与 {@link #findUserPermissionTree(Integer, Integer)} 的核心差别:
* - 后者返回用户在全部已开通应用下的权限聚合(供管理端权限一览使用);
* - 本方法只返回 {@code appId} 对应这一个应用下的模块,所有 SQL/集合构建均限定该应用(数据库层面过滤)。
*
* 上下文组(schId)语义与开放接口一致:schId 非空=只认该组链路;schId 为空=取用户主学校组(若仍空=用户主归属组 id)。
* 超级管理员:不做组/继承判定,直接返回该应用下全部模块(但仍只返回该应用,不会跨应用返回)。
* 角色授权并集:最终权限 = 组授权模块 ∪ 启用角色下属于该应用的模块(逻辑与权限一览保持一致)。
*
* @param appId 调用方应用ID(必填;查不到或该应用无开通=返回空列表)
* @param schId 学校/组上下文(可选;为 null 时按"主学校→主归属组"规则推断)
* @param userId 用户ID(必填)
* @return 该应用下用户有权限的模块(扁平化;无权限时返回空列表)
*/
List findUserModulesForApp(Integer appId, Integer schId, Integer userId);
// ===================== 新增:树节点式增量授权(供左侧树 + 右侧 appAuthzList 的勾选交互) =====================
/**
* 获取某组「已开通的应用ID列表」,供前端右侧应用清单回显勾选状态。
* 实现思路:应用开通「不继承」(见 SysGroupAppRel 设计)——直接查 sys_group_app_rel 中 group_id=nodeId 的记录,
* 返回其 app_id 集合;不沿祖先链回填父组开通的应用(父组开通不代表子组自动拥有)。
* 与现有 GroupAppList(返回全量应用 + checked)的区别:本接口只返回已选 id 列表,更轻量,适合做勾选回显。
*
* @param nodeId 左侧树点击的组节点ID
* @return 该组已开通的应用ID列表(无则空列表)
*/
List findAuthorizedAppIds(Integer nodeId);
/**
* 增量设置某组的应用开通/取消(对应前端「应用多选框」勾选/取消)。
* 入参:{@link SchoolAppAuthzRequest} 封装 schid / appids(逗号分隔字符串) / mode。
* 实现思路:
* - mode=0(追加/APPEND):对 appids 中每个应用,若 sys_group_app_rel 尚无记录则插入(已存在则跳过,幂等)。
* - mode=2(删除/DELETE):对每个应用删除开通记录,并同步清理该组在该应用下模块的权限残留(与现有 assignAppsToGroup 取消开通时行为一致,避免脏数据)。
* - 应用开通「不继承」,故仅操作传入的 schId 自身,不做子组级联。
* - mode=1(覆盖) 不适用于单条增量,直接拒绝。
*
* @param request 学校组应用授权请求(组ID + 应用ID字符串 + 操作模式)
*/
void setSchoolApps(SchoolAppAuthzRequest request);
/**
* 增量设置「组-应用-模块」的模块授权(对应前端「组授权」面板逐条勾选/取消)。
* 实现思路:逐条处理 items 列表(顺序即执行顺序,后者覆盖前者):
* - mode=0(追加):对 (groupId, 展开后的 moduleId) upsert 一条 sys_group_module_perm,inherit_flag=isAll。
* - mode=2(删除):删除对应权限记录。
* - moduleId=0 表示「该应用下全部模块」,展开为应用下每个真实模块各写/删一条(不写 module_id=0 伪记录)。
* - isAll 的语义(重点):直接映射现有 sys_group_module_perm.inherit_flag 列。
* isAll=1 → inherit_flag=true:子孙组在 hasModulePermission 判定时沿祖先链自动继承该模块权限(逻辑继承,不物理写子组);
* isAll=0 → inherit_flag=false:仅本组生效。
* 这与「应用不继承 / 模块可继承」的对称性一致,且复用现有权限判定逻辑,不破坏其他功能。
* - 越权防护:模块必须属于该 appId;追加前要求该组已开通该应用(与现有 SaveModulePerms 边界一致)。
*
* 若产品实际想要「物理写子组」(子组各存一条 perm,而非逻辑继承),把 isAll=1 分支改成「对 groupId + 其全部子孙组分别 upsert」即可,详见实现类注释。
*
* @param items 模块授权条目列表(groupId/appId/moduleId/mode/isAll)
*/
void setGroupModulePerms(List items);
/**
* 查询用户有权访问的应用ID集合(oapi 开放接口 getUserApp 权限过滤用)。
* 规则与 {@link #hasModulePermission(Integer, Integer)} 对齐:
* - 超级管理员:全部应用;
* - 普通用户:用户所属组(沿祖先链)开通的应用 ∪ 启用角色模块所属应用。
*
* @param userId 用户ID
* @return 有权应用ID列表(无则空列表)
*/
List findUserAuthorizedAppIds(Integer userId);
}