SpringBoot实例:医院统一信息平台(组织类型)
补充说明:继续我们的用户系统开发。前面有了基本的用户信息,还有登录账号,还完成了单点登录。账号必须绑定用户,用户不一定拥有可登录账号。这是对于管理系统而言还是有必要的。有些用户是业务的参与者,但并不需要有操作。恩,也可以说这里的用户并不是常说的那种包含用户名密码的用于登录的账号,而只是一个用户信息的资源。这系统里用于登录的账号的对象叫Account(账号),而User(用户)是业务中的参与者,业务中的关联都是关联到User的。把前面代码的Safety对象的名称已经改成了Account。这个可以参与源码。
用户登录先告一段落,在后续业务中,不够用时再补充功能,或者发现不足时再加强。如:客户端管理,本来客户端是要放在数据库中管理的,token是要保存到数据库中的。这些都是要做的,只是先往后放一放。
什么是组织
组织是对资源进行分组的一种方式。如:XX集团、XX医院、XX科室、XX病区…… 那么组织类型就是:集团、医院、科室、部门……
组织的作用在这里是非常大的。可以说是这一号主角。这么定义的原因是:用户会属于某些组织,办公用品是某组织的,这台设备是某组织的资产等等,在管理中,人、事、物都是需要分组管理的。
在这里,科室指的是医院下面的组织类型,为了区别于常说的“内科”、“儿科”等。我们约定:像“内科”、“儿科”这种人们常说的分科我们定义为“学科”,而科室是对组织的一种类型的定义。科室更像是部门,学科更像专业。
上面这些定义也没什么可以争议的,我们只是在这里有这么个约定。在跟某院长讨论这个问题的时候,我们认为这样的定义更容易描述区分。
在这节我们先完成组织类型的业务实现,组织的业务会稍稍复杂些。就把组织类型和组织分成两节来写。
创建数据模型
组织类型:
package com.biboheart.huip.user.domain;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;
@Data
@Entity
@Table(name = "bh_user_org_type")
public class OrgType implements Serializable {
private static final long serialVersionUID = -7469448447123762569L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name; // 名称
private String sn; // 类型的序号
}
组织类型规则:这是为了对组织类型进行上下级规则的提前设置,比如:集团下面只允许添加医院类型的组织,设置了这样的规则后,在组织管理时就会有更好的用户体验。
package com.biboheart.huip.user.domain;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;
@Data
@Entity
@Table(name = "bh_user_org_type_rule")
public class OrgTypeRule implements Serializable {
private static final long serialVersionUID = -6713717729763625987L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private Integer pid; // 规则的父id
private Integer cid; // 规则的子id
}
创建数据连接
OrgTypeRepository:
package com.biboheart.huip.user.repository;
import java.util.List;
import org.springframework.data.jpa.repository.Query;
import com.biboheart.huip.user.basejpa.CustomRepository;
import com.biboheart.huip.user.domain.OrgType;
public interface OrgTypeRepository extends CustomRepository<OrgType, Integer> {
OrgType findBySnAndIdNot(String sn, Integer id);
OrgType findByNameAndIdNot(String name, Integer id);
@Query("select id from OrgType where sn in ?1")
List<Integer> findIdsBySnIn(List<String> inSnList);
@Query("select id from OrgType")
public List<Integer> findAllIds();
}
OrgTypeRuleRepository:
package com.biboheart.huip.user.repository;
import java.util.List;
import org.springframework.data.jpa.repository.Query;
import com.biboheart.huip.user.basejpa.CustomRepository;
import com.biboheart.huip.user.domain.OrgTypeRule;
public interface OrgTypeRuleRepository extends CustomRepository<OrgTypeRule, Integer> {
OrgTypeRule findByPidAndCid(Integer pid, Integer cid);
@Query("select cid from OrgTypeRule where pid in ?1")
public List<Integer> findCidsByPidIn(List<Integer> pids);
@Query("select pid from OrgTypeRule where cid in ?1")
public List<Integer> findPidsByCidIn(List<Integer> cids);
}
创建服务
两个数据模型只是为了对应两张数据表。服务我们只需要一个。
OrgTypeService
package com.biboheart.huip.user.service;
import java.util.List;
import com.biboheart.brick.exception.BhException;
import com.biboheart.huip.user.domain.OrgType;
import com.biboheart.huip.user.domain.OrgTypeRule;
public interface OrgTypeService {
/**
* 添加组织类型
* @param orgType 组织类型对象
* @return
*/
public OrgType save(OrgType orgType) throws BhException;
/**
* 删除组织类型
* @param id 组织类型ID
* @return
*/
public OrgType delete(Integer id, String sn);
/**
* 查询组织类型
* @param id
* @param sn
* @return
*/
public OrgType load(Integer id, String sn);
/**
* 取所有类型
* @return
*/
public List<OrgType> list(List<Integer> ids, List<String> sns, List<Integer> pidList, Integer descendant, Integer self);
public List<Integer> listId(List<String> sns, List<Integer> pidList, Integer descendant, Integer self);
/**
* 添加组织类型规则
* @param pid 父类型ID
* @param cid 子类型ID
* @throws EberException
*/
public OrgTypeRule addOrgTypeRule(Integer pid, Integer cid) throws BhException;
public OrgTypeRule deleteOrgTypeRule(Integer pid, Integer cid) throws BhException;
}
实现它
package com.biboheart.huip.user.service.impl;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.biboheart.brick.exception.BhException;
import com.biboheart.brick.utils.CheckUtils;
import com.biboheart.brick.utils.ListUtils;
import com.biboheart.huip.user.domain.OrgType;
import com.biboheart.huip.user.domain.OrgTypeRule;
import com.biboheart.huip.user.repository.OrgTypeRepository;
import com.biboheart.huip.user.repository.OrgTypeRuleRepository;
import com.biboheart.huip.user.service.OrgTypeService;
@Service
public class OrgTypeServiceImpl implements OrgTypeService {
@Autowired
private OrgTypeRepository orgTypeRepository;
@Autowired
private OrgTypeRuleRepository orgTypeRuleRepository;
@Override
public OrgType save(OrgType orgType) throws BhException {
if(CheckUtils.isEmpty(orgType.getId())) {
orgType.setId(0);
}
if (CheckUtils.isEmpty(orgType.getSn())) {
throw new BhException("类型编号不能为空");
}
if(CheckUtils.isEmpty(orgType.getName())) {
throw new BhException("类型名称不能为空");
}
if(null != orgTypeRepository.findBySnAndIdNot(orgType.getSn(), orgType.getId())) {
throw new BhException("编号已经存在");
}
if(null != orgTypeRepository.findByNameAndIdNot(orgType.getName(), orgType.getId())) {
throw new BhException("名称已经存在");
}
orgType = orgTypeRepository.save(orgType);
return orgType;
}
@Override
public OrgType delete(Integer id, String sn) {
OrgType ot = null;
if (null == ot && !CheckUtils.isEmpty(sn)) {
ot = orgTypeRepository.findBySnAndIdNot(sn, 0);
}
if (null == ot && !CheckUtils.isEmpty(id)) {
ot = orgTypeRepository.findById(id).get();
}
if (null != ot) {
orgTypeRepository.delete(ot);
}
return ot;
}
@Override
public OrgType load(Integer id, String sn) {
OrgType ot = null;
if (null == ot && !CheckUtils.isEmpty(sn)) {
ot = orgTypeRepository.findBySnAndIdNot(sn, 0);
}
if (null == ot && !CheckUtils.isEmpty(id)) {
ot = orgTypeRepository.findById(id).get();
}
return ot;
}
@Override
public List<OrgType> list(List<Integer> ids, List<String> sns, List<Integer> pidList, Integer descendant,
Integer self) {
if(CheckUtils.isEmpty(ids) && CheckUtils.isEmpty(sns) && CheckUtils.isEmpty(pidList)) {
return orgTypeRepository.findAll();
}
if(!CheckUtils.isEmpty(sns)) {
List<Integer> idList = orgTypeRepository.findIdsBySnIn(sns);
if(CheckUtils.isEmpty(ids)) {
ids = idList;
} else {
ids = ListUtils.intersectionList(ids, idList);
}
}
if(!CheckUtils.isEmpty(pidList)) {
if(CheckUtils.isEmpty(descendant)) {
List<Integer> cidList = orgTypeRuleRepository.findCidsByPidIn(pidList);
if(!CheckUtils.isEmpty(self)) {
ListUtils.mergeList(cidList, pidList);
}
if(CheckUtils.isEmpty(ids)) {
ids = cidList;
} else {
ids = ListUtils.intersectionList(ids, cidList);
}
} else {
List<Integer> cidList = new ArrayList<>();
listCid(pidList, cidList);
if(!CheckUtils.isEmpty(self)) {
ListUtils.mergeList(cidList, pidList);
}
if(CheckUtils.isEmpty(ids)) {
ids = cidList;
} else {
ids = ListUtils.intersectionList(ids, cidList);
}
}
}
if(CheckUtils.isEmpty(ids)) {
return null;
}
return orgTypeRepository.findAllById(ids);
}
@Override
public List<Integer> listId(List<String> sns, List<Integer> pidList, Integer descendant, Integer self) {
List<Integer> idList = new ArrayList<>();
if(CheckUtils.isEmpty(sns) && CheckUtils.isEmpty(pidList)) {
return orgTypeRepository.findAllIds();
}
if(!CheckUtils.isEmpty(sns)) {
idList = orgTypeRepository.findIdsBySnIn(sns);
}
if(!CheckUtils.isEmpty(pidList)) {
if(CheckUtils.isEmpty(descendant)) {
List<Integer> cidList = orgTypeRuleRepository.findCidsByPidIn(pidList);
if(!CheckUtils.isEmpty(self)) {
ListUtils.mergeList(cidList, pidList);
}
if(CheckUtils.isEmpty(idList)) {
idList = cidList;
} else {
idList = ListUtils.intersectionList(idList, cidList);
}
} else {
List<Integer> cidList = new ArrayList<>();
listCid(pidList, cidList);
if(!CheckUtils.isEmpty(self)) {
ListUtils.mergeList(cidList, pidList);
}
if(CheckUtils.isEmpty(idList)) {
idList = cidList;
} else {
idList = ListUtils.intersectionList(idList, cidList);
}
}
}
return idList;
}
@Override
public OrgTypeRule addOrgTypeRule(Integer pid, Integer cid) throws BhException {
if(CheckUtils.isEmpty(pid) || CheckUtils.isEmpty(cid)) {
throw new BhException("提供的参数有误");
}
OrgTypeRule otr = orgTypeRuleRepository.findByPidAndCid(pid, cid);
if(null != otr) {
return otr;
}
otr = new OrgTypeRule();
otr.setPid(pid);
otr.setCid(cid);
return orgTypeRuleRepository.save(otr);
}
@Override
public OrgTypeRule deleteOrgTypeRule(Integer pid, Integer cid) throws BhException {
if(CheckUtils.isEmpty(pid) || CheckUtils.isEmpty(cid)) {
throw new BhException("提供的参数有误");
}
OrgTypeRule otr = orgTypeRuleRepository.findByPidAndCid(pid, cid);
if(null == otr) {
return null;
}
orgTypeRuleRepository.delete(otr);
return otr;
}
private void listCid(List<Integer> pidList, List<Integer> cidList) {
if(CheckUtils.isEmpty(pidList)) {
return;
}
if(null == cidList) {
cidList = new ArrayList<>();
}
List<Integer> tcids = orgTypeRuleRepository.findCidsByPidIn(pidList);
if(CheckUtils.isEmpty(tcids)) {
return;
}
ListUtils.mergeList(cidList, tcids);
listCid(tcids, cidList);
}
}
开放接口
OrgTypeController
package com.biboheart.huip.user.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.biboheart.brick.exception.BhException;
import com.biboheart.brick.model.BhResponseResult;
import com.biboheart.brick.utils.CheckUtils;
import com.biboheart.brick.utils.PrimaryTransverter;
import com.biboheart.huip.user.domain.OrgType;
import com.biboheart.huip.user.domain.OrgTypeRule;
import com.biboheart.huip.user.service.OrgTypeService;
@RestController
public class OrgTypeController {
@Autowired
private OrgTypeService orgTypeService;
/**
* 添加/修改组织类型
* @param ot
* @return
* @throws EberException
*/
@RequestMapping(value = "/userapi/user/orgType/save", method = {RequestMethod.POST})
public BhResponseResult<?> save(OrgType ot) throws BhException {
ot = orgTypeService.save(ot);
return new BhResponseResult<>(0, "success", ot);
}
/**
* 删除组织类型
* @param id
* @return
* @throws EberException
*/
@RequestMapping(value = "/userapi/user/orgType/delete", method = {RequestMethod.POST, RequestMethod.GET})
public BhResponseResult<?> delete(Integer id, String sn) {
OrgType ot = orgTypeService.delete(id, sn);
return new BhResponseResult<>(0, "success", ot);
}
/**
* 取组织类型
* @param id
* @param sn
* @return
* @throws EberException
*/
@RequestMapping(value = "/userapi/user/orgType/load", method = {RequestMethod.POST, RequestMethod.GET})
public BhResponseResult<?> load(Integer id, String sn) {
OrgType ot = orgTypeService.load(id, sn);
return new BhResponseResult<>(0, "success", ot);
}
/**
* 组织类型列表
* @param ids
* @param pid
* @return
* @throws EberException
*/
@RequestMapping(value = "/userapi/user/orgType/list", method = {RequestMethod.POST, RequestMethod.GET})
public BhResponseResult<?> list(String ids, Integer pid, String sns, Integer descendant, Integer self) {
List<OrgType> ots = null;
List<Integer> idList = PrimaryTransverter.idsStr2List(ids);
List<Integer> pidList = new ArrayList<>();
if (!CheckUtils.isEmpty(pid)) {
pidList.add(pid);
}
List<String> snList = new ArrayList<>();
if(!CheckUtils.isEmpty(sns)) {
String[] snArr = sns.split(",");
for(String sn : snArr) {
snList.add(sn);
}
}
ots = orgTypeService.list(idList, snList, pidList, descendant, self);
return new BhResponseResult<>(0, "success", ots);
}
/**
* 添加规则
* @param id
* @param cid
* @return
* @throws EberException
*/
@RequestMapping(value = "/userapi/user/orgTypeRule/save", method = {RequestMethod.POST, RequestMethod.GET})
public BhResponseResult<?> saveRule(Integer id, Integer cid) throws BhException {
OrgTypeRule otr = orgTypeService.addOrgTypeRule(id, cid);
return new BhResponseResult<>(0, "success", otr);
}
/**
* 删除规则
* @param id
* @param cid
* @return
* @throws BhException
* @throws EberException
*/
@RequestMapping(value = "/userapi/user/orgTypeRule/delete", method = {RequestMethod.POST, RequestMethod.GET})
public BhResponseResult<?> deleteRule(Integer id, Integer cid) throws BhException {
OrgTypeRule otr = orgTypeService.deleteOrgTypeRule(id, cid);
return new BhResponseResult<>(0, "success", otr);
}
}
组织类型的数量不会很多,而且是低频业务。如果规则的添加和删除就不做批量了。
测试接口
测试保存

