远程调用
不同模块之间的远程调用。service-hosp医院管理模块调用service-cmn数据字典内容模块。
注册中心(中介)
远程调用的实现前提是先将微服务模块在注册中心进行注册
尚医通使用nacos注册中心,启动nacos,账号密码nacos。
- 导入依赖spring-cloud-starter-alibaba-nacos-discovery
- 将hosp和cmn服务在nacos注册中心中注册,修改配置文件:
#nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
#服务名
spring.application.name=service-cmn
- 在启动类上标明@EnableDiscoveryClient注解,对此服务进行注册。
远程调用场景:
通过value和dict_code获取对应id,再通过赋值id给parent_id属性作为条件查询另一条bean的getName(),从而得到医院等级旗下的级别信息。
这其中需要hosp模块远程调用cmn模块(的接口)中查询到的级别名。
Controller层:
/**
* 在数据字典中根据dictcode和value查询,因为有value会重复
* 返回医院等级名
* @param dictCode
* @param value
* @return
*/
@GetMapping("getName/{dictCode}/{value}")
public String getName(@PathVariable String dictCode,
@PathVariable String value){
String dictName = dictService.getDictName(dictCode,value);
return dictName;
}
/**
* 在数据字典中根据value查询
* @param value
* @return
*/
@GetMapping("getName/{value}")
public String getName(@PathVariable String value){
String dictName = dictService.getDictName("",value);
return dictName;
}
Service层:
@Override
public String getDictName(String dictCode, String value) {
if (StringUtils.isEmpty(dictCode)) { // 如果dictCode为空,则只通过value查询
QueryWrapper<Dict> wrapper = new QueryWrapper<>();
wrapper.eq("value", value);
Dict dict = baseMapper.selectOne(wrapper);
return dict.getName();
}else { // 如果dictCode不为空,可能会出现value有相同值的情况
/*
总结,一个对象,通过一个属性值查询出一条bean对象,再通过这条bean对象获取另一属性值
可解决:某些情况下,不同bean对象有属性值重复的问题
*/
Dict dictByDictCode = this.getDictByDictCode(dictCode);// 根据dictcode查询dict对象的id值,再通过parent_id
Long id = dictByDictCode.getId();
Dict finalDict = baseMapper.selectOne(new QueryWrapper<Dict>()
.eq("parent_id", id) // 医院等级id为10000,查找parent_id列值等于10000(id)的数据
.eq("value", value));
return finalDict.getName();
}
}
// 根据dictcode查询dict对象的id值,再通过parent_id
private Dict getDictByDictCode(String dictCode){
QueryWrapper<Dict> wrapper = new QueryWrapper<>();
wrapper.eq("dict_code", dictCode);
Dict codeDict = baseMapper.selectOne(wrapper);
return codeDict;
}
在父模块中新增封装性质的service-client模块,在其中再创建子模块(用被调用者service-cmn命名)
- 在service-client引入依赖和在service-hosp中引入依赖
<!-- 打包方式为pom类型,因为其下仍有子模块-->
<packaging>pom</packaging>
<modules>
<module>service-cmn-client</module>
</modules>
<dependencies>
<dependency>
<groupId>com.lwt</groupId>
<artifactId>common_util</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.lwt</groupId>
<artifactId>model</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
- 创建接口DictFeignClient,其上标明@FeignClient(“被调用模块的服务名”),复制被调用模块的控制层方法。@Repository
@ApiModelProperty(value = "其他参数")
@Transient //被该注解标注的,将不会被录入到数据库中。只作为普通的javaBean属性
private Map<String,Object> param = new HashMap<>();
- 在调用者模块中的service层的实现类中注入DictFeignClient,我现在是想在简单的分页查询集合中 先调用后添加数据字典中的医院等级级别。但Hospital中并没有医院等级的属性,但在继承BaseEntity类中额外声明了不在数据库中的map属性,专门用来存储这种数据信息。也就是说,被调用者(cmn的医院等级信息)中的属性字段在调用者(hosp的hospital和BaseEntity有个map)的实体类中并不会出现,故BaseEntity存在的意义是,如下:
@ApiModelProperty(value = “其他参数”)
@Transient //被该注解标注的,将不会被录入到数据库中。只作为普通的javaBean属性
private Map<String,Object> param = new HashMap<>();
// 在param这个map中封装医院等级信息,以便在页面中显示
/**
* 查询医院信息,分页显示
* 后将简单分页重新注入其他模块查询出的医院等级信息 远程调用来的信息添加到分页数据中
* @param page
* @param limit
* @param hospitalQueryVo
* @return
*/
@Override
public Page<Hospital> selectHospPage(Integer page, Integer limit, HospitalQueryVo hospitalQueryVo) {
Pageable pageable = PageRequest.of(page-1,limit); // 创建分页对象
ExampleMatcher exampleMatcher = ExampleMatcher.matching()
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)
.withIgnoreCase(true); // 创建条件匹配器
Hospital hospital = new Hospital();
BeanUtils.copyProperties(hospitalQueryVo, hospital);
Example<Hospital> example = Example.of(hospital,exampleMatcher); // 创建查询对象
Page<Hospital> pages = hospitalRepository.findAll(example, pageable);
// List<Hospital> content = pages.getContent();
// 使用java8 Stream流 mongo中hostype
pages.getContent().stream().forEach(item->{
this.setHospitalHosType(item); // 向遍历出来的每条分页信息中再设置医院等级信息名
});
return pages;
}
// 调用数据字典中接口封装好的方法,传入dictCode和value值查询医院,放进map集合中欧冠
private Hospital setHospitalHosType(Hospital hospital) {
String hostypeString = dictFeignClient.getName("Hostype", hospital.getHostype());
// 练习,查询省 市 地区
String provinceString = dictFeignClient.getName(hospital.getProvinceCode());
String cityString = dictFeignClient.getName(hospital.getCityCode());
String districtString = dictFeignClient.getName(hospital.getDistrictCode());
hospital.getParam().put("fullAddress",provinceString+cityString+districtString);
hospital.getParam().put("hostypeString", hostypeString);
return hospital;
}
-
易忘操作
(1)在service模块中引入依赖openfeign包
(2)在service-hosp模块启动类中添加@EnableFeignClients注解保证可以找到DictFeignClient中指定的服务名,并指定扫描的基础包basePackage=com.lwt,因为在不同模块中,防止找不到
核心业务 思路总结
(1)数据字典模块
(2)医院模块
在数据字典中有许多属性 包括居民身份证 医院等级…等
但在医院模块中不可能将所有属性都直接声明创建getset方法
此时有必要在另一模块中查询出数据作为被调用者 被调用者调用。
而此时,我们在医院实体类中需要额外定义一个map集合去专门存放cmn中查询出的额外信息,比如医院的地理位置省试区、医院等级,使用远程调用技术feign实现具体复杂业务。(前提是在nacos注册中心将模块进行注册)