# eddyoj-backend **Repository Path**: Eddyer/eddyoj-backend ## Basic Information - **Project Name**: eddyoj-backend - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-04-11 - **Last Updated**: 2024-05-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # spring boot 版本问题: 1,如果不是学习,那我建议最好选择稳定版本,也就是官方release版本。 2,我的习惯是一般是最新版本再往前推一个版本。比如目前是springboot 3.0 ,但是这个springboot 3.0要求JDK17 起步,好家伙这玩意谁敢上正式。 3,我觉得如果偏向新版本还是2.7X保险一点。 4,但是我个人比较喜欢选择带release的版本,而且后面整合spring cloud Alibaba 需要使用2.3.X所以我决定选择2.3.12.RELEASE版本。 https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E 此版本为2.7.2 没有适配 spring cloud # 1,集成mybatis-puls https://baomidou.com/pages/a61e1b/#dynamic-datasource 使用了 分页插件功能,按需添加功能,代码: MyBatisPlusConfig ### 1.2,使用mybatisx 生成代码 使用插件来生成代码 ,mybatisx 生成代码 ![image-20230721154057155](README.assets/image-20230721154057155.png) 表和字段是以"下划线"的形式来创建的 ~~~sql DROP TABLE IF EXISTS `t_user`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `t_user` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id', `user_name` varchar(256) DEFAULT NULL COMMENT '用户昵称', `profile` varchar(512) DEFAULT NULL COMMENT '个人简介', `user_account` varchar(256) DEFAULT NULL COMMENT '账号', `user_password` varchar(512) NOT NULL COMMENT '密码', `phone` varchar(128) DEFAULT NULL COMMENT '电话', `email` varchar(512) DEFAULT NULL COMMENT '邮箱', `user_status` int NOT NULL DEFAULT '0' COMMENT '状态 0 - 正常', `avatar_url` varchar(1024) DEFAULT NULL COMMENT '用户头像', `gender` tinyint DEFAULT NULL COMMENT '性别', `is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', `user_role` varchar(32) NOT NULL DEFAULT 'user' COMMENT '用户角色:user/admin/ban', `planet_code` varchar(512) DEFAULT NULL COMMENT '星球编号', `tags` varchar(1024) DEFAULT NULL COMMENT '标签 json 列表', `union_id` varchar(256) DEFAULT NULL COMMENT '开放平台id', `mp_open_id` varchar(256) DEFAULT NULL COMMENT '公众号openId', `revision` int DEFAULT NULL COMMENT '版本', `created_by` varchar(32) DEFAULT NULL COMMENT '创建人', `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updated_by` varchar(32) DEFAULT NULL COMMENT '更新人', `updated_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表'; /*!40101 SET character_set_client = @saved_cs_client */; ~~~ ![image-20230721154305104](README.assets/image-20230721154305104.png) ![image-20230721154408595](README.assets/image-20230721154408595.png) ![image-20230721161320079](README.assets/image-20230721161320079.png) ~~~shell 补充: 1.生成mapper.xml: 修改package name 为:generator.mapper 2,生成实体类代码如下 ~~~ ~~~java /** * 用户表 * @TableName t_user */ @TableName(value ="t_user") @Data public class User implements Serializable { /** * id */ @TableId(value = "id", type = IdType.AUTO) private Long id; /** * 用户昵称 */ @TableField(value = "user_name") private String userName; /** * 账号 */ @TableField(value = "user_account") private String userAccount; /** * 密码 */ @TableField(value = "user_password") private String userPassword; ... ~~~ ~~~shell 3,如果不想要注解(太麻烦),可以在applicaton.yml 中添加: mybatis-plus: configuration: #配置数据库下划线字段转驼峰: userName 适配 user_name 'mybatisplus 在进行查询时会进行pojo和字段的匹配' map-underscore-to-camel-case: true ~~~ 表以驼峰命名方式来创建的(推荐) ![image-20240402135647785](README.assets/image-20240402135647785.png) # 2, 集成druid https://blog.csdn.net/qq_44981526/article/details/126336010 访问: http://localhost:8101/api/druid/login.html 此版本使用了 starter 的jar 引入 ~~~xml com.alibaba druid-spring-boot-starter 1.1.17 ~~~ # 3,统一返回处理/异常处理 ### 3.1,统一返回处理 4xxx 开头的都是客户端的错误(请求参数等) 5xxx 开头的都是服务器的错误 ~~~json // 成功 { "code": 0 // 业务状态码 "data": { "name": "eddy" }, "message": "ok" } // 错误 { "code": 50001 // 业务状态码 "data": null "message": "用户操作异常、xxx" } ~~~ 代码:ResultUtils 使用: ![image-20240403154209983](README.assets/image-20240403154209983.png) ### 3.2,统一异常处理 1,定义业务异常类, extends RuntimeException 代码:BusinessException 2,通常的,需要定义 枚举类,来统一后端业务返回,更规范 代码:ErrorCode 3,使用 ![image-20240403154740693](README.assets/image-20240403154740693.png) 4,除此之外,我们还需要定义全局的异常处理 ~~~java /** * 全局异常处理器 * 不论是 controller 还是 services 都可以同一个地方处理 */ @RestControllerAdvice @Slf4j public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public BaseResponse businessExceptionHandler(BusinessException e) { log.error("businessException: " + e.getMessage(), e); return ResultUtils.error(e.getCode(), e.getMessage()); } @ExceptionHandler(RuntimeException.class) public BaseResponse runtimeExceptionHandler(RuntimeException e) { log.error("runtimeException", e); return ResultUtils.error(ErrorCode.SYSTEM_ERROR, e.getMessage()); } } ~~~ # 4,统一日志打印 aop 依赖: ~~~xml org.springframework.boot spring-boot-starter-aop ~~~ 代码:LogAspect # 5,统一跨域处理 代码:WebMvcConfig # 6,集成Swagger + Knife4j 接口文档 1. 引入依赖(Swagger 或 Knife4j:https://doc.xiaominfo.com/knife4j/documentation/get_start.html) 2. 自定义 Swagger 配置类 3. 定义需要生成接口文档的代码位置(Controller) 4. 千万注意:线上环境不要把接口暴露出去!!!可以通过在 SwaggerConfig 配置文件开头加上 `@Profile({"dev", "test"})` 限定配置仅在部分环境开启 5. 可以通过在 controller 方法上添加 @Api、@ApiImplicitParam(name = "name",value = "姓名",required = true) @ApiOperation(value = "向客人问好") 等注解来自定义生成的接口描述信息 6. 启动即可,api 为我们自己添加的路径 http://localhost:8080/api/doc.html#/home 7. 扩展:元数据,为什么swagger 可以提供不同的主题给到我们使用,不同主题的作者为什么能修改?依据是什么?答 :swagger 有自己的元数据,即提供接口文档信息的接口http://localhost:8080/api/v2/api-docs,我们可以获取这里的信息对 swagger ui 进行调整 此版本的集成 springboot 升级到2.6之后 ~~~xml com.github.xiaoymin knife4j-spring-boot-starter 3.0.3 ~~~ 解决bug ~~~java package com.eddy.project.config; import org.springframework.core.env.Environment; import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType; import org.springframework.boot.actuate.endpoint.ExposableEndpoint; import org.springframework.boot.actuate.endpoint.web.*; import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier; import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier; import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.util.StringUtils; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Knife4j 接口文档配置 * https://doc.xiaominfo.com/knife4j/documentation/get_start.html * */ @Configuration @EnableSwagger2 //@EnableOpenApi @Profile({"dev", "test"}) public class Knife4jConfig { @Bean public Docket defaultApi2() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(new ApiInfoBuilder() .title("接口文档") .description("springboot-init") .version("1.0") .build()) .select() // 指定 Controller 扫描包路径 .apis(RequestHandlerSelectors.basePackage("com.eddy.project.controller")) .paths(PathSelectors.any()) .build(); } /** * 解决bug * Failed to start bean ‘documentationPluginsBootstrapper’; nested exception is java.lang.NullPointerException * * * com.github.xiaoymin * knife4j-spring-boot-starter * 3.0.3 * * @return */ @Bean public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) { List> allEndpoints = new ArrayList<>(); Collection webEndpoints = webEndpointsSupplier.getEndpoints(); allEndpoints.addAll(webEndpoints); allEndpoints.addAll(servletEndpointsSupplier.getEndpoints()); allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints()); String basePath = webEndpointProperties.getBasePath(); EndpointMapping endpointMapping = new EndpointMapping(basePath); boolean shouldRegisterLinksMapping = webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT)); return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null); } } ~~~ # 7,集成EasyExcel https://easyexcel.opensource.alibaba.com/docs/current/ # 8,集成redis + spring session 解决分布式session 问题 1. 安装 Redis 官网:https://redis.io/ windows 下载: Redis 5.0.14 下载: 链接:https://github.com/tporadowski/redis/releases 选择 msi 版本 redis 管理工具 quick redis:https://quick123.net/ 2. 引入 redis,能够操作 redis: ```xml org.springframework.boot spring-boot-starter-data-redis ``` 3. 引入 spring-session 和 redis 的整合,**使得自动将 session 存储到 redis** 中: ```xml org.springframework.session spring-session-data-redis ``` 4. 修改 spring-session 存储配置 `spring.session.store-type` 默认是 none,表示存储在单台服务器 store-type: redis,表示从 redis 读写 session 5. 代码无侵入式,添加spring-session,对session的 存和取都不需要对代码进行修改,spring-session 会帮我们去redis 中进行获取 ~~~shell #存 session.setAttribute("login_user", username); #取 session.getAttribute("login_user"); ~~~ JWT 的优缺点:https://zhuanlan.zhihu.com/p/108999941 不适合存储用户的登录态,适合一次性的校验 # 9,缓存引入 Redis ~~~xml org.springframework.boot spring-boot-starter-data-redis ~~~ 解决redissonTemplate 插入Redis 乱码问题 代码:RedisTemplateConfig # 10,添加定时任务 + 分布式锁(redission) ~~~xml org.redisson redisson-spring-boot-starter 3.16.3 ~~~ 解决 redisson 插入Redis乱码问题 代码:RedissonConfig Redisson 是一个 java 操作 Redis 的客户端,**提供了大量的分布式数据集来简化对 Redis 的操作和使用,可以让开发者像使用本地集合一样使用 Redis,完全感知不到 Redis 的存在。** 代码:RedissonTest # 11,简单的权限校验 切面实现: 1,定义一个注解 ~~~java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AuthCheck { /** * 必须有某个角色 * * @return */ String mustRole() default ""; } ~~~ 2,在controller 中使用, ~~~java @AuthCheck(mustRole = UserConstant.ADMIN_ROLE) public BaseResponse addUser(@RequestBody UserAddRequest userAddRequest, HttpServletRequest request) { ~~~ 3,使用aop 编程来判定 代码: AuthInterceptor.java # 12,controller 层制定标准的 crud 模版 代码:PostController,PostService ![image-20240402182813034](README.assets/image-20240402182813034.png) # 13,集成微信开放平台 ~~~xml com.github.binarywang wx-java-mp-spring-boot-starter 4.4.0 ~~~ 配置: ~~~yml # 微信相关 wx: # 微信公众平台 # todo 需替换配置 mp: token: xxx aesKey: xxx appId: xxx secret: xxx config-storage: http-client-type: HttpClient key-prefix: wx redis: host: 127.0.0.1 port: 6379 type: Memory # 微信开放平台 # todo 需替换配置 open: appId: xxx appSecret: xxx ~~~ ![image-20240411105740565](README.assets/image-20240411105740565.png) 这里定义了对公众操作的基本处理 # 14,集成腾讯 oss ~~~xml com.qcloud cos_api 5.6.89 ~~~ 配置: ~~~yml # 对象存储 # todo 需替换配置 cos: client: accessKey: xxx secretKey: xxx region: xxx bucket: xxx ~~~ 代码: FileController.java # 15,集成es es(太重【吃服务器性能】,非必要可以不用,或者报错不处理) **不能注释掉 es jar 包,否则java bean 的引用会报错** ~~~xml org.springframework.boot spring-boot-starter-data-elasticsearch ~~~