何为数据字典?数据字典就是管理系统常用的分类数据或者一些固定数据,例如:省市区三级联动数据、民族数据、行业数据、学历数据等,由于该系统大量使用这种数据,所以我们要做一个数据管理方便管理系统数据,一般系统基本都会做数据管理。
1、页面效果:
2、表设计:
3、数据分析:
数据库中的表:
表中字段说明:
数据字典的表:
CREATE TABLE `dict` (
`id` bigint(20) NOT NULL DEFAULT '0' COMMENT 'id',
`parent_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '上级id',
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '名称',
`value` bigint(20) DEFAULT NULL COMMENT '值',
`dict_code` varchar(20) DEFAULT NULL COMMENT '编码',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` tinyint(3) NOT NULL DEFAULT '1' COMMENT '删除标记(0:不可用 1:可用)',
PRIMARY KEY (`id`),
KEY `idx_dict_code` (`dict_code`),
KEY `idx_parent_id` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='组织架构表';
表中的数据:
太多了,自己资料中复制
4,根据页面效果分析数据接口:
数据字典是树形展示,由于数据众多,我们使用“树形数据与懒加载”的方式展现数据列表,其他就是对数据的新增、修改与删除操作,因此需要提供的接口如下:
1,根据上级id获取下级数据(构造树形数据),参考文档:https://element.eleme.cn/#/zh-CN/component/table,页面搜索:树形数据与懒加载
2,导入接口
3,导出接口
接下来我们封装服务器端数据接口,接口测试通过后再做页面渲染
1.1 搭建service-cmn模块,也是service父模块的子模块,与service_hosp同级
创建新模块:
1.2 修改配置
修改pom.xml
此pom:不修改
添加配置文件application.properties,复制一个hosp的配置文件,
然后修改端口和服务名称spring.application.name
我们将数据字典的表放在mysql中,操作的话用xxxmapper来操作数据库;
# 服务端口
server.port=8202
# 服务名
spring.application.name=service-cmn
# 环境设置:dev、test、prod
spring.profiles.active=dev
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/yygh_cmn?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
# springboot2.6.9等高版本继承swagger需要增加这个配置
#spring.mvc.pathmatch.matching-strategy=ant_path_matcher
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
#spring.data.mongodb.uri=mongodb://192.168.44.165:27017/yygh_hosp
# nacos服务地址
#spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
#rabbitmq地址
#spring.rabbitmq.host=192.168.44.165
#spring.rabbitmq.port=5672
#spring.rabbitmq.username=guest
#spring.rabbitmq.password=guest
#配置mapper xml文件的路径
#mybatis-plus.mapper-locations=classpath:com/atguigu/yygh/mapper/xml/*.xml
#mybatis-plus.mapper-locations=classpath:com/atguigu/yygh/mapper/xml/*.xml
# nacos服务地址
#spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
#开启sentinel
#feign.sentinel.enabled=true
#设置sentinel地址
#spring.cloud.sentinel.transport.dashboard=http://127.0.0.1:8858
#mongodb地址
#spring.data.mongodb.host=192.168.44.163
#spring.data.mongodb.port=27017
#spring.data.mongodb.database=yygh_hosp
#rabbitmq地址
#spring.rabbitmq.host=127.0.0.1
#spring.rabbitmq.port=5672
#spring.rabbitmq.username=guest
#spring.rabbitmq.password=guest
#logging.level.root=DEBUG
创建包:
1.3 启动类:
启动类:
package com.fan.yygh.cmn;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = "com.fan")
public class ServiceCmnApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceCmnApplication.class,args);
}
}
根据element组件要求,返回列表数据必须包含hasChildren字典,如图:
https://element.eleme.cn/#/zh-CN/component/table
2.1.1 model模块添加数据字典实体
在model模块查看实体:com.fan.yygh.model.cmn.Dict
构建项目包:
配置类:
2.1.2 添加数据字典mapper接口
//添加com.fan.yygh.cmn.mapper.DictMapper
public interface DictMapper extends BaseMapper {
}
2.1.3 添加数据字典service接口:
//1、添加com.fan.yygh.cmn.service.DictService
public interface DictService extends IService {
//根据数据id查询子数据列表
List findChlidData(Long id);
}
DictService中有一个泛型别忘记修改
2、添加com.fan.yygh.cmn.service.impl.DictServiceImpl接口实现:
2.1.4 添加数据字典controller:
添加com.fan.yygh.cmn.controller.DictController:
为了配合这个ui层级关系;
controller:
package com.fan.yygh.cmn.controller;
import com.fan.yygh.cmn.service.DictService;
import com.fan.yygh.common.result.Result;
import com.fan.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 javax.annotation.Resource;
import java.util.List;
@RestController
@Api(value = "数据字典接口")
@CrossOrigin
@RequestMapping("/admin/cmn/dict")
public class DictController {
@Resource
private DictService dictService;
//
@ApiOperation("根据数据id查询子数据列表")
@GetMapping("findChildData/{id}")
public Result findChildData(@PathVariable long id){
List list = dictService.findChildData(id);
return Result.ok(list);
}
}
=注意:数据字典的表存在于mysql数据库中,所以我们用xxxMapper进行查询;
package com.fan.yygh.cmn.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fan.yygh.cmn.mapper.DictMapper;
import com.fan.yygh.cmn.service.DictService;
import com.fan.yygh.model.cmn.Dict;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DictServiceImpl extends ServiceImpl implements DictService {
@Override
public List findChildData(long id) {
QueryWrapper wrapper = new QueryWrapper<>();
//即条件是parent_id==id,即字典的父级id等于参数id时,返回那些数据
wrapper.eq("parent_id",id);
List dictList = baseMapper.selectList(wrapper);
//向list集合每个dict对象中设置hasChildren字段值
for (Dict dict : dictList) {
Long dictId = dict.getId();//查出每个二级字典自己的主id
boolean isChild = this.isChildren(dictId);
dict.setHasChildren(isChild);
}
return dictList;
}
//判断id下面是否有子节点
private boolean isChildren(Long id){
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id",id);
Integer count = baseMapper.selectCount(queryWrapper);
return count>0;
}
}
用swagger测试后台数据:启动service_cmn服务和redis等:
id输入1 进行测试:
注意"hasChildren": true,字段;
{
"code": 200,
"message": "成功",
"data": [
{
"id": 86,
"createTime": "2019-06-10 18:43:35",
"updateTime": "2020-06-23 23:43:48",
"isDeleted": 0,
"param": {},
"parentId": 1,
"name": "省",
"value": "86",
"dictCode": "Province",
"hasChildren": true
},
{
"id": 100,
"createTime": "2022-08-02 03:17:57",
"updateTime": "2022-08-02 03:22:46",
"isDeleted": 0,
"param": {},
"parentId": 1,
"name": "电脑",
"value": "100",
"dictCode": "cc",
"hasChildren": true
},
{
"id": 10000,
"createTime": "2019-06-10 18:43:32",
"updateTime": "2020-06-15 19:26:44",
"isDeleted": 0,
"param": {},
"parentId": 1,
"name": "医院等级",
"value": null,
"dictCode": "Hostype",
"hasChildren": true
},
{
"id": 20000,
"createTime": "2019-06-10 18:43:32",
"updateTime": "2020-06-16 22:52:57",
"isDeleted": 0,
"param": {},
"parentId": 1,
"name": "证件类型",
"value": "20000",
"dictCode": "CertificatesType",
"hasChildren": true
},
{
"id": 30000,
"createTime": "2019-06-10 18:43:32",
"updateTime": "2020-06-09 01:26:39",
"isDeleted": 0,
"param": {},
"parentId": 1,
"name": "学历",
"value": "30000",
"dictCode": "Education",
"hasChildren": true
},
{
"id": 99100,
"createTime": "2019-06-11 18:39:11",
"updateTime": "2020-06-09 01:26:39",
"isDeleted": 0,
"param": {},
"parentId": 1,
"name": "民族",
"value": "99100",
"dictCode": "Nation",
"hasChildren": true
}
],
"ok": true
}
这里本来后端数据需要一个层级显示的,但是ui帮我们进行了一个封装;
支持树类型的数据的显示。当 row 中包含 children 字段时,被视为树形数据。渲染树形数据时,必须要指定 row-key。支持子节点数据异步加载。设置 Table 的 lazy 属性为 true 与加载函数 load 。通过指定 row 中的 hasChildren 字段来指定哪些行是包含子节点。children 与 hasChildren 都可以通过 tree-props 配置。
漏由设置:
/* 数据字典的漏由 */
{
path: '/cmn',
component: Layout,
redirect: '/cmn/list',
name: '数据字典管理',
alwaysShow:true, //总是显示
meta: { title: '数据字典管理', icon: 'example' },
children: [
{
path: 'list',
name: '数据字典列表',
component: () => import('@/views/dict/list'),
meta: { title: '数据字典列表', icon: 'table' }
}
]
},
views创建:
接口定义:dict.js
import request from '@/utils/request'
//定义接口路径的
export default {
//数据字典列表
dictList(id) {
return request ({
url: `/admin/cmn/dict/findChildData/${id}`,
method: 'get'
})
}
}
页面调用:
{{ scope.row.name }}
{{ row.dictCode }}
{{ scope.row.value }}
{{ scope.row.createTime }}
此方法很重要:
//1.数据字典列表
getDictList(id) {
dict.dictList(id) //调用api接口方法,==》调用后端查询方法
.then(response => {
this.list = response.data
})
},
//2、层级的递归遍历,前端ui帮我们做好了
//定义此方法作用,ui帮我们做好的递归查询
//getChildrens方法是关联的表格的这个:load="getChildrens"
getChildrens(tree, treeNode, resolve) {
//这里调用了api前端接口中的查询方法dictList,参数是下层节点的id,然后继续查询,返回的结果进行解析展示
dict.dictList(tree.id).then(response => {
resolve(response.data)
})
}
修改完端口号后,重启前端,查看:
由于前一个cmd终端没关掉,直接开启新的,9528端口被占用了:
换成高版本进行数据字典的下拉显示:
然后 npm install 从新下载依赖,并npm run dev ,测试:
lazy属性是我们点下拉按钮的时候才进行查询后台
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便。
EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。
文档地址:https://alibaba-easyexcel.github.io/index.html
github地址:https://github.com/alibaba/easyexcel
3.3.1 添加依赖
1,添加依赖,将依赖放到cmn模块中:
com.alibaba
easyexcel
2.1.1
2,导入导出需要定义对象,对象上需要引用easyexcel标签,所以model模块中的pom需要引入easyexcel依赖:如下:
com.alibaba
easyexcel
provided
3,导入导出我们会把它封装成工具类,放在common-util中,所有模块公用,所以该模块common-util也得引入easyexcel依赖:
com.alibaba
easyexcel
2.创建包和实体类:
测试:
package com.fan.easyexcel;
import com.alibaba.excel.EasyExcel;
import java.util.ArrayList;
public class TestWriter {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
UserDate userDate = new UserDate();
userDate.setUid(i);
userDate.setUsername("tom"+i);
list.add(userDate);
}
//设置excel文件路径和文件名称
String fileName = "F:\\excel\\01.xlsx";
//调用方法实现写操作
EasyExcel.write(fileName,UserDate.class).sheet("用户信息")
.doWrite(list);
}
}
启动项目测试:运行main方法进行测试:
注意了:刚才写入操作中高版本jdk可能会报错导致数据加载不到xlsx中 换成低版本的就可以了
读操作:
创建监听器:
package com.fan.easyexcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.util.ConverterUtils;
import java.util.Map;
public class ExcelListener extends AnalysisEventListener {
@Override
public void invoke(UserDate userDate, AnalysisContext analysisContext) {
System.out.println(userDate);
}
@Override
public void invokeHeadMap(Map headMap, AnalysisContext context) {
System.out.println("表头信息:"+headMap);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
测试类:
package com.fan.easyexcel;
import com.alibaba.excel.EasyExcel;
public class TestRead {
public static void main(String[] args) {
String fileName = "F:\\excel\\01.xlsx";
EasyExcel.read(fileName,UserDate.class,new ExcelListener())
.sheet().doRead();
}
}
和项目的集成实操:
在model模块添加导出实体:
在model模块查看实体:com.atguigu.yygh.vo.cmn.DictEeVo
然后:
controller:
//导出数据字典接口
@GetMapping("exportData")
public Result exportDict(HttpServletResponse response){
dictService.exportDictData(response);
return Result.ok();
}
实现类DictServiceImpl:
@Override //导出字典的接口
public void exportDictData(HttpServletResponse response) {
//设置下载信息
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = null;
try {
fileName = URLEncoder.encode("dict", "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
response.setHeader("Content-disposition", "attachment;filename="+ fileName + ".xlsx");
List dictList = baseMapper.selectList(null);
//Dict-->DictEevo
ArrayList dictVoList = new ArrayList<>();
for (Dict dict : dictList) {
DictEeVo dictEeVo = new DictEeVo();
BeanUtils.copyProperties(dict,dictEeVo);
dictVoList.add(dictEeVo);
}
//调用方法进行写操作
try {
EasyExcel.write(response.getOutputStream(),DictEeVo.class)
.sheet("dict")
.doWrite(dictVoList);
} catch (IOException e) {
e.printStackTrace();
}
}
前端:
列表页面添加导出按钮
src/views/cmn/dict/list.vue
导出按钮标签:
js添加导出方法:
exportData() {
window.location.href = 'http://localhost:8202/admin/cmn/dict/exportData'
}
测试:谷歌自动连接到迅雷下载了:
点击之后直接下载没有弹窗提示下载路径的,和浏览器设置有关,默认下载到浏览器下载地址
改造下载页面:
直接下载是和浏览器有关, 在设置里面有个:下载前询问每个文件的保存位置,选上就会有弹框了
导入数据字典的controller:
//导入数据字典的controller
@PostMapping("importData")
public Result importDict(MultipartFile file){
dictService.importDictData(file);
return Result.ok();
}
监听器:
spring构造器注入、还可以setting注入
实现类DictServiceImpl中:
@Override//导入数据字典的controller
public void importDictData(MultipartFile file) {
try {
EasyExcel.read(file.getInputStream(),DictEeVo.class,new DictListener(baseMapper))
.sheet().doRead();
} catch (IOException e) {
e.printStackTrace();
}
}
前端:
列表页面添加导入按钮,说明:按钮位置与导出并列
src/views/cmn/dict/list.vue
导入
点击上传
只能上传xls文件,且不超过500kb
添加弹出可见模型:
// 定义数据
data() {
return {
list: [],
listLoading: true,
dialogImportVisible: false //设置弹框是否弹出
}
}
测试:
id 上级id 名称 值 编码
100 1 电脑 100 cc
1001 100 华为电脑 1001 huawei
页面测试:重启后台:
看数据库:已经上传上去;将逻辑删除改成0看页面显示不:
缓存:
Spring Cache 是一个非常优秀的缓存组件。自Spring 3.1起,提供了类似于@Transactional注解事务的注解Cache支持,且提供了Cache抽象,方便切换各种底层Cache(如:redis)
使用Spring Cache的好处:
1,提供基本的Cache抽象,方便切换各种底层Cache;
2,通过注解Cache可以实现类似于事务一样,缓存逻辑透明的应用到我们的业务代码上,且只需要更少的代码就可以完成;
3,提供事务回滚时也自动回滚缓存;
4,支持比较复杂的缓存逻辑;
因为缓存也是公共使用,所有的service模块都有可能使用缓存,所以我们把依赖与部分配置加在service-util模块,这样其他service模块都可以使用了
1.1service-util添加依赖
在service-util模块的pom.xml添加依赖
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
2.6.0
1.2 service-util添加配置类
创建com.atguigu.yygh.common.config.RedisConfig
package com.fan.yygh.common.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.lang.reflect.Method;
import java.time.Duration;
@Configuration
@EnableCaching //开启缓存
public class RedisConfig {
/**
* 自定义key规则
* @return
*/
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
/**
* 设置RedisTemplate规则
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate
说明:
@EnableCaching:标记注解 @EnableCaching,开启缓存,并配置Redis缓存管理器。@EnableCaching 注释触发后置处理器, 检查每一个Spring bean 的 public 方法是否存在缓存注解。如果找到这样的一个注释, 自动创建一个代理拦截方法调用和处理相应的缓存行为。
1.3service-cmn添加redis配置
spring.redis.host=192.168.44.165
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
2.1.2 缓存@Cacheable
根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上。
查看源码,属性值如下:
属性/方法名 | 解释 |
---|---|
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间 |
cacheNames | 与 value 差不多,二选一即可 |
key | 可选属性,可以使用 SpEL 标签自定义缓存的key |
2.1.2 缓存@CachePut
使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上。
查看源码,属性值如下:
属性/方法名 | 解释 |
---|---|
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间 |
cacheNames | 与 value 差不多,二选一即可 |
key | 可选属性,可以使用 SpEL 标签自定义缓存的key |
2.1.3 缓存@CacheEvict
evict 逐出,赶出,收回,清空
使用该注解标志的方法,会清空指定的缓存。一般用在更新或者删除方法上
查看源码,属性值如下:
属性/方法名 | 解释 |
---|---|
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间 |
cacheNames | 与 value 差不多,二选一即可 |
key | 可选属性,可以使用 SpEL 标签自定义缓存的key |
allEntries | 是否清空所有缓存,默认为 false。如果指定为 true,则方法调用后将立即清空所有的缓存 |
beforeInvocation | 是否在方法执行前就清空,默认为 false。如果指定为 true,则在方法执行前就会清空缓存 |
改造com.fan.yygh.cmn.service.impl.DictServiceImpl类方法:
类似于事务的注解一样的使用:
查询方法需要的注解:
@Cacheable(value = “dict”,keyGenerator = “keyGenerator”)
导入方法需要的注解:
allEntries = true: 方法调用后清空所有缓存
@CacheEvict(value = “dict”, allEntries=true)
value: 缓存管理器中配置的缓存的名称,这里可以理解为一个组的概念,缓存管理器中可以有多套缓存配置,每套都有一个名称,类似于组名,这个可以配置这个值,选择使用哪个缓存的名称,配置后就会应用那个缓存名称对应的配置。
下面介绍一下 @Cacheable 这个注解常用的几个属性:
cacheNames/value :用来指定缓存组件的名字
key :缓存数据时使用的 key,可以用它来指定。默认是使用方法参数的值。(这个 key 你可以使用 spEL 表达式来编写)
keyGenerator :key 的生成器。 key 和 keyGenerator 二选一使用
cacheManager :可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。
condition :可以用来指定符合条件的情况下才缓存
unless :否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然你也可以获取到结果进行判断。(通过 #result 获取方法结果)
sync :是否使用异步模式。
① cacheNames
用来指定缓存组件的名字,将方法的返回结果放在哪个缓存中,可以是数组的方式,支持指定多个缓存。
新增:
启动后端和redis:测试:
前端页面查询后:
获取此key 对应的value:
配置nginx:
由于我们后端有很多服务模块,每个模块都有对应的访问路径与端口,为了提供统一的api接口,所以使用nginx作为反向代理服务器;
反向代理,其实客户端对代理是无感知的,因为客户端不需要任何配置就可以访问,我们只需要将请求发送到反向代理服务器,由反向代理服务器去选择目标服务器获取数据后,在返回给客户端,此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器IP地址。
1,下载安装nginx(window版)
2,配置nginx:
为了简单化,使用windows的nginx:
下载到本地解压:
server {
listen 80;
server_name localhost;
location /api/hosp/ {
proxy_pass http://localhost:8201;
}
location /api/cmn/ {
proxy_pass http://localhost:8202;
}
...
}
启动:nginx
注意:启动报错的话,把nginx文件夹单独放出来到根目录。重新启动;
3,调整/config/dev.env.js中的BASE_API
BASE_API: ‘http://localhost’
说明:
1、后续添加service模块自行添加nginx配置,不做说明
后续我们将了Spring Cloud Gateway网关,将替代nginx网关
测试:
提醒:这里只需要开启一个nginx即可,不需要在后端修改任何东西。前端只需要修改一个base路径即可;
然后提醒:linux的docker不稳定,linux中的redis服务和mongodb容易崩。建议将整个vm虚拟机重启。然后开启redis等;