写点什么

如何从业务中抽取出通用性模板或框架 - 通用权限管理框架

用户头像
张音乐
关注
发布于: 3 小时前

在进入正题之前,先来聊一个耳熟能详,家喻户晓的东西,这里称之为东西,因为不好界定他到底是什么,往大了说,他可以单独拎出来作为一个微服务系统,他包括所有权限相关,用户鉴权服务,比如说一般电商系统中,或者会员相关的系统中,权限很错综复杂,但是往小了说他缺失智能作为一个模块存在。仅仅只包含简单的如下关系。

用户表

DROP TABLE IF EXISTS `cpt_system_user` ;CREATE TABLE IF NOT EXISTS `cpt_system_user` (  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',  `uk` varchar(20) NOT NULL DEFAULT '' COMMENT '用户uk',  `name` varchar(100) NOT NULL DEFAULT '' COMMENT '用户中文名',  `create_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '创建时间',  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',  `last_login_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '最近一次登录时间',  PRIMARY KEY (`id`),  KEY `uk_index` (`uk`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';
复制代码

角色表

-- 角色表DROP TABLE IF EXISTS `cpt_system_role` ;CREATE TABLE IF NOT EXISTS `cpt_system_role` (  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',  `name` varchar(255) NOT NULL DEFAULT '' COMMENT '角色名',  `create_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '创建时间',  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色';
复制代码

分组表

-- 用户组DROP TABLE IF EXISTS `cpt_system_group` ;CREATE TABLE `cpt_system_group` (  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',  `name` varchar(200) NOT NULL COMMENT '组名',  `create_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '创建时间',  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码

用户关联角色表

-- 用户和角色绑定关系表DROP TABLE IF EXISTS `cpt_system_user_role`;CREATE TABLE IF NOT EXISTS `cpt_system_user_role` (  `uk` varchar(20) NOT NULL COMMENT '用户ID',  `role_id` bigint(20) NOT NULL COMMENT '角色ID',  `active`  int(1) NOT NULL DEFAULT 1 COMMENT '是否有效 0,无效, 1: 有效,  用来作为软删除的状态位',  PRIMARY KEY (`uk`,`role_id`, `active`),  KEY `index_uk` (`uk`),  KEY `index_role_id` (`role_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户和角色绑定关系';
复制代码

用户关联分组表

-- 用户和用户组绑定关系DROP TABLE IF EXISTS `cpt_system_user_group` ;CREATE TABLE IF NOT EXISTS `cpt_system_user_group` (  `uk` varchar(20) NOT NULL COMMENT '用户ID',  `group_id` bigint(20) NOT NULL COMMENT '用户组ID',  `active`  int(1) NOT NULL DEFAULT 1 COMMENT '是否有效 0,无效, 1: 有效,  用来作为软删除的状态位',  PRIMARY KEY (`uk`, `group_id`, `active`),  KEY `index_uk` (`uk`),  KEY `index_group_id` (`group_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户和用户组绑定关系';
复制代码

那么最简单粗暴的 方案就是这样。

持久层代码如下

    // 查询出用于已经绑定的角色,无论状态位是0还是1    @Select("select * from cpt_system_user_role where uk = #{uk};")    List<UserRole> fetchRoleNoMatter(@Param("uk") String uk);     // 查询出用于已经绑定的分组,无论状态位是0还是1    @Select("select * from cpt_system_user_group where uk = #{uk};")    List<UserGroup> fetchGroupNoMatter(@Param("uk") String uk);         // 因为用户绑定分组和绑定角色相差甚微 使用SQL注入来合并为一个方法,以下两个方法类似    //设置有效状态位无效    @Update("<script>"            +"update ${table} set active = 0 where active = 1 and uk = #{uk} and ${joinKey} in "            +   "<foreach collection='list' item='item' index='index' open='(' separator=',' close=')'>"            +       "${item}"            +   "</foreach>"            +"</script>")    int disabledUserGroupOrRole(@Param("uk") String uk, @Param("list") List<Long> list, @Param("table") String table, @Param("joinKey") String joinKey);     // 设置无效状态为有效    @Update("<script>"            +"update ${table} set active = 1 where active = 0 and uk = #{uk} and ${joinKey} in "            +   "<foreach collection='list' item='item' index='index' open='(' separator=',' close=')'>"            +       "${item}"            +   "</foreach>"            +"</script>")    int enabledUserGroupOrRole(@Param("uk") String uk, @Param("list") List<Long> list, @Param("table") String table, @Param("joinKey") String joinKey);     // 新增有效关联数据    @Update("<script>"            +"insert into ${table} (uk, ${joinKey}, active) values "            +   "<foreach collection='list' item='item' index='index' separator=','>"            +       "(#{uk}, ${item}, 1)"            +   "</foreach>"            +"</script>")    int insertUserGroupOrRole(@Param("uk") String uk, @Param("list") List<Long> list, @Param("table") String table, @Param("joinKey") String joinKey);
复制代码

说完持久层,service 层代码如下

@Servicepublic class UserServiceImpl implements UserService {     @Autowired    private UserDao userDao;     @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)    @Override    public void grantRole(String uk, List<Long> list) {        List<Long> exists = userDao.fetchRoleNoMatter(uk).stream().map(UserRole::getRoleId).collect(Collectors.toList());        baseGrant(uk, list, exists, "cpt_system_user_role", "role_id");    }     @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)    @Override    public void grantGroup(String uk, List<Long> list) {        List<Long> exists = userDao.fetchGroupNoMatter(uk).stream().map(UserGroup::getGroupId).collect(Collectors.toList());        baseGrant(uk, list, exists, "cpt_system_user_group", "group_id");    }      /**     * 通用的授权,授分组方法     * @param uk     * @param list     * @param exists     * @param table     * @param joinKey     */    public void baseGrant(String uk, List<Long> list, List<Long> exists, String table, String joinKey) {        if(list == null || list.size() == 0) {            // 查询出已经存在的            if(exists.size() != 0) {                //全部删除并返回                userDao.disabledUserGroupOrRole(uk, list, table, joinKey);                return;            }            // 直接返回,不做任何和处理            return;        }        //1、查询已经存在的        if(exists.size() == 0) {            //如果不存在,那么全部新增之后返回            userDao.insertUserGroupOrRole(uk, list, table, joinKey);            return;        }        //2、差集 求出需要新增的   新增设置为  1        List<Long> add = list.stream().filter(t -> !exists.contains(t)).collect(Collectors.toList());        userDao.insertUserGroupOrRole(uk, add, table, joinKey);         //3、差集  求出需要剔除的  设置为 0        List<Long> del = exists.stream().filter(t -> !list.contains(t)).collect(Collectors.toList());        userDao.disabledUserGroupOrRole(uk, del, table, joinKey);         //4、求出交集,并全部设置为active为 1        List<Long> join = list.stream().filter(exists::contains).collect(Collectors.toList());        userDao.enabledUserGroupOrRole(uk, join , table, joinKey);    }}
复制代码


其实如果这么写,如果已经稳定没有其他需求了,其实就可以了,但是,如果以后再来一个用户绑定地区,或者地区绑定用户,用户绑定身份啥的,那么,就需要再写一个相同的逻辑和业务,我们现在要做的就是把这个东西抽象出来,能不管你什么绑定什么,比如再反着来一个分组绑定用户

dao 层

    @Select("select b.uk,b.name,b.create_time,b.update_time,b.last_login_time from cpt_system_user_group a join cpt_system_user b on a.uk = b.uk where a.group_id = ${groupId}")    List<User> fetchUserByGroupIdNoMatter(@Param("groupId") Long groupId);     //设置有效状态位无效    @Update("<script>"            +"update cpt_system_user_group set active = 0 where active = 1 and group_id = ${groupId} and uk in "            +   "<foreach collection='list' item='item' index='index' open='(' separator=',' close=')'>"            +       "#{item}"            +   "</foreach>"            +"</script>")    int disabledGroupUser(@Param("groupId") Long groupId, @Param("list") List<String> list);     // 设置无效状态为有效    @Update("<script>"            +"update cpt_system_user_group set active = 1 where active = 0 and group_id = ${groupId} and uk in "            +   "<foreach collection='list' item='item' index='index' open='(' separator=',' close=')'>"            +       "#{item}"            +   "</foreach>"            +"</script>")    int enabledGroupUser(@Param("groupId") Long groupId, @Param("list") List<String> list);     // 新增有效关联数据    @Update("<script>"            +"insert into cpt_system_user_group (uk, group_id, active) values "            +   "<foreach collection='list' item='item' index='index' separator=','>"            +       "(#{item}, ${groupId}, 1)"            +   "</foreach>"            +"</script>")    int insertGroupUser(@Param("groupId") Long groupId, @Param("list") List<String> list);
复制代码

很明显,不能使用刚才哪个 baseGrant 方法了,因为参数类型变化了。

用户绑定分组 逻辑: 是一个 String 类型的 uk 对应多个 Long 类型的分组 ID

分组绑定用户 逻辑: 是一个 Long 类型的 分组 ID 对应多个 String 类型的 uk

所以如果能抽取一个更加通用的模板,参数可以支持任意类型的映射关系就可以减少大量重复的代码。

抽取步骤

1、选择适合的设计模式:因为模板设计模式相对简单,并且能够解决当前问题,所以选择模板方法设计模式

2、既然选择模板方法设计模式,那么就要把 baseGrant 这个方法中的与具体业务相关的摘除掉。通过参数传递进来。

3、实现。

定义 Selector 接口,里面包含业务中所使用到的三个业务方法

/** * 通用用户授权,用户授组,组内用户组件 选择器 */public interface Selector<T> {     // 将 active = 0 设置为 active = 1    void enable(List<T> join);     // 将 active = 1 设置为 active = 0    void disable(List<T> del);     // 新增 active = 1    void insert(List<T> add); }
复制代码


进行模板的封装

@Componentpublic class BasicTemplate {     /**     * 通用的授权,授分组方法     * @param list      提交的 数组     * @param selector  查询已经存在的方法 ,包含 禁用方法,启用方法,新增方法     */    public <T> void baseGrant(List<T> list, List<T> exists, Selector<T> selector) {        // 一.如果提交的list为空表示清空        if(list == null || list.size() == 0) {            //查询出已经存在的,无论状态位  是 0 还是 1 ,保证 每个绑定关系无论怎么删除新增 只有一条            if(exists.size() != 0) {                //全部删除并返回                selector.disable(exists);                return;            }            // 直接返回,不做任务和处理            return;        }        // 二.如果在新增前没有存在过的数据,表示都是新增        if(exists.size() == 0) {            //如果不存在,那么全部新增之后返回            selector.insert(list);            return;        }        // 三. 正常两边都有数据,有提交,并且之前也有数据存在        //2、差集 求出需要新增的   新增设置为  1        List<T> add = list.stream().filter(t -> !exists.contains(t)).collect(Collectors.toList());        selector.insert(add);         //3、差集  求出需要剔除的  设置为 0        List<T> del = exists.stream().filter(t -> !list.contains(t)).collect(Collectors.toList());        selector.disable(del);         //4、求出交集,并全部设置为active为 1        List<T> join = list.stream().filter(exists::contains).collect(Collectors.toList());        selector.enable(join);    }}
复制代码

然后在 service 中只要关系持久层的方法实现就可以了。

UserService 中

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)    @Override    public void grantRole(String uk, List<Long> list) {        List<Long> exists = userDao.fetchRoleNoMatter(uk).stream().map(UserRole::getRoleId).collect(Collectors.toList());        basicTemplate.baseGrant(list, exists, new Selector<Long>() {            @Override            public void enable(List<Long> join) {                userDao.enabledUserGroupOrRole(uk, join, "cpt_system_user_role", "role_id");            }             @Override            public void disable(List<Long> del) {                userDao.disabledUserGroupOrRole(uk, del, "cpt_system_user_role", "role_id");            }             @Override            public void insert(List<Long> add) {                userDao.insertUserGroupOrRole(uk, add, "cpt_system_user_role", "role_id");            }        });    }         @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)    @Override    public void grantGroup(String uk, List<Long> list) {        List<Long> exists = userDao.fetchGroupNoMatter(uk).stream().map(UserGroup::getGroupId).collect(Collectors.toList());        basicTemplate.baseGrant(list, exists, new Selector<Long>() {            @Override            public void enable(List<Long> join) {                userDao.enabledUserGroupOrRole(uk, join, "cpt_system_user_group", "group_id");            }            @Override            public void disable(List<Long> del) {                userDao.disabledUserGroupOrRole(uk, del, "cpt_system_user_group", "group_id");            }            @Override            public void insert(List<Long> add) {                userDao.insertUserGroupOrRole(uk, add, "cpt_system_user_group", "group_id");            }        });    }
复制代码

GroupService 中

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)    @Override    public void addUsersToGroup(Long groupId, List<String> list) {        List<String> exists = groupDao.fetchUserByGroupIdNoMatter(groupId).stream().map(User::getUk).collect(Collectors.toList());        basicTemplate.baseGrant(list, exists, new Selector<String>() {            @Override            public void enable(List<String> join) {                groupDao.enabledGroupUser(groupId, join);            }            @Override            public void disable(List<String> del) {                groupDao.disabledGroupUser(groupId, del);            }            @Override            public void insert(List<String> add) {                groupDao.insertGroupUser(groupId, add);            }        });    }
复制代码

以上就是整个思考的心路历程啦。 

发布于: 3 小时前阅读数: 3
用户头像

张音乐

关注

求你关注我,别不识抬举.别逼我跪下来求你. 2021.03.28 加入

还未添加个人简介

评论

发布
暂无评论
如何从业务中抽取出通用性模板或框架-通用权限管理框架