利用枚举进行业务逻辑抽象
2020-01-19 本文已影响0人
毁于一蛋
背景需求
用户管理模块,需要系统兼容本地用户架构和第三方的用户架构,比如淘宝,微信,或者其他的公司的ladp服务器的组织架构
何为抽象
java的三大特性,封装、继承、多态
封装:封装就是将类的信息隐藏在类内部,不允许外部程序直接访问,而是通过该类的方法实现对隐藏信息的操作和访问。
继承:继承是类与类的一种关系,比较像集合中的从属于关系。
多态:对象的多种形态。多态有两种:引用多态和方法多态。继承是多态的实现基础。
搞清楚两点:引用多态、方法多态
如何针对这个业务抽象
定义枚举:
这里将用户的组织架构类型给抽象枚举出来,枚举的意思类似于举例,枚举是单实例的
/**
* create by wuxuan.chai
*
* @since 4.7.3
**/
public enum PlatCodeValue {
/** 用于与crm的集成认证 */
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);
}
}
定义接口
接口是定义业务通用的方法,如你你这个用户管理将要实现什么功能,需要做什么,接口定义的方法都将是通用的方法,即每一个枚举类中枚举的用户管理模块都具备的功能
/**
* create by wuxuan.chai
* at 2019/11/25 5:29 下午
*
* @since 4.7.3
**/
public interface IUserResolver {
//支持的类型
PlatCodeValue supportType();
//刷新用户的权限信息
void refreshPermission(Long platUserId, Organization organization);
//获取用户列表
UserVO fetchUserList(UserVO queryUserVo);
//获取组织架构节点
OrgTagVO fetchOrgTagList(OrgTagVO queryOrgVo);
}
定义父类
父类的作用:给子类提供类引用、父类提供所有子类的实现模版方法,或者默认实现,一般父类中的抽象方法都是子类必须实现的,而非抽象方法则是父类给子类的提供的默认实现
/**
* 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);
}
}
注册类
1、子类的注册类,这个类的作用就是将所有的子类实现和枚举进行绑定,在特定的调用位置时,可以通过这个类去获取到特定的枚举业务的实现类,执行特定的实现方法。当然直接调用具体的子类也可以,但是一旦业务场景由某个参数控制时,就不灵活了。
2、这个类是单实例的,使用的是恶汉式单示例的方式。在spring的bean初始化时完成所有子类的注入。详细可见这个类中的逻辑。
/**
* 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);
}
}
实现类
针对Newbi的本地用户管理的特定实现
/**
* 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();
//获取某个节点下的直属用户
Integer tagId = queryUserVo.getOrgId();
List<Tag> filterTags = Lists.newArrayList();
if(tagId != null) {
Tag singleTag = Tag.fetch(Long.valueOf(tagId));
filterTags.add(singleTag);
}
//获取某个节点下的所有子节点的所有用户
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();
}
}
针对于公司的crm的用户的特定实现
/**
* 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();
//判断是否有系统角色
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(), "用户为配置组织角色,请在epassport的角色配置中关联。");
assert min.isPresent();
long roleId = min.getAsLong();
resultRoles.add(Role.fetchRole(roleId));
userRoleService.refreshUserRole(resultRoles, user, organization);
//刷新用户的个人权限
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());
//刷新用户,兼容配置初始化项目,数据权限,后面可能会出现这种问题
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());
//构建前端分页信息
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的组织架构节点不入库,这里取得实时的
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);
}
}
如何调用
只举例刷新权限,其他的都一样
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());
}
总结
这个代码模版很简单:
一个枚举,一个接口,一个父类,一个注册器,若干枚举子类实现
其中注册器是我们的门面,后面的调用都直接从注册器中拿就行了。
这里面应用到的设计模式有:单例模式、原型模式、模版方法模式