目录
- 一、医院管理
- 医院管理效果展示
- 二、注册中心与服务调用
- 1. Nacos 概述
- 2. 注册服务
- 三、医院管理实现
- 1. 医院列表
- 2. service-cmn 模块提供接口
- 3. 封装 Feign 服务调用
- 4. 医院接口远程调用数据字典
- 5. 添加数据字典显示接口
- 6. 医院列表前端
一、医院管理
目前我们把医院、科室和排班都上传到了平台,那么管理平台就应该把他们管理起来,在我们的管理平台能够直观的查看这些信息。
医院管理效果展示
A、列表
B、详情
二、注册中心与服务调用
目前在医院列表中需要医院的信息和等级信息,而两段信息属于不同的的模块,service-hosp 和 service-cmn,所以我们需要使用到远程调用。
1. Nacos 概述
A、什么是 Nacos
Nacos 是阿里巴巴推出来的一个新开源项目,这是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施
B、常见的注册中心
Eureka :原生,2.0 遇到瓶颈,停止维护
Zookeeper :支持,专业的独立产品,例如:dubbo
Consul :原生,GO语言开发
Nacos :相对于 Spring Cloud Eureka 来说,Nacos 更强大
Nacos = Spring Cloud Eureka + Spring Cloud Config
Nacos 可以与 Spring, Spring Boot, Spring Cloud 集成,并能代替 Spring Cloud Eureka,Spring Cloud Config。
- 通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-config 实现配置的动态变更。
- 通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-discovery 实现服务的注册与发现。
C、Nacos 结构图
D、Nacos 下载和安装
下载地址:https://github.com/alibaba/nacos/releases
下载版本:nacos-server-1.1.4.tar.gz 或 nacos-server-1.1.4.zip,解压任意目录即可
启动 Nacos 服务
Linux/Unix/Mac
启动命令 (standalone 代表着单机模式运行,非集群模式)
启动命令 :sh startup.sh -m standalone
Windows
启动命令:cmd startup.cmd 或者双击 startup.cmd 运行文件。
访问:http://localhost:8848/nacos
用户名密码:nacos/nacos
2. 注册服务
A、Nacos 注册 service-hosp
第一步:在 service 模块 pom 文件引入依赖
<!-- 服务注册 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
第二步:在 service-hosp 的配置文件添加 nacos 服务地址
# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
第三步:在service-hosp的启动类添加注解
@SpringBootApplication
@ComponentScan(basePackages = "com.fancy")
@EnableDiscoveryClient
public class ServiceHospApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceHospApplication.class, args);
}
}
启动 service-hosp 服务,在 Nacos 管理界面的服务列表中可以看到注册的服务
service-cmn 注册过程和 service-hosp 相同 (省略)
三、医院管理实现
1. 医院列表
A、医院列表 api 接口
添加 service 分页接口与实现:
在 HospitalService 类添加分页接口
/**
* 分页查询
* @param page 当前页码
* @param limit 每页记录数
* @param hospitalQueryVo 查询条件
* @return
*/
Page<Hospital> selectHospPage(Integer page, Integer limit, HospitalQueryVo hospitalQueryVo);
HospitalServiceImpl 类实现分页
@Override
public Page<Hospital> selectHospPage(Integer page, Integer limit, HospitalQueryVo hospitalQueryVo) {
Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
//0为第一页
Pageable pageable = PageRequest.of(page-1, limit, sort);
Hospital hospital = new Hospital();
BeanUtils.copyProperties(hospitalQueryVo, hospital);
//创建匹配器,即如何使用查询条件
ExampleMatcher matcher = ExampleMatcher.matching() //构建对象
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //改变默认字符串匹配方式:模糊查询
.withIgnoreCase(true); //改变默认大小写忽略方式:忽略大小写
//创建实例
Example<Hospital> example = Example.of(hospital, matcher);
Page<Hospital> pages = hospitalRepository.findAll(example, pageable);
return pages;
}
B、添加 controller 方法
添加 com.fancy.yygh.hosp.controller.HospitalController 类
package com.fancy.yygh.hosp.controller;
@Api(tags = "医院管理接口")
@RestController
@RequestMapping("/admin/hosp/hospital")
public class HospitalController {
@Autowired
private HospitalService hospitalService;
@GetMapping("list/{page}/{limit}")
public Result listHosp(@PathVariable Integer page,
@PathVariable Integer limit,
HospitalQueryVo hospitalQueryVo) {
Page<Hospital> pageModel = hospitalService.selectHospPage(page,limit,hospitalQueryVo);
List<Hospital> content = pageModel.getContent();
long totalElements = pageModel.getTotalElements();
return Result.ok(pageModel);
}
}
2. service-cmn 模块提供接口
由于我们的医院等级、省市区地址都是取的数据字典 value值,因此我们在列表显示医院等级与医院地址时要根据数据字典 value 值获取数据字典名称。
通过学习数据字典我们知道,根据上级编码与 value 值可以获取对应的数据字典名称,如果 value 值能够保持唯一 (不一定唯一),我们也可以直接通过 value 值获取数据字典名称,目前省市区三级数据我们使用的是国家统计局的数据,数据编码我们就是数据字典的 id 与 value,所以 value 能够唯一确定一条数据字典,如图:
A、添加 service 接口与实现
在 DictService 类添加接口
/**
* 根据上级编码与值获取数据字典名称
* @param parentDictCode
* @param value
* @return
*/
String getNameByParentDictCodeAndValue(String parentDictCode, String value);
DictServiceImpl 类实现
@Cacheable(value = "dict",keyGenerator = "keyGenerator")
@Override
public String getNameByParentDictCodeAndValue(String parentDictCode, String value) {
//如果value能唯一定位数据字典,parentDictCode可以传空,例如:省市区的value值能够唯一确定
if(StringUtils.isEmpty(parentDictCode)) {
Dict dict = dictMapper.selectOne(new QueryWrapper<Dict>().eq("value", value));
if(null != dict) {
return dict.getName();
}
} else {
Dict parentDict = this.getDictByDictCode(parentDictCode);
if(null == parentDict) return "";
Dict dict = dictMapper.selectOne(new QueryWrapper<Dict>().eq("parent_id", parentDict.getId()).eq("value", value));
if(null != dict) {
return dict.getName();
}
}
return "";
}
private Dict getDictByDictCode(String dictCode) {
QueryWrapper<Dict> wrapper = new QueryWrapper<>();
wrapper.eq("dict_code",dictCode);
Dict codeDict = baseMapper.selectOne(wrapper);
return codeDict;
}
B、添加 controller 方法
DictController 类添加方法
@ApiOperation(value = "获取数据字典名称")
@GetMapping(value = "/getName/{parentDictCode}/{value}")
public String getName(
@ApiParam(name = "parentDictCode", value = "上级编码", required = true)
@PathVariable("parentDictCode")
String parentDictCode,
@ApiParam(name = "value", value = "值", required = true)
@PathVariable("value")
String value) {
return dictService.getNameByParentDictCodeAndValue(parentDictCode, value);
}
@ApiOperation(value = "获取数据字典名称")
@ApiImplicitParam(name = "value", value = "值", required = true, dataType = "Long", paramType = "path")
@GetMapping(value = "/getName/{value}")
public String getName(
@ApiParam(name = "value", value = "值", required = true)
@PathVariable("value")
String value) {
return dictService.getNameByParentDictCodeAndValue("", value);
}
说明:提供两个 api 接口,如省市区不需要上级编码,医院等级需要上级编码
3. 封装 Feign 服务调用
A、搭建 service-client 父模块
搭建过程如 service父模块
修改 pom.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.fancy.yygh</groupId>
<artifactId>yygh-parent</artifactId>
<version>1.0</version>
</parent>
<artifactId>service-client</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>com.fancy.yygh</groupId>
<artifactId>common-util</artifactId>
<version>1.0</version>
<scope>provided </scope>
</dependency>
<dependency>
<groupId>com.fancy.yygh</groupId>
<artifactId>model</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided </scope>
</dependency>
<!-- 服务调用feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
B、搭建 service-cmn-client 模块
搭建过程如 service-hosp 模块
修改 pom.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.fancy.yygh</groupId>
<artifactId>service-client</artifactId>
<version>1.0</version>
</parent>
<version>1.0</version>
<artifactId>service-cmn-client</artifactId>
<packaging>jar</packaging>
<name>service-cmn-client</name>
<description>service-cmn-client</description>
</project>
添加 Feign 接口类
package com.fancy.yygh.cmn.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("service-cmn")
public interface DictFeignClient {
/**
* 获取数据字典名称
* @param parentDictCode
* @param value
* @return
*/
@GetMapping(value = "/admin/cmn/dict/getName/{parentDictCode}/{value}")
String getName(@PathVariable("parentDictCode") String parentDictCode, @PathVariable("value") String value);
/**
* 获取数据字典名称
* @param value
* @return
*/
@GetMapping(value = "/admin/cmn/dict/getName/{value}")
String getName(@PathVariable("value") String value);
}
4. 医院接口远程调用数据字典
A、service 模块引入依赖
在 pom.xml 添加依赖
<!-- 服务调用feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
B、操作 service-hosp 模块
在 service-hosp 添加依赖
<dependency>
<groupId>com.fancy.yygh</groupId>
<artifactId>service-cmn-client</artifactId>
<version>1.0</version>
</dependency>
启动类开启服务调用
@SpringBootApplication
@ComponentScan(basePackages = "com.fancy")
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.fancy")
public class ServiceHospApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceHospApplication.class, args);
}
}
调整 service 方法
修改 HospitalServiceImpl 类实现分页
@Autowired
private DictFeignClient dictFeignClient;
@GetMapping("list/{page}/{limit}")
public Page<Hospital> listHosp(@PathVariable Integer page,
@PathVariable Integer limit,
HospitalQueryVo hospitalQueryVo) {
Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
//0为第一页
Pageable pageable = PageRequest.of(page-1, limit, sort);
Hospital hospital = new Hospital();
BeanUtils.copyProperties(hospitalQueryVo, hospital);
//创建匹配器,即如何使用查询条件
ExampleMatcher matcher = ExampleMatcher.matching() //构建对象
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) //改变默认字符串匹配方式:模糊查询
.withIgnoreCase(true); //改变默认大小写忽略方式:忽略大小写
//创建实例
Example<Hospital> example = Example.of(hospital, matcher);
Page<Hospital> pages = hospitalRepository.findAll(example, pageable);
pages.getContent().stream().forEach(item -> {
this.packHospital(item);
});
return pages;
}
/**
* 封装数据
* @param hospital
* @return
*/
private Hospital packHospital(Hospital hospital) {
String hostypeString = dictFeignClient.getName(DictEnum.HOSTYPE.getDictCode(),hospital.getHostype());
String provinceString = dictFeignClient.getName(hospital.getProvinceCode());
String cityString = dictFeignClient.getName(hospital.getCityCode());
String districtString = dictFeignClient.getName(hospital.getDistrictCode());
hospital.getParam().put("hostypeString", hostypeString);
hospital.getParam().put("fullAddress", provinceString + cityString + districtString + hospital.getAddress());
return hospital;
}
5. 添加数据字典显示接口
A、编写 controller
根据 dictCode 查询下层节点
@ApiOperation(value = "根据dictCode获取下级节点")
@GetMapping(value = "/findByDictCode/{dictCode}")
public Result<List<Dict>> findByDictCode(
@ApiParam(name = "dictCode", value = "节点编码", required = true)
@PathVariable
String dictCode) {
List<Dict> list = dictService.findByDictCode(dictCode);
return Result.ok(list);
}
B、编写 service
根据 dictCode 查询下层节点
@Override
public List<Dict> findByDictCode(String dictCode) {
Dict codeDict = this.getDictByDictCode(dictCode);
if(null == codeDict) return null;
return this.findChlidData(codeDict.getId());
}
6. 医院列表前端
A、添加路由
在 src/router/index.js 文件添加路由
{
path: '/hosp',
component: Layout,
redirect: '/hosp/list',
name: '医院管理',
alwaysShow: true,
meta: { title: '医院管理', icon: 'example' },
children: [
{
path: 'hospital/list',
name: '医院列表',
component: () => import('@/views/hosp/list'),
meta: { title: '医院列表', icon: 'table' }
},
]
},
B、封装 api 请求
创建 /api/hosp.js
import request from '@/utils/request'
export default {
//医院列表
getPageList(current,limit,searchObj) {
return request ({
url: `/admin/hosp/hospital/list/${current}/${limit}`,
method: 'get',
params: searchObj
})
},
//查询dictCode查询下级数据字典
findByDictCode(dictCode) {
return request({
url: `/admin/cmn/dict/findByDictCode/${dictCode}`,
method: 'get'
})
},
//根据id查询下级数据字典
findByParentId(dictCode) {
return request({
url: `/admin/cmn/dict/findChildData/${dictCode}`,
method: 'get'
})
}
}
C、添加组件
创建 /views/hosp/hospital/list.vue 组件
<template>
<div class="app-container">
<el-form :inline="true" class="demo-form-inline">
<el-form-item>
<el-select
v-model="searchObj.provinceCode"
placeholder="请选择省"
@change="provinceChanged">
<el-option
v-for="item in provinceList"
:key="item.id"
:label="item.name"
:value="item.id"/>
</el-select>
</el-form-item>
<el-form-item>
<el-select
v-model="searchObj.cityCode"
placeholder="请选择市"
@change="cityChanged">
<el-option
v-for="item in cityList"
:key="item.id"
:label="item.name"
:value="item.id"/>
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="searchObj.hosname" placeholder="医院名称"/>
</el-form-item>
<el-button type="primary" icon="el-icon-search" @click="fetchData()">查询</el-button>
<el-button type="default" @click="resetData()">清空</el-button>
</el-form>
<!-- banner列表 -->
<el-table v-loading="listLoading" :data="list"
border
fit
highlight-current-row>
<el-table-column
label="序号"
width="60"
align="center">
<template slot-scope="scope">
{{(page - 1) * limit + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column label="医院logo">
<template slot-scope="scope">
<img :src="'data:image/jpeg;base64,'+scope.row.logoData" width="80">
</template>
</el-table-column>
<el-table-column prop="hosname" label="医院名称"/>
<el-table-column prop="param.hostypeString" label="等级" width="90"/>
<el-table-column prop="param.fullAddress" label="详情地址"/>
<el-table-column label="状态" width="80">
<template slot-scope="scope">
{{ scope.row.status === 0 ? '未上线' : '已上线' }}
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间"/>
<el-table-column label="操作" width="230" align="center">
</el-table-column>
</el-table>
<!-- 分页组件 -->
<el-pagination
:current-page="page"
:total="total"
:page-size="limit"
:page-sizes="[5, 10, 20, 30, 40, 50, 100]"
style="padding: 30px 0; text-align: center;"
layout="sizes, prev, pager, next, jumper, ->, total, slot"
@current-change="fetchData"
@size-change="changeSize"
/>
</div>
</template>
<script>
import hospitalApi from '@/api/hosp/hosp'
export default {
data() {
return {
listLoading: true, // 数据是否正在加载
list: null, // banner列表
total: 0, // 数据库中的总记录数
page: 1, // 默认页码
limit: 10, // 每页记录数
searchObj: {}, // 查询表单对象
provinceList: [],
cityList: [],
districtList: []
}
},
// 生命周期函数:内存准备完毕,页面尚未渲染
created() {
console.log('list created......')
this.fetchData()
hospitalApi.findByDictCode('Province').then(response => {
this.provinceList = response.data
})
},
methods: {
// 加载banner列表数据
fetchData(page = 1) {
console.log('翻页。。。' + page)
// 异步获取远程数据(ajax)
this.page = page
hospitalApi.getPageList(this.page, this.limit, this.searchObj).then(
response => {
this.list = response.data.content
this.total = response.data.totalElements
// 数据加载并绑定成功
this.listLoading = false
}
)
},
// 当页码发生改变的时候
changeSize(size) {
console.log(size)
this.limit = size
this.fetchData(1)
},
// 重置查询表单
resetData() {
console.log('重置查询表单')
this.searchObj = {}
this.fetchData()
},
provinceChanged() {
this.cityList = []
this.searchObj.cityCode = null
this.districtList = []
this.searchObj.districtCode = null
hospitalApi.findByParentId(this.searchObj.provinceCode).then(response => {
this.cityList = response.data
})
}
}
}
</script>