利用枚舉進(jìn)行業(yè)務(wù)邏輯抽象

背景需求

用戶管理模塊,需要系統(tǒng)兼容本地用戶架構(gòu)和第三方的用戶架構(gòu)啦辐,比如淘寶,微信蜈项,或者其他的公司的ladp服務(wù)器的組織架構(gòu)

何為抽象

java的三大特性芹关,封裝、繼承紧卒、多態(tài)
封裝:封裝就是將類的信息隱藏在類內(nèi)部侥衬,不允許外部程序直接訪問,而是通過(guò)該類的方法實(shí)現(xiàn)對(duì)隱藏信息的操作和訪問常侦。
繼承:繼承是類與類的一種關(guān)系浇冰,比較像集合中的從屬于關(guān)系。
多態(tài):對(duì)象的多種形態(tài)聋亡。多態(tài)有兩種:引用多態(tài)和方法多態(tài)肘习。繼承是多態(tài)的實(shí)現(xiàn)基礎(chǔ)。
搞清楚兩點(diǎn):引用多態(tài)坡倔、方法多態(tài)

如何針對(duì)這個(gè)業(yè)務(wù)抽象

定義枚舉:

這里將用戶的組織架構(gòu)類型給抽象枚舉出來(lái)漂佩,枚舉的意思類似于舉例,枚舉是單實(shí)例的

/**
 * create by wuxuan.chai
 *
 * @since 4.7.3
 **/
public enum PlatCodeValue {
    /** 用于與crm的集成認(rèn)證 */
    crm {
    },
    /** ldap */
    ldap {
        @Override
        public Role fetchOrgRole(ThirdOrg thirdOrg, ThirdUser thirdUserNickName) {
            String ldapDefaultRoleName = LdapConfiguration.getInstance().getLdapDefaultRoleName();
            return ApplicationContextHolder.getInstance(RoleService.class).fetchRoleByName(ldapDefaultRoleName);
        }
    },
    local {

    };

    public Role fetchOrgRole(ThirdOrg thirdOrg, ThirdUser thirdUserNickName) {
        return ApplicationContextHolder.getInstance(RoleService.class).fetchRoleByName(Role.NAME_ORG_MEMBER);
    }
}

定義接口

接口是定義業(yè)務(wù)通用的方法罪塔,如你你這個(gè)用戶管理將要實(shí)現(xiàn)什么功能投蝉,需要做什么,接口定義的方法都將是通用的方法征堪,即每一個(gè)枚舉類中枚舉的用戶管理模塊都具備的功能

/**
 * create by wuxuan.chai
 * at 2019/11/25 5:29 下午
 *
 * @since 4.7.3
 **/

public interface IUserResolver {

    //支持的類型
    PlatCodeValue supportType();

    //刷新用戶的權(quán)限信息
    void refreshPermission(Long platUserId, Organization organization);

    //獲取用戶列表
    UserVO fetchUserList(UserVO queryUserVo);

    //獲取組織架構(gòu)節(jié)點(diǎn)
    OrgTagVO fetchOrgTagList(OrgTagVO queryOrgVo);


}

定義父類

父類的作用:給子類提供類引用瘩缆、父類提供所有子類的實(shí)現(xiàn)模版方法,或者默認(rèn)實(shí)現(xiàn)佃蚜,一般父類中的抽象方法都是子類必須實(shí)現(xiàn)的庸娱,而非抽象方法則是父類給子類的提供的默認(rèn)實(shí)現(xiàn)


/**
 * create by wuxuan.chai
 * at 2019/11/25 5:49 下午
 *
 * @since 4.7.3
 **/

public abstract class UserResolverSupport implements IUserResolver, InitializingBean {
    @Override
    public PlatCodeValue supportType() {
        return null;
    }

    @Override
    public abstract void refreshPermission(Long platUserId, Organization organization);

    @Override
    public abstract UserVO fetchUserList(UserVO queryUserVo);

    @Override
    public abstract OrgTagVO fetchOrgTagList(OrgTagVO queryOrgVo);


    @Override
    public void afterPropertiesSet() {
        PlatCodeValue platCodeValue = this.supportType();
        if(platCodeValue == null){
            return;
        }
        UserResolverRegistry.getInstance().registerUserResolver(platCodeValue,this);
    }
}

注冊(cè)類

1、子類的注冊(cè)類谐算,這個(gè)類的作用就是將所有的子類實(shí)現(xiàn)和枚舉進(jìn)行綁定熟尉,在特定的調(diào)用位置時(shí),可以通過(guò)這個(gè)類去獲取到特定的枚舉業(yè)務(wù)的實(shí)現(xiàn)類洲脂,執(zhí)行特定的實(shí)現(xiàn)方法斤儿。當(dāng)然直接調(diào)用具體的子類也可以,但是一旦業(yè)務(wù)場(chǎng)景由某個(gè)參數(shù)控制時(shí),就不靈活了往果。
2疆液、這個(gè)類是單實(shí)例的,使用的是惡漢式單示例的方式棚放。在spring的bean初始化時(shí)完成所有子類的注入枚粘。詳細(xì)可見這個(gè)類中的邏輯。

/**
 * create by wuxuan.chai
 * at 2019/11/25 5:55 下午
 *
 * @since
 **/

public class UserResolverRegistry {
    public static final UserResolverRegistry INSTANCE = new UserResolverRegistry();

    public Map<PlatCodeValue,IUserResolver> iUserResolverMap = Maps.newHashMap();

    public static UserResolverRegistry getInstance(){
        return INSTANCE;
    }

    public void registerUserResolver(PlatCodeValue platCodeValue,IUserResolver iUserResolver){
        iUserResolverMap.put(platCodeValue,iUserResolver);
    }

    public IUserResolver getUserResolver(PlatCodeValue platCodeValue){
        return iUserResolverMap.get(platCodeValue);
    }
}

實(shí)現(xiàn)類

針對(duì)Newbi的本地用戶管理的特定實(shí)現(xiàn)

/**
 * create by wuxuan.chai
 * at 2019/11/25 6:04 下午
 *
 * @since 4.7.3
 **/
@Service
public class NewBIUserResolver extends UserResolverSupport {
    @Resource
    private RbacUserService rbacUserService;
    @Resource
    private OrgTagService orgTagService;


    @Override
    public PlatCodeValue supportType() {
        return PlatCodeValue.local;
    }

    @Override
    public void refreshPermission(Long platUserId, Organization organization) {
        //nothing
    }

    @Override
    public UserVO fetchUserList(UserVO userVO) {
        //模糊查詢(email/nickname)
        CRMUserParamVO queryUserVo = userVO.getQueryUserVo();
        Page page = userVO.getPage();
        String kw = queryUserVo.getKw();
        //獲取某個(gè)節(jié)點(diǎn)下的直屬用戶
        Integer tagId = queryUserVo.getOrgId();
        List<Tag> filterTags = Lists.newArrayList();
        if(tagId != null) {
            Tag singleTag = Tag.fetch(Long.valueOf(tagId));
            filterTags.add(singleTag);
        }
        //獲取某個(gè)節(jié)點(diǎn)下的所有子節(jié)點(diǎn)的所有用戶
        Integer topTagId = queryUserVo.getTopOrgId();
        if(!ObjectUtils.isLogicalNull(topTagId)) {
            Tag topTag = Tag.fetch(Long.valueOf(topTagId));
            List<Tag> childrenTags = orgTagService.fetchChildTags(orgTagService.list(OrgUtils.fetchOrgId()), topTag.getId());
            filterTags.add(topTag);
            filterTags.addAll(childrenTags);
        }
        List<Long> filterTagIds = Lists.transform(filterTags, Tag::getId);
        List<User> users = rbacUserService.fuzzyQueryUsers(kw, page,filterTagIds)
                                .stream()
                                .peek(user->user.attr("isLocal","true"))
                                .collect(Collectors.toList());
        return UserVO.builder()
                .users(users)
                .page(page)
                .build();
    }

    @Override
    public OrgTagVO fetchOrgTagList(OrgTagVO orgTagVO) {
        //模糊查詢
        CRMOrgTagParamVO queryOrgVo = orgTagVO.getQueryOrgVo();
        String kw = queryOrgVo.getKw();
        Integer integer = Optional.ofNullable(queryOrgVo.getParentId()).orElse(0);
        Long parentId = Long.valueOf(integer);
        Page page = orgTagVO.getPage();
        List<Tag> tagList = orgTagService.fuzzyQueryTags(kw, parentId, page);
        return OrgTagVO.builder()
                .tags(tagList)
                .page(page)
                .build();
    }
}

針對(duì)于公司的crm的用戶的特定實(shí)現(xiàn)

/**
 * create by wuxuan.chai
 * at 2019/11/25 6:05 下午
 *
 * @since 4.7.3
 **/
@Service
public class CrmUserResolver extends UserResolverSupport {

    @Resource
    private ThirdUserService thirdUserService;

    @Resource
    private UserRoleService userRoleService;
    @Resource
    private UserAttrService userAttrService;

    @Override
    public PlatCodeValue supportType() {
        return PlatCodeValue.crm;
    }

    @Override
    public void refreshPermission(Long platUserId,Organization organization) {
        long orgId = organization.getId();
        ThirdUser thirdUser = thirdUserService.lookUp(String.valueOf(platUserId), orgId);
        if(thirdUser == null) {
            return;
        }
        User user = thirdUser.fetchUser();
        List<Permission> permissions = EPassportApiUtils.fetchCrmUserPermission(platUserId);
        //刷新用戶的角色
        List<Role> roles = permissions
                .stream()
                .map(p -> {
                    String code = p.getCode();
                    DefaultPermissionTypeValue byCode = DefaultPermissionTypeValue.getByCode(code);
                    return byCode.fetchRole();
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        List<Role> resultRoles = Lists.newArrayList();
        //判斷是否有系統(tǒng)角色
        roles.forEach(role -> {
            if(role.getRestrictType() == RoleRestrictTypeValue.system) {
                resultRoles.add(role);
            }
        });
        //獲取最大的組織角色
        OptionalLong min = roles.stream().filter(role -> role.getId() > 0).mapToLong(Role::getId).min();
        Asserts.assertTrue(min.isPresent(), "用戶為配置組織角色飘蚯,請(qǐng)?jiān)趀passport的角色配置中關(guān)聯(lián)馍迄。");
        assert min.isPresent();
        long roleId = min.getAsLong();
        resultRoles.add(Role.fetchRole(roleId));
        userRoleService.refreshUserRole(resultRoles, user, organization);
        //刷新用戶的個(gè)人權(quán)限
        List<UserAttr> userAttrs = permissions
                .stream()
                .map(p -> {
                    String code = p.getCode();
                    DefaultPermissionTypeValue permissionTypeValue = DefaultPermissionTypeValue.getByCode(code);
                    UserAttr userAttr = permissionTypeValue.fetchUserAttr();
                    if(userAttr == null){
                        return null;
                    }
                    userAttr.setUserId(user.getId());
                    userAttr.setUpdateDate(new Date());
                    return userAttr;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        userAttrService.refreshUserAttrs(userAttrs,organization.getId(),user.getId());

    }

    @Override
    public UserVO fetchUserList(UserVO userVo) {
        CRMUserParamVO crmUserParamVO = userVo.getQueryUserVo();
        if(crmUserParamVO == null) {
            crmUserParamVO = new CRMUserParamVO();
        }
        crmUserParamVO.buildBy(userVo.getPage());
        userVo.setQueryUserVo(crmUserParamVO);
        CRMUserVO crmUserVO = EPassportApiUtils.fetchUserList(crmUserParamVO);
        List<CrmUser> crmUsers = crmUserVO.getData();
        Long orgId = OrgUtils.fetchOrgId();
        List<ThirdUser> thirdUsers = CrmUtils.convertToThirdUser(crmUsers, orgId.toString());

        //刷新用戶,兼容配置初始化項(xiàng)目局骤,數(shù)據(jù)權(quán)限攀圈,后面可能會(huì)出現(xiàn)這種問題
        syncThirdPartyUser(thirdUsers);

        List<User> users = thirdUsers.stream()
                .map(thirdUser -> thirdUserService.lookUp(thirdUser.getPlatUserNick(), this.supportType()))
                .map(thirdUser -> {
                    User user = thirdUser.fetchUser();
                    user.attr("isLocal", "false");
                    user.attr("thirdUserId", thirdUser.getPlatUserId());
                    return user;
                })
                .collect(Collectors.toList());

        //構(gòu)建前端分頁(yè)信息
        int page = crmUserVO.getPage();
        int pageSize = crmUserVO.getPageSize();
        int total = crmUserVO.getTotal();

        Page build = Page.builder()
                .currentPage(page)
                .pageSize(pageSize)
                .totalCount(total)
                .build();

        return UserVO.builder()
                .users(users)
                .page(build)
                .build();
    }

    @Override
    public OrgTagVO fetchOrgTagList(OrgTagVO queryOrgVo) {
        CRMOrgTagParamVO crmOrgTagParamVO = queryOrgVo.getQueryOrgVo();
        if(crmOrgTagParamVO == null) {
            crmOrgTagParamVO = new CRMOrgTagParamVO();
        }
        crmOrgTagParamVO.buildBy(queryOrgVo.getPage());
        queryOrgVo.setQueryOrgVo(crmOrgTagParamVO);
        CRMOrgVO crmOrgVO = Optional.ofNullable(EPassportApiUtils.fetchOrgList(crmOrgTagParamVO)).orElse(new CRMOrgVO());
        ;
        List<CrmOrg> crmOrgs = crmOrgVO.getData();
        //epassport的組織架構(gòu)節(jié)點(diǎn)不入庫(kù),這里取得實(shí)時(shí)的
        List<Tag> tagList = crmOrgs.stream().map(Tag::buildBy).peek(tag -> tag.setOrgId(OrgUtils.fetchOrgId())).collect(Collectors.toList());
        int page = crmOrgVO.getPage();
        int pageSize = crmOrgVO.getPageSize();
        int total = crmOrgVO.getTotal();

        Page pageObject = Page.builder()
                .totalCount(total)
                .pageSize(pageSize)
                .currentPage(page)
                .build();
        return OrgTagVO.builder()
                .tags(tagList)
                .page(pageObject)
                .build();
    }

    @Transactional(rollbackFor = Exception.class)
    private void syncThirdPartyUser(List<ThirdUser> thirdUsers) {
        Organization organization = OrgUtils.fetchOrg();
        ThirdPartySyncResolverRegister.getInstance().getThirdPartySyncResolver(ThirdPartyTypeValue.crm).refreshThirdPartyUser(thirdUsers, organization);
    }
}

如何調(diào)用

只舉例刷新權(quán)限峦甩,其他的都一樣

public static void refreshPermission(String securityProfile){
    PlatCodeValue platCodeValue = PlatCodeValue.valueOf(securityProfile);
    IUserResolver userResolver = UserResolverRegistry.getInstance().getUserResolver(platCodeValue);
    ThirdUser thirdUser = thirdUserService.getByUserId(user.getId());
    userResolver.refreshPermission(Long.valueOf(thirdUser.getPlatUserId()),OrgUtils.fetchOrg());
}

總結(jié)

這個(gè)代碼模版很簡(jiǎn)單:
一個(gè)枚舉赘来,一個(gè)接口,一個(gè)父類凯傲,一個(gè)注冊(cè)器犬辰,若干枚舉子類實(shí)現(xiàn)
其中注冊(cè)器是我們的門面,后面的調(diào)用都直接從注冊(cè)器中拿就行了冰单。

這里面應(yīng)用到的設(shè)計(jì)模式有:?jiǎn)卫J交戏臁⒃湍J健⒛0娣椒J?/p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末诫欠,一起剝皮案震驚了整個(gè)濱河市涵卵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荒叼,老刑警劉巖轿偎,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異被廓,居然都是意外死亡坏晦,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門嫁乘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)英遭,“玉大人,你說(shuō)我怎么就攤上這事亦渗。” “怎么了汁尺?”我有些...
    開封第一講書人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵法精,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)搂蜓,這世上最難降的妖魔是什么狼荞? 我笑而不...
    開封第一講書人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮帮碰,結(jié)果婚禮上相味,老公的妹妹穿的比我還像新娘。我一直安慰自己殉挽,他們只是感情好丰涉,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著斯碌,像睡著了一般一死。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上傻唾,一...
    開封第一講書人閱讀 52,584評(píng)論 1 312
  • 那天投慈,我揣著相機(jī)與錄音,去河邊找鬼冠骄。 笑死伪煤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的凛辣。 我是一名探鬼主播抱既,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蟀给!你這毒婦竟也來(lái)了蝙砌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤跋理,失蹤者是張志新(化名)和其女友劉穎择克,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體前普,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肚邢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拭卿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骡湖。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖峻厚,靈堂內(nèi)的尸體忽然破棺而出响蕴,到底是詐尸還是另有隱情,我是刑警寧澤惠桃,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布浦夷,位于F島的核電站辖试,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏劈狐。R本人自食惡果不足惜罐孝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肥缔。 院中可真熱鬧莲兢,春花似錦、人聲如沸续膳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)姑宽。三九已至遣耍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間炮车,已是汗流浹背舵变。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瘦穆,地道東北人纪隙。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像扛或,于是被迫代替她去往敵國(guó)和親绵咱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容