248 lines
11 KiB
Java
248 lines
11 KiB
Java
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;
|
||
import com.yunhai.userbase.model.sysuser.SysUserRequest;
|
||
import com.yunhai.userbase.model.sysuser.SysUserVo;
|
||
import com.yunhai.userbase.model.sysuser.UserIdentityVo;
|
||
import org.springframework.web.multipart.MultipartFile;
|
||
import org.springframework.data.domain.Page;
|
||
import org.springframework.data.domain.Pageable;
|
||
|
||
import javax.servlet.http.HttpServletResponse;
|
||
import java.io.IOException;
|
||
import java.util.List;
|
||
|
||
/**
|
||
* @author chenx
|
||
* @date 2026/7/20 16:14
|
||
* @description 系统用户服务接口
|
||
*/
|
||
public interface ISysUserService {
|
||
|
||
/** 新增用户:校验密码/手机号/身份证,BCrypt 加密,并写入组用户关联。返回创建后的用户ID。 */
|
||
Integer add(SysUserRequest request);
|
||
|
||
/** 编辑用户;密码为空时不修改密码。 */
|
||
void edit(Integer id, SysUserRequest request);
|
||
|
||
/**
|
||
* 批量新增用户:逐条走完整校验(含密码强度),返回创建后的用户ID列表(顺序与入参一致)。
|
||
*/
|
||
List<Integer> batchAdd(List<SysUserRequest> requests);
|
||
|
||
/**
|
||
* 把用户从指定组中移除;若移除后该用户已无其它组关联,则级联删除用户本身。
|
||
*
|
||
* @param userId 用户ID
|
||
* @param groupId 当前组ID
|
||
*/
|
||
void deleteFromGroup(Integer userId, Integer groupId);
|
||
|
||
/**
|
||
* 批量把用户从指定组中移除;无其它组关联者级联删除用户本身。
|
||
*
|
||
* @param userIds 用户ID列表
|
||
* @param groupId 当前组ID
|
||
*/
|
||
void batchDeleteFromGroup(List<Integer> userIds, Integer groupId);
|
||
|
||
/**
|
||
* 开放接口专用:在学校(组)范围内按组解绑用户。<br>
|
||
* 与 {@link #deleteFromGroup(Integer, Integer)} 语义一致,但额外强制边界:<br>
|
||
* ① groupId 不能等于 schoolId(禁止对学校根组解绑,避免整校移除);<br>
|
||
* ② groupId 必须属于 schoolId 子树(禁止跨校解绑)。<br>
|
||
* 任意一条不满足即抛异常。
|
||
*
|
||
* @param userId 用户ID
|
||
* @param groupId 当前组ID
|
||
* @param schoolId 学校组ID(限定解绑范围)
|
||
*/
|
||
void deleteFromGroup(Integer userId, Integer groupId, Integer schoolId);
|
||
|
||
/**
|
||
* 开放接口专用:批量删除用户-组关系;每条携带自身 (userId, groupId, schoolId),逐条做上述学校边界校验后解绑。
|
||
*
|
||
* @param items 待删除项列表({@link SysUserGroupDeleteItem})
|
||
*/
|
||
void deleteUsers(List<SysUserGroupDeleteItem> items);
|
||
|
||
/**
|
||
* 启用/禁用用户。<br>
|
||
*
|
||
* @param id 用户ID
|
||
* @param disabled true=禁用,false=启用
|
||
*/
|
||
void changeStatus(Integer id, boolean disabled);
|
||
|
||
/**
|
||
* 批量启用/禁用用户。
|
||
*
|
||
* @param userIds 用户ID列表
|
||
* @param disabled true=禁用,false=启用
|
||
*/
|
||
void batchChangeStatus(List<Integer> userIds, boolean disabled);
|
||
|
||
/**
|
||
* 分页查询组下用户。<br>
|
||
* 依据 query.groupId 与 query.withSub 决定组范围,query.name 做多字段匹配。<br>
|
||
*
|
||
* @param query 查询条件
|
||
* @param pageable 分页与排序
|
||
* @return 用户分页
|
||
*/
|
||
Page<SysUserVo> list(SysUserQuery query, Pageable pageable);
|
||
|
||
/**
|
||
* 查询指定用户当前所属的所有组ID。
|
||
*
|
||
* @param userId 用户ID
|
||
* @return 组ID列表
|
||
*/
|
||
List<Integer> findGroupIdsByUserId(Integer userId);
|
||
|
||
/**
|
||
* 查询用户身份(管理端「用户身份」回显):后端按所在组解析 schoolId 后返回 {schoolId, roleIds};<br>
|
||
* 同一学校多组返回同一 schoolId 与角色并集。前端回显唯一来源(roles 列已停写,不再作为真源)。<br>
|
||
* 编辑请求提交时原样携带返回的 {schoolId, roleIds},由后端按 schoolId 全量覆盖写库并对齐。
|
||
*
|
||
* @param userId 用户ID
|
||
* @param groupId 所在组ID(Select 当前选中项;schoolId 解析规则见 {@link com.yunhai.userbase.service.ISysGroupService#resolveSchoolId})
|
||
* @return {schoolId, roleIds}
|
||
*/
|
||
UserIdentityVo getUserIdentity(Integer userId, Integer groupId);
|
||
|
||
/**
|
||
* 批量调整用户所属组:覆盖式更新 sys_user_group_rel。<br>
|
||
* 主归属组按三级降级确定:<br>
|
||
* ① mainGroupId 显式传入(须 ∈ groupIds,否则 400)→ 所有用户统一置主;<br>
|
||
* ② 未传 → 逐用户沿用旧主组(旧主组 ∈ 新 groupIds);<br>
|
||
* ③ 旧主组不在新集合 → 取 groupIds 首项(现状行为兜底)。<br>
|
||
* ② 级为行为增强:只要用户没把主组勾掉,调整组后主组不动(旧实现按数组顺序随机重指)。
|
||
*
|
||
* @param userIds 待调整用户ID列表
|
||
* @param groupIds 目标组ID列表
|
||
* @param mainGroupId 主归属组ID(可选;null 走 ②③ 级降级)
|
||
*/
|
||
void changeGroups(List<Integer> userIds, List<Integer> groupIds, Integer mainGroupId);
|
||
|
||
/**
|
||
* 开放接口专用:在学校(组)范围内移动用户。<br>
|
||
* 语义同 {@link #changeGroups}(覆盖式把用户组归属替换为目标组,目标设为主组),但额外强制:<br>
|
||
* ① 用户当前必须已在该学校子树内(至少一个组归属落在 schoolId 子树);<br>
|
||
* ② 目标 groupId 必须也在该学校子树内。<br>
|
||
* 任意一条不满足即抛异常,禁止跨校移动。
|
||
*
|
||
* @param userId 待移动用户ID
|
||
* @param groupId 目标组ID(移动后的主归属组)
|
||
* @param schoolId 学校组ID(限定移动范围,仅允许同校内移动)
|
||
*/
|
||
void moveUserWithinSchool(Integer userId, Integer groupId, Integer schoolId);
|
||
|
||
/**
|
||
* 调整用户在某组内的排序号(sort_no)。<br>
|
||
* sortNo 为 null 时清除排序号,列表回退到 user_id 升序兜底。
|
||
*
|
||
* @param userId 用户ID
|
||
* @param groupId 组ID(排序号按组维度,必须落到具体组)
|
||
* @param sortNo 排序号(1~999),可空表示清除
|
||
*/
|
||
void changeSortNo(Integer userId, Integer groupId, Integer sortNo);
|
||
|
||
/**
|
||
* 批量导入用户:按模板解析 Excel,逐行校验并新增。
|
||
* 失败行继续处理,返回成功/失败统计及失败原因。
|
||
*
|
||
* @param file Excel 文件
|
||
* @return 导入结果
|
||
*/
|
||
BatchImportResult batchImport(MultipartFile file);
|
||
|
||
/**
|
||
* 下载用户批量导入模板(真实 .xlsx):平铺指定组及其所有子孙组,仅填充组名(路径)与组id。
|
||
*
|
||
* @param groupId 当前组ID(可空,空则取组织树根)
|
||
* @param response 直接写入 Excel 流
|
||
*/
|
||
void downloadImportTemplate(Integer groupId, HttpServletResponse response) throws IOException;
|
||
|
||
/**
|
||
* 导出组下所有用户(含子组)为 Excel:与导入模板同构,末尾追加「用户id」列。<br>
|
||
* 组名 / 组id 取每个用户实际所属的主组(isMain=1);密码为密文不可逆,留空。
|
||
*
|
||
* @param groupId 当前组ID(可空,空则取组织树根)
|
||
* @param withSub 是否包含子组:1=含所有子孙组;其它=仅当前组
|
||
* @param name 关键字(可选)
|
||
* @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);
|
||
|
||
/**
|
||
* 修改用户头像:校验图片(非空/≤20MB/扩展名白名单)→ 压缩 → 存储至 avatar 子目录 →
|
||
* 写入 profile_picture。oapi 开放接口专用;返回新头像完整 Web URL。
|
||
*
|
||
* @param userId 用户ID
|
||
* @param file 头像图片文件(multipart)
|
||
* @return 新头像完整可访问 URL
|
||
*/
|
||
String changeProfilePicture(Integer userId, MultipartFile file);
|
||
|
||
/**
|
||
* 开放接口专用:设置用户在指定组上的状态(转出/恢复)。<br>
|
||
* 仅作关系标记,不影响成员查询、组织链与下游推送;同值重复调用幂等。<br>
|
||
* 边界:groupId 必须属于 schoolId 子树且不能等于 schoolId(禁止跨校/学校根组操作)。
|
||
*
|
||
* @param userId 用户ID
|
||
* @param groupId 组ID
|
||
* @param schoolId 学校组ID(限定范围)
|
||
* @param state 目标状态:0=恢复正常,1=转出
|
||
*/
|
||
void changeUserGroupState(Integer userId, Integer groupId, Integer schoolId, Integer state);
|
||
|
||
/**
|
||
* 开放接口专用:查询用户在指定组上的状态(0=正常,1=转出)。<br>
|
||
* 存量数据 status 为 NULL 视为正常返回 0。
|
||
*
|
||
* @param userId 用户ID
|
||
* @param groupId 组ID
|
||
* @param schoolId 学校组ID(限定范围)
|
||
* @return 状态值 0/1
|
||
*/
|
||
Integer getUserGroupState(Integer userId, Integer groupId, Integer schoolId);
|
||
|
||
/**
|
||
* 开放接口专用:跨校转入(转出后迁移到其他学校)。<br>
|
||
* 前置:用户已在源学校(fromSchoolId 子树内)存在至少一条「转出」状态的组关系;
|
||
* 操作成功后:删除源学校范围内全部组关系(其他学校保留),在目标学校建立目标组关系
|
||
* (主归属、状态恢复正常,转出流程结束),并推送编辑用户订阅(UPDATE_USER)。<br>
|
||
* 边界:fromSchoolId 与目标 schoolId 均须为学校组且不相同;目标组须属于目标学校子树且不等于学校根组;
|
||
* 用户已在目标学校存在组关系时拒绝(转入语义为进入全新学校)。
|
||
*
|
||
* @param userId 待迁移用户ID
|
||
* @param fromSchoolId 原学校组ID(源学校)
|
||
* @param toSchoolId 目标学校组ID
|
||
* @param groupId 目标组ID(目标学校内的挂载组,迁移后的主归属)
|
||
*/
|
||
void transferUserToSchool(Integer userId, Integer fromSchoolId, Integer toSchoolId, Integer groupId);
|
||
}
|