# 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 生成代码

表和字段是以"下划线"的形式来创建的
~~~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 */;
~~~



~~~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
~~~
表以驼峰命名方式来创建的(推荐)

# 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
使用:

### 3.2,统一异常处理
1,定义业务异常类, extends RuntimeException
代码:BusinessException
2,通常的,需要定义 枚举类,来统一后端业务返回,更规范
代码:ErrorCode
3,使用

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

# 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
~~~

这里定义了对公众操作的基本处理
# 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
~~~