开始时间:2022-05-27
课程链接:课程链接:【尚医通】
注册中心与服务调用
目前在医院列表中需要医院的信息和等级信息,而两段信息属于不同的的模块,service-hosp和service-cmn,所以我们需要使用到远程调用。
参考Dubbo入门
本项目使用Nacos
配置nacos
下载的是nacos-server-1.1.4版本
启动startup.cmd
启动后不要关闭,关了就会运行不了
在service的pom中添加
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
然后配置我们的两个微服务
将他们的application.properties分别设置一下
# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
并在他们的主启动类上都加
@EnableDiscoveryClient
注解
此时打开网站
localhost:8848/nacos
默认登录名和密码都是nacos
进来之后
可以看到我们已经添加了两个服务
医院列表接口
目前我们把医院、科室和排班都上传到了平台,那么管理平台就应该把他们管理起来,在我们的管理平台能够直观的查看这些信息。
医院列表api接口
hosp微服务
写接口
package com.bupt.yygh.hosp.service;
import com.bupt.yygh.model.hosp.Hospital;
import com.bupt.yygh.vo.hosp.HospitalQueryVo;
import org.springframework.data.domain.Page;
import java.util.Map;
public interface HospitalService {
//上传医院接口
void save(Map<String, Object> paramMap);
//实现根据医院编号查询
Hospital getByHoscode(String hoscode);
//医院列表(条件查询带分页)
Page<Hospital> selectHospPage(Integer page, Integer limit, HospitalQueryVo hospitalQueryVo);
void updateStatus(String id, Integer status);
Map<String, Object> getHospById(String id);
}
在HospitalServiceImpl类实现接口
package com.bupt.yygh.hosp.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.bupt.yygh.cmn.client.DictFeignClient;
import com.bupt.yygh.hosp.repository.HospitalRepository;
import com.bupt.yygh.hosp.service.HospitalService;
import com.bupt.yygh.model.hosp.Hospital;
import com.bupt.yygh.vo.hosp.HospitalQueryVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Service
public class HospitalServiceImpl implements HospitalService {
@Autowired
private HospitalRepository hospitalRepository;
@Autowired
private DictFeignClient dictFeignClient;
@Override
public void save(Map<String, Object> paramMap) {
//把参数map集合转换成字符串再转为对象 Hospital
String mapString = JSONObject.toJSONString(paramMap);
Hospital hospital = JSONObject.parseObject(mapString, Hospital.class);
//判断是否存在数据
String hoscode = hospital.getHoscode();
Hospital hospitalExist = hospitalRepository.getHospitalByHoscode(hoscode);
//如果存在,进行修改
if (hospitalExist != null) {
hospital.setStatus(hospitalExist.getStatus());
hospital.setCreateTime(hospitalExist.getCreateTime());
hospital.setUpdateTime(new Date());
hospital.setIsDeleted(0);
hospitalRepository.save(hospital);
} else {//如果不存在,进行添加
//判断项目是否上线
hospital.setStatus(0);
hospital.setCreateTime(new Date());
hospital.setUpdateTime(new Date());
hospital.setIsDeleted(0);
hospitalRepository.save(hospital);
}
}
@Override
public Hospital getByHoscode(String hoscode) {
Hospital hospital = hospitalRepository.getHospitalByHoscode(hoscode);
return hospital;
}
//医院列表(条件查询分页)
@Override
public Page<Hospital> selectHospPage(Integer page, Integer limit, HospitalQueryVo hospitalQueryVo) {
//创建pageable对象
Pageable pageable = PageRequest.of(page - 1, limit);
//创建条件匹配器
ExampleMatcher matcher = ExampleMatcher.matching()
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)
.withIgnoreCase(true);
//hospitalQueryVo转换Hospital对象
Hospital hospital = new Hospital();
BeanUtils.copyProperties(hospitalQueryVo, hospital);
//创建对象
Example<Hospital> example = Example.of(hospital, matcher);
//调用方法实现查询
Page<Hospital> pages = hospitalRepository.findAll(example, pageable);
// //获取查询list集合,遍历进行医院等级封装
pages.getContent().stream().forEach(item -> {
this.setHospitalHosType(item);
});
return pages;
}
//更新医院上线状态
@Override
public void updateStatus(String id, Integer status) {
//根据id查询医院信息
Hospital hospital = hospitalRepository.findById(id).get();
//设置修改的值
hospital.setStatus(status);
hospital.setUpdateTime(new Date());
hospitalRepository.save(hospital);
}
@Override
public Map<String, Object> getHospById(String id) {
Map<String, Object> result = new HashMap<>();
Hospital hospital = this.setHospitalHosType(hospitalRepository.findById(id).get());
//医院基本信息(包含医院等级)
result.put("hospital",hospital);
//单独处理更直观
result.put("bookingRule", hospital.getBookingRule());
//不需要重复返回
hospital.setBookingRule(null);
return result;
}
//获取查询list集合,遍历进行医院等级封装
private Hospital setHospitalHosType(Hospital hospital) {
//根据dictCode和value获取医院等级名称
//这里算多表查询,一个mysql一个MongoDB
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;
}
}
添加controller方法
package com.bupt.yygh.hosp.controller;
import com.bupt.yygh.common.result.Result;
import com.bupt.yygh.hosp.service.HospitalService;
import com.bupt.yygh.model.hosp.Hospital;
import com.bupt.yygh.vo.hosp.HospitalQueryVo;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/admin/hosp/hospital")
//@CrossOrigin
public class HospitalController {
@Autowired
private HospitalService hospitalService;
//医院列表(条件查询分页)
@GetMapping("list/{page}/{limit}")
public Result listHosp(@PathVariable Integer page,
@PathVariable Integer limit,
HospitalQueryVo hospitalQueryVo) {
//MongoDB返回对象
Page<Hospital> pageModel = hospitalService.selectHospPage(page, limit, hospitalQueryVo);
List<Hospital> content = pageModel.getContent();
long totalElements = pageModel.getTotalElements();
return Result.ok(pageModel);
}
//更新医院上线状态
@ApiOperation(value = "更新医院上线状态")
@GetMapping("updateHospStatus/{id}/{status}")
public Result updateHospStatus(@PathVariable String id, @PathVariable Integer status) {
hospitalService.updateStatus(id, status);
return Result.ok();
}
//医院详情信息
@ApiOperation(value = "医院详情信息")
@GetMapping("showHospDetail/{id}")
public Result showHospDetail(@PathVariable String id) {
Map<String, Object> map = hospitalService.getHospById(id);
return Result.ok(map);
}
}
cmn微服务
由于我们的医院等级、省市区地址都是取的数据字典value值,因此我们在列表显示医院等级与医院地址时要根据数据字典value值获取数据字典名称
接口
package com.bupt.yygh.cmn.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bupt.yygh.model.cmn.Dict;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
public interface DictService extends IService<Dict> {
//@Autowired不用写了,已经通过MP帮忙注册了
List<Dict> findChlidData(Long id);
void exportData(HttpServletResponse response);
void importData(MultipartFile file);
String getDictName(String dictCode, String value);
//根据dictCode获取下级节点
List<Dict> findByDictCode(String dictCode);
}
实现类
package com.bupt.yygh.cmn.service.impl;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bupt.yygh.cmn.listener.DictListener;
import com.bupt.yygh.cmn.mapper.DictMapper;
import com.bupt.yygh.cmn.service.DictService;
import com.bupt.yygh.model.cmn.Dict;
import com.bupt.yygh.vo.cmn.DictEeVo;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
@Service
public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements DictService {
//@Autowired不用写了,已经通过MP帮忙注册了
@Override
@Cacheable(value = "dict", keyGenerator = "keyGenerator")
public List<Dict> findChlidData(Long id) {
//条件构造器
QueryWrapper<Dict> wrapper = new QueryWrapper<>();
wrapper.eq("parent_id", id);
List<Dict> dictList = baseMapper.selectList(wrapper);
//向list集合每个dict对象中设置hasChildren
for (Dict dict : dictList) {
Long dictId = dict.getId();
boolean isChild = this.isChildren(dictId);
dict.setHasChildren(isChild);
}
return dictList;
}
//判断id下面是否有子节点
private boolean isChildren(Long id) {
QueryWrapper<Dict> wrapper = new QueryWrapper<>();
wrapper.eq("parent_id", id);
Integer count = baseMapper.selectCount(wrapper);
// 0>0 1>0
return count > 0;
}
@Override
public void exportData(HttpServletResponse response) {
try {
//设置下载信息,以下载方式打开
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = "dict";
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
//查询数据库
List<Dict> dictList = baseMapper.selectList(null);
// 这个dictList不能直接传,因为对应关系不匹配,我们要传的是DictEeVo对象
List<DictEeVo> dictVoList = new ArrayList<>(dictList.size());
for (Dict dict : dictList) {
DictEeVo dictEeVo = new DictEeVo();
BeanUtils.copyProperties(dict, dictEeVo);
dictVoList.add(dictEeVo);
}
//刚刚是传的路径,这里传IO流了
EasyExcel.write(response.getOutputStream(), DictEeVo.class).sheet("数据字典").doWrite(dictVoList);
} catch (IOException e) {
e.printStackTrace();
}
}
//清空缓存,装最新更新的数据
@Override
@CacheEvict(value = "dict", allEntries = true)
public void importData(MultipartFile file) {
//导入数据字典
try {
EasyExcel.read(file.getInputStream(), DictEeVo.class, new DictListener(baseMapper)).sheet().doRead();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String getDictName(String dictCode, String value) {
//如果dictCode为空,直接根据value查询
if (StringUtils.isEmpty(dictCode)) {
//直接根据value查询
QueryWrapper<Dict> wrapper = new QueryWrapper<>();
wrapper.eq("value", value);
Dict dict = baseMapper.selectOne(wrapper);
return dict.getName();
} else {//如果dictCode不为空,根据dictCode和value查询
//根据dictcode查询dict对象,得到dict的id值(此时定位的就是诸如“医院等级”这种字段)
Dict codeDict = this.getDictByDictCode(dictCode);
Long parent_id = codeDict.getId();
//然后根据刚刚dict的id值,去匹配看,谁的parent_id根据parent_id和value进行查询
// 此时定位的就是医院等级下具体的数据
Dict finalDict = baseMapper.selectOne(new QueryWrapper<Dict>()
.eq("parent_id", parent_id)
.eq("value", value));
return finalDict.getName();
}
}
//根据dictCode获取下级节点
@Override
public List<Dict> findByDictCode(String dictCode) {
//根据dictCode获取对应id
Dict dict = this.getDictByDictCode(dictCode);
// 根据id获取子节点
List<Dict> childData = this.findChlidData(dict.getId());
return childData;
}
private Dict getDictByDictCode(String dictCode) {
QueryWrapper<Dict> wrapper = new QueryWrapper<>();
wrapper.eq("dict_code", dictCode);
Dict codeDict = baseMapper.selectOne(wrapper);
return codeDict;
}
}
添加controller
package com.bupt.yygh.cmn.controller;
import com.bupt.yygh.cmn.service.DictService;
import com.bupt.yygh.common.result.Result;
import com.bupt.yygh.model.cmn.Dict;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@Api("数据字典接口")
@RestController
@RequestMapping("/admin/cmn/dict")
//@CrossOrigin
public class DictController {
@Autowired
private DictService dictService;
//根据数据id查询子数据列表
@ApiOperation(value = "根据数据id查询子数据列表")
@GetMapping("findChildData/{id}")
public Result findChildData(@PathVariable Long id) {
List<Dict> list = dictService.findChlidData(id);
return Result.ok(list);
}
@ApiOperation(value = "导出")
@GetMapping(value = "/exportData")
public void exportData(HttpServletResponse response) {
dictService.exportData(response);
}
@ApiOperation(value = "导入")
@PostMapping("importData")
public Result importData(MultipartFile file) {
dictService.importData(file);
return Result.ok();
}
// //导入数据字典
// @PostMapping("importData")
// public Result importDict(MultipartFile file) {
// dictService.importDictData(file);
// return Result.ok();
// }
//
// //导出数据字典接口
// @GetMapping("exportData")
// public void exportDict(HttpServletResponse response) {
// dictService.exportDictData(response);
// }
//根据dictCode获取下级节点
//比如查询到省份这个字段的节点,那么通过他对应的id,去找下面谁的父id为这个id
//找到的就是其下的具体数据
@ApiOperation(value = "根据dictCode获取下级节点")
@GetMapping("findByDictCode/{dictCode}")
public Result findByDictCode(@PathVariable String dictCode) {
List<Dict> list = dictService.findByDictCode(dictCode);
return Result.ok(list);
}
//根据dictcode和value查询
@GetMapping("getName/{dictCode}/{value}")
public String getName(@PathVariable String dictCode,
@PathVariable String value) {
String dictName = dictService.getDictName(dictCode, value);
return dictName;
}
//根据value查询
@GetMapping("getName/{value}")
public String getName(@PathVariable String value) {
String dictName = dictService.getDictName("", value);
return dictName;
}
}
封装Feign服务调用
我们的cmn是服务提供者,hosp是消费者,那么还需要建立他俩以及注册中心的连接
搭建模块
添加Feign接口类
package com.bupt.yygh.cmn.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
//需要调用服务的名称
@FeignClient("service-cmn")
@Repository
public interface DictFeignClient {
@GetMapping("/admin/cmn/dict/getName/{dictCode}/{value}")
public String getName(@PathVariable("dictCode") String dictCode, @PathVariable("value") String value);
//根据value查询
@GetMapping("/admin/cmn/dict/getName/{value}")
public String getName(@PathVariable("value") String value);
}
报错
在service模块引入依赖
<!-- 服务调用feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
在service-hosp添加依赖
<dependency>
<groupId>com.atguigu.yygh</groupId>
<artifactId>service-cmn-client</artifactId>
<version>1.0</version>
</dependency>
启动类开启服务调用
@SpringBootApplication
@ComponentScan(basePackages = "com.bupt")
@EnableDiscoveryClient
//下面这个注解是为了找到微服务的提供者
@EnableFeignClients(basePackages = "com.bupt")
public class ServiceHospApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceHospApplication.class, args);
}
}
前端代码略
此时测试一下
发现报错
后端部分是可以启动的
我怀疑是跨域问题,于是考虑配置网关
配置网关
之前用nginx不能解决跨域
每次都要在controller上加crossorigin注解
用spring cloud gateway来解决会更好
算是技术使用的一种升级
启动报错
是因为我的80端口被占用,需要把nginx关掉
配置了网关,还在报这个错误
但另外的服务都是正常的
并且转发的URL也是localhost而不是localhost:9001了
问题解决:注意观察
我们上面的报错信息
是list/1/10/一大堆东西
但是我们后端传参只传了
@GetMapping("list/{page}/{limit}")
也就是有一个参数我们是没有的,我就去看前端代码
在src\api\hosp.js下
我们写了一个获取医院列表的方法
应该是
getHospList(page, limit, searchObj) {
return request({
url: `/admin/hosp/hospital/list/${page}/${limit}`,
method: 'get',
params: searchObj
})
我写成了
url: `/admin/hosp/hospital/list/${page}/${limit}/${searchObj}`,
诶唷,找了半天
看看效果
使用idea配置前端代码(抛弃掉VSCODE)
因为我目前用idea上传代码到码云上很容易,VSCODE不太行
所以我尝试在idea上配置前端代码,然后上传到gitee很方便
参考博客
启动
正常运行
然后上传代码到gitee里面,
从gitee里下载代码到本地
平时都是用工位电脑做开发的,现在需要用自己的电脑做开发,于是把gitee上代码得克隆下来,在这里走一遍流程。
参考博客
下载到本地后导入idea,参考博客
我自己的电脑要使用前端代码,还得先下载node.js
最好把VSCODE那一套走一遍,把环境配置好
再用idea打开正常使用
当然也可以直接在idea中打开终端
选中terminal
再通过cd命令切换到对应目录即可
实现医院列表功能
具体代码略,参考码云仓库尚医通后端代码第八次提交
查看医院详情
上线下线
排班列表
结束时间:2022-05-29