尚医通项目--功能模块集成
一、Mybatis-Plus
使用Mybatis-Plus的项目模块:
Service包:
service-hosp医院服务接口模块
Mybatis-Plus的配置、集成以及CRUD操作请看我的另一篇博客
https://blog.csdn.net/tufhgddty/article/details/123332559?spm=1001.2014.3001.5501
二、Swagger2
引入依赖
<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId></dependency>
添加配置类
/ * Swagger2配置信息 */@Configuration@EnableSwagger2public class Swagger2Config { @Bean public Docket webApiConfig(){ return new Docket(DocumentationType.SWAGGER_2) .groupName("webApi") .apiInfo(webApiInfo()) .select() //只显示api路径下的页面 .paths(Predicates.and(PathSelectors.regex("/api/.*"))) .build(); } @Bean public Docket adminApiConfig(){ return new Docket(DocumentationType.SWAGGER_2) .groupName("adminApi") .apiInfo(adminApiInfo()) .select() //只显示admin路径下的页面 .paths(Predicates.and(PathSelectors.regex("/admin/.*"))) .build(); } private ApiInfo webApiInfo(){ return new ApiInfoBuilder() .title("网站-API文档") .description("本文档描述了网站微服务接口定义") .version("1.0") .contact(new Contact("atguigu", "http://atguigu.com", "493211102@qq.com")) .build(); } private ApiInfo adminApiInfo(){ return new ApiInfoBuilder() .title("后台管理系统-API文档") .description("本文档描述了后台管理系统微服务接口定义") .version("1.0") .contact(new Contact("atguigu", "http://atguigu.com", "49321112@qq.com")) .build(); }}
1.默认的访问地址
http://localhost:端口/swagger-ui.html;
2.如果配置文件中配置了 context-path:/目录
http://localhost:端口/目录/swagger-ui.html
swagger通过注解表明该接口会生成文档,包括接口名、请求方法、参数、返回信息的等等。
@Api:修饰整个类,描述Controller的作用
@ApiOperation:描述一个类的一个方法,或者说一个接口
@ApiParam:单个参数描述
@ApiModel:用对象来接收参数
@ApiModelProperty:用对象接收参数时,描述对象的一个字段
@ApiImplicitParam:一个请求参数 @ApiImplicitParams:多个请求参数
三、自定义全局异常处理
spring boot 默认情况下会映射到 /error 进行异常处理,但是提示并不十分友好,下面自定义异常处理,提供友好展示。
自定义全局异常类
我们在搭建模块时在common-util模块已经添加了YyghException类,这里不做解释
添加全局异常处理类
在common/common_util包里的exception文件夹下添加
@Data@ToString@ApiModel(value = "自定义全局异常类")public class YyghException extends RuntimeException { @ApiModelProperty(value = "异常状态码") private Integer code; / * 通过状态码和错误消息创建异常对象 * @param message * @param code */ public YyghException(String message, Integer code) { super(message); this.code = code; } / * 接收枚举类型对象 * @param resultCodeEnum */ public YyghException(ResultCodeEnum resultCodeEnum) { super(resultCodeEnum.getMessage()); this.code = resultCodeEnum.getCode(); }
package com.atguigu.yygh.common.exception;/ * 全局异常处理类 * */@ControllerAdvice 【@ControllerAdvice来声明一些全局性的东西,最常见的是结合@ExceptionHandler注解用于全局异常的处理】public class GlobalExceptionHandler {@ExceptionHandler(Exception.class) 【异常处理Handler注解,参数为异常类型,即出现哪种异常用这个异常处理Hanlder方法】@ResponseBody 【让返回的结果可以输出(以Json格式)】public Result error(Exception e){ e.printStackTrace(); 【输出异常内容】return Result.fail(); 【前面集成的,自定义返回结果】 }/ * 自定义异常处理方法 * @param e* @return*/@ExceptionHandler(YyghException.class)@ResponseBodypublic Result error(YyghException e){return Result.build(e.getCode(), e.getMessage()); 【根据传入的Code和Message实现返回自定义异常结果】 }}
自定义异常处理方法需要手动抛出异常,并且传入自定义的参数:错误信息Message和错误状态码Code
四、LogBack
配置日志级别
日志记录器(Logger)的行为是分等级的。如下表所示:
分为:OFF关闭、FATAL致命、ERROR错误、WARN警告、INFO消息、DEBUG调试、ALL所有
默认情况下,spring boot从控制台打印出来的日志级别只有INFO及以上级别,可以配置日志级别
在application.properties设置日志级别
logging.level.root=WARN
这种方式只能将日志打印在控制台上
LogBack日志
spring boot内部使用Logback作为日志实现的框架。
Logback和log4j非常相似,如果你对log4j很熟悉,那对logback很快就会得心应手。
配置 resources/logback-spring.xml(能看懂就行,不要求掌握)
<configuration scan="true" scanPeriod="10 seconds"> <!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR <contextName>logback</contextName> <property name="log.path" value="D:/yygh_log/edu" /> <property name="CONSOLE_LOG_PATTERN"value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> <encoder> <Pattern>${CONSOLE_LOG_PATTERN}</Pattern> <charset>UTF-8</charset> </encoder> </appender> <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/log_info.log</file> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15</maxHistory> </rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/log_warn.log</file> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15</maxHistory> </rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>warn</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/log_error.log</file> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <maxHistory>15</maxHistory> </rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 用来设置某一个包或者具体的某一个类的日志打印级别、以及指定。 仅有一个name属性, 一个可选的level和一个可选的addtivity属性。 name:用来指定受此logger约束的某一个包或者具体的某一个类。 level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,如果未设置此属性,那么当前logger将会继承上级的级别。 --> <!-- 使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作: 第一种把改成这样就会打印sql,不过这样日志那边会出现很多其他消息 第二种就是单独给mapper下目录配置DEBUG模式,代码如下,这样配置sql语句会打印,其他还是正常DEBUG级别: --> <springProfile name="dev"> <logger name="com.guli" level="INFO" /> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> </root> </springProfile> <springProfile name="pro"> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="DEBUG_FILE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="ERROR_FILE" /> <appender-ref ref="WARN_FILE" /> </root> </springProfile></configuration>
五、EasyExcel
EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel
官方文档https://www.yuque.com/easyexcel/doc/easyexcel
添加依赖,在service-cmn接口模块
<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.1.1</version> </dependency></dependencies>
添加依赖,在model模块,因为需要在实体类加上EasyExcel的注解
【注意此处加了provided】对标签的描述点这里
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><scope>provided </scope> </dependency>
添加依赖,在common-util模块,因为后面要把导入导出封装为所有模块公用的工具类
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId></dependency>
实体类,在model模块的com.atguigu.yygh.vo.cmn.DictEeVo
package com.atguigu.yygh.vo.cmn;@Datapublic class DictEeVo {@ExcelProperty(value = "id",index = 0) private Long id;@ExcelProperty(value = "上级id",index = 1)private Long parentId;@ExcelProperty(value = "名称",index = 2)private String name;@ExcelProperty(value = "值",index = 3)private String value;@ExcelProperty(value = "编码",index = 4)private String dictCode;}
接口方法,在DictService
/ * 导出 * @param response*/void exportData(HttpServletResponse response);
实现类方法,在DictServiceImpl
@Overridepublic void exportData(HttpServletResponse response) {try { response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系String fileName = URLEncoder.encode("数据字典", "UTF-8"); response.setHeader("Content-disposition", "attachment;filename="+ fileName + ".xlsx"); List<Dict> dictList = dictMapper.selectList(null); List<DictEeVo> dictVoList = new ArrayList<>(dictList.size());for(Dict dict : dictList) { DictEeVo dictVo = new DictEeVo(); BeanUtils.copyBean(dict, dictVo, DictEeVo.class); dictVoList.add(dictVo); } EasyExcel.write(response.getOutputStream(), DictEeVo.class).sheet("数据字典").doWrite(dictVoList); } catch (IOException e) { e.printStackTrace(); }}
Controller类方法,在DictController
@ApiOperation(value="导出")@GetMapping(value = "/exportData")public void exportData(HttpServletResponse response) {dictService.exportData(response);}
六、Spring Cache
因为缓存也是公共使用,所有的service模块都有可能使用缓存,所以我们把依赖与部分配置加在service-util模块,这样其他service模块都可以使用了
添加依赖
service-util模块的pom.xml
<!-- redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- spring2.X集成redis所需common-pool2--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.6.0</version></dependency>
service-util添加配置类
创建com.atguigu.yygh.common.config.RedisConfig
package com.atguigu.yygh.common.config;@Configuration@EnableCachingpublic class RedisConfig {/ * 自定义key规则 * @return*/@Beanpublic KeyGenerator keyGenerator() {return new KeyGenerator() {@Overridepublic 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*/@Beanpublic RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper();// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和publicom.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om);//序列号key valueredisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); redisTemplate.afterPropertiesSet();return redisTemplate; }/ * 设置CacheManager缓存规则 * @param factory* @return*/@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) { RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om);// 配置序列化(解决乱码的问题),过期时间600秒RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(600)) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager.builder(factory) .cacheDefaults(config) .build();return cacheManager; }}
说明:
@EnableCaching:标记注解
@EnableCaching,开启缓存,并配置Redis缓存管理器。
@EnableCaching 注释触发后置处理器,检查每一个Spring bean 的 public 方法是否存在缓存注解。如果找到这样的一个注释, 自动创建一个代理拦截方法调用和处理相应的缓存行为。
service-cmn添加redis配置
application.properties
spring.redis.host=192.168.44.165spring.redis.port=6379spring.redis.database= 0spring.redis.timeout=1800000spring.redis.lettuce.pool.max-active=20spring.redis.lettuce.pool.max-wait=-1#最大阻塞等待时间(负数表示没限制)spring.redis.lettuce.pool.max-idle=5spring.redis.lettuce.pool.min-idle=0
使用Spring Cache
@Cacheable
根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上。
属性/方法名 | 解释 |
---|---|
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间 |
cacheNames | 与 value 差不多,二选一即可 |
key | 可选属性,可以使用 SpEL 标签自定义缓存的key |
@CachePut
使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上。
参数与@Cacheable一样
@CacheEvict
使用该注解标志的方法,会清空指定的缓存。一般用在更新或者删除方法上
参数比@Cacheable多两个
属性/方法名 | 解释 |
---|---|
allEntries | 是否清空所有缓存,默认为 false。如果指定为true,则方法调用后将立即清空所有的缓存 |
beforeInvocation | 是否在方法执行前就清空,默认为 false。如果指定为 true,则在方法执行前就会清空缓存 |
应用:改造com.atguigu.yygh.cmn.service.impl.DictServiceImpl类方法
/ * 根据上级id获取子节点数据列表 * @param parentId*/@Cacheable(value = "dict",keyGenerator = "keyGenerator")@Overridepublic List<Dict> findByParentId(Long parentId) {List<Dict> dictList = dictMapper.selectList(new QueryWrapper<Dict>().eq("parent_id", parentId));dictList.stream().forEach(dict -> {boolean isHasChildren = this.isHasChildren(dict.getId()); dict.setHasChildren(isHasChildren);});return dictList;}/ * 导入 * allEntries = true: 方法调用后清空所有缓存 * @param file*/@CacheEvict(value = "dict", allEntries=true)@Overridepublic void importData(MultipartFile file) { ExcelHelper fileHelper = new ExcelHelper(DictEeVo.class); List<DictEeVo> dictVoList = fileHelper.importExcel(file);if(!CollectionUtils.isEmpty(dictVoList)) {dictMapper.insertBatch(dictVoList); }}
七、Redis
即将根据项目写一篇专门的博客
八、Nginx
下载安装nginx(window版)
在nginx.conf里的http{ }中添加
server { listen9001; server_name localhost;location ~ /hosp/ { proxy_pass http://localhost:8201;}location ~ /cmn/ { proxy_pass http://localhost:8202;}}
前端 调整/config/dev.env.js中的BASE_API
BASE_API: 'http://localhost:9001'
九、MongoDB
MongoDB的CRUD操作请看另一篇博客
https://blog.csdn.net/tufhgddty/article/details/123540812
十、图片Bash64编码工具类
十一、SpringCloud Nacos
十二、SpringCloud Feign
十三、Spring Cloud Gateway
十四、JWT
十五、阿里云短信服务
十六、用户认证与网关整合
十七、OAuth2 微信登录
十八、阿里云OSS对象存储服务
十九、RabbitMQ
暂时有一篇博客
https://blog.csdn.net/tufhgddty/article/details/120073169?spm=1001.2014.3001.5501
即将根据项目写一篇专门的博客