点击“send”后出错,返回的登录页面的代码。

这个看问题现象可以想到它是因为security造成的。我们先改下资源配置
com.biboheart.huip.user.security.ResourceConfiguration
package com.biboheart.huip.user.security;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
@Configuration
@EnableResourceServer
public class ResourceConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().disable()
.requestMatchers()
.antMatchers("/user/**", "/userapi/**")
.and()
.authorizeRequests()
.antMatchers("/user/**", "/userapi/**").authenticated();
}
}
/user/**是security中的controller中,获取用户信息、权限等的API的首部。我们约定用户系统中的API的定义都以"/userapi/"开头,这样的约定,以便resource服务的配置。以后在这个系统中的接口增加就不用改变配置文件。
修改后再来试下访问情况。

这下告诉我们的是无权限访问。那我们来取个token再访问。



使用password的grant_type获取token。用access_token去请求接口。



保存成功了。
其实我们在用户登录中就可以进行这段测试。当时没测,现在放到这里来走个流程。也说明了接口已经受到了用户权限的控制。到目前为止,只是控制用户是否登录,并没有做更细致的权限管理。这个要放在后面进行。




我添加了4个规则

有了规则后再访问列表就可以加些规则的参数

列表参数的含义是:列出ID为2的组织类型下面的所有后辈(子孙)组织类型,结果中包含自己。如果去掉self的参数,结果就会只有3个项。
目录结构

git地址:https://gitee.com/biboheart/huip.git