背景需求
用戶管理模塊,需要系統(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>