SpringBoot

SpringBoot项目建立

新建项目,勾选依赖项,如:

Developer Tools中的Lombok(通过注释简化java开发)

Web中的Spring Web(相当于SpringMVC)

SQL中的Mybatis Framework(Mybatis的框架)和MySQL Driver(MySQL的驱动)

image-20221221201317655

数据库设计

通过Navicat创造新的MySQL数据库,并设计表格的名称,类型,以便于后端拿数据

SpringBoot各级设计

配置类编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#--------------------------通用配置-------------------------
spring:
application:
# 应用名
name: novel
server:
port: 80
--- #---------------------数据库配置---------------------------
spring:
datasource:

driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/novel?useUnicode=true&characterEncoding=utf-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
username: root
password: Tec@258011
--- #---------------------自定义配置----------------------------
novel:
# 跨域配置
cors:
# 允许跨域的域名
allow-origins:
- http://localhost:1024
- http://localhost:8888
- http://localhost:8080


--- #配置mapper xml文件的路径(找不到xml的问题,最后是用config层的MybatisPlusConfig的相关配置来解决的)

mybatis-plus:
mapper-locations: classpath:/mapper/*.xml

mybatis:
configuration:
mapUnderscoreToCamelCase=true:

# JSON 序列化时,将所有 Number 类型的属性都转为 String 类型返回,避免前端数据精度丢失的问题。(Long类型最多17位,超过的部分会变成相同数量的0,卡了一个下午加一个过了考试周的晚上QAQ,问题一开始在前端收到的id与数据库不一致,然后发现F12中的预览和响应不同,而响应是对的)
spring:
jackson:
generator:
write_numbers_as_strings: true

Dao层编写

数据访问对象,负责封装对数据库的CRUD操作,与数据库进行交互,一般是mapper写接口,xml文件写sql语句的形式。

包括entity层(实体层)编写以及Mapper层编写

entity层(实体层)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//@TableName:如果数据库的表名是home_book,但是实体类的名字不同,那么就需要借助TableName去做绑定  implements Serializable:一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才是可序列化的。因此如果要序列化某些类的对象,这些类就必须实现Serializable接口。而实际上,Serializable是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。
@TableName("home_book")
public class HomeBook implements Serializable {

private static final long serialVersionUID = 1L;
//@TableId:主键专属比如数据中的表中的字段是id,但是实体类是userId,那么就需要在userId上打上这个注解,用法:设置主键映射 value映射主键字段的名字
@TableId(value = "id", type = IdType.AUTO)
private Long id;

private Integer type;

private Integer sort;

private Long bookId;

private LocalDateTime createTime;

private LocalDateTime updateTime;
}

Mapper层

不要忘了在启动类下@MapperScan(“com.tec.vuepractice.dao.mapper”),这样在包扫描的时候就不会漏掉mapper层

1
2
3
//extends BaseMapper<HomeBook>是mybaits-plus的组件,用来进行基础的增删改查
public interface HomeBookMapper extends BaseMapper<HomeBook> {
}

Service层编写+Impl(实现层)

更加关注业务逻辑,是业务处理层,将manager组合过的操作和业务逻辑组合在一起,再封装成业务操作。

service层

1
2
3
4
5
6
7
8
9
public interface HomeService {

/**
* 查询首页小说推荐列表
*
* @return 首页小说推荐列表的 rest 响应结果
*/
RestResp<List<HomeBookRespDto>> listHomeBooks();
}

serviceImpl层

1
2
3
4
5
6
7
8
9
10
11
@Service
@RequiredArgsConstructor
//implements:一个类通过关键字implements声明自己使用一个或者多个接口。
public class HomeServiceImpl implements HomeService {
private final HomeBookCacheManager homeBookCacheManager;

@Override
public RestResp<List<HomeBookRespDto>> listHomeBooks() {
return RestResp.ok(homeBookCacheManager.listHomeBooks());
}
}

Controller层编写

主要负责接受前台的数据和请求,并且在底层处理完之后把结果返回回去,一般不能写业务逻辑在这一层,因为第一造成了不可复用,第二以后的维护困难,第三这一层没有上层,如果给用户返回了奇怪的错误信息将会非常丑陋。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//@RestController:作用等同于@Controller + @ResponseBody
//@Controller:在一个类上添加@Controller注解,表明了这个类是一个控制器类
//@RestController:用其实是将java对象转为json格式的数据
@RestController
//@RequestMapping:映射URL
@RequestMapping(ApiRouterConsts.API_FRONT_HOME_URL_PREFIX)//(/home)
//在springboot项目中,controller或service层中需要注入多个mapper接口或者另外的service接口,这时候代码中就会有多个@AutoWired注解,使得代码看起来非常的混乱。
//@RequiredArgsConstructor写在类上面可以代替@AutoWired注解,需要注意的是:在注入的时候需要用final定义,或者使用@notnull注解
@RequiredArgsConstructor
public class HomeController {
//注入service
private final HomeService homeService;

/**
* 首页小说推荐查询接口
*/
@GetMapping("books")
public RestResp<List<HomeBookRespDto>> listHomeBooks() {
return homeService.listHomeBooks();
}
//@PathVariable("chapterId")(卡了3.1一整晚加上3.2早上一小时QAQ)
//@PathVariable 映射 URL 绑定的占位符
//通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:URL 中的 {xxx} 占位符可以通过
@GetMapping("content/{chapterId}")
public RestResp<BookContentAboutRespDto> getBookContentAbout(@PathVariable("chapterId") Long chapterId) {
log.info("查询方法调用之前,参数id={}", chapterId);
return bookService.getBookContentAbout(chapterId);
}

Dto层编写

传输对象,一般是把数据库表封装成对象,表的各个字段就是该对象的各个变量

包括req层(Http 请求数据封装)resp层(Http 响应数据封装)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/**
* 小说信息 响应DTO
*
* @author xiongxiaoyang
* @date 2022/5/15
*/
//@Data:@Data : 注在类上,提供类的get、set、equals、hashCode、canEqual、toString方法
//@AllArgsConstructor : 注在类上,提供类的全参构造
//@NoArgsConstructor : 注在类上,提供类的无参构造
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BookInfoRespDto {

/**
* ID
*/
//@Schema:Swagger3的注解,用于输出api文档,此处提供了描述
@Schema(description = "小说ID")
private Long id;

/**
* 类别ID
*/
@Schema(description = "类别ID")
private Long categoryId;

/**
* 类别名
*/
@Schema(description = "类别名")
private String categoryName;

/**
* 小说封面地址
*/
@Schema(description = "小说封面地址")
private String picUrl;

/**
* 小说名
*/
@Schema(description = "小说名")
private String bookName;

/**
* 作家id
*/
@Schema(description = "作家id")
private Long authorId;

/**
* 作家名
*/
@Schema(description = "作家名")
private String authorName;

/**
* 书籍描述
*/
@Schema(description = "书籍描述")
private String bookDesc;

/**
* 书籍状态;0-连载中 1-已完结
*/
@Schema(description = "书籍状态;0-连载中 1-已完结")
private Integer bookStatus;

/**
* 点击量
*/
@Schema(description = "点击量")
private Long visitCount;

/**
* 总字数
*/
@Schema(description = "总字数")
private Integer wordCount;

/**
* 评论数
*/
@Schema(description = "评论数")
private Integer commentCount;

/**
* 首章节ID
*/
@Schema(description = "首章节ID")
private Long firstChapterId;

/**
* 最新章节ID
*/
@Schema(description = "最新章节ID")
private Long lastChapterId;

/**
* 最新章节名
*/
@Schema(description = "最新章节名")
private String lastChapterName;

/**
* 最新章节更新时间
*/
@Schema(description = "最新章节更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime updateTime;


}

manager层编写

负责将Dao层中的数据库操作组合复用,主要是一些缓存方案,中间件的处理,以及对第三方平台封装的层。

cache层编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@Component
@RequiredArgsConstructor
public class HomeBookCacheManager {
private final HomeBookMapper homeBookMapper;

private final BookInfoMapper bookInfoMapper;

/**
* 查询首页小说推荐,并放入缓存中(此处没有采用缓存策略)
*/
public List<HomeBookRespDto> listHomeBooks() {
// 从首页小说推荐表中查询出需要推荐的小说
QueryWrapper<HomeBook> queryWrapper = new QueryWrapper<>();
//orderByAsc:按照DatabaseConsts.CommonColumnEnum.SORT.getName()排序
queryWrapper.orderByAsc(DatabaseConsts.CommonColumnEnum.SORT.getName());
List<HomeBook> homeBooks = homeBookMapper.selectList(queryWrapper);

// 获取推荐小说ID列表
if (!CollectionUtils.isEmpty(homeBooks)) {
List<Long> bookIds = homeBooks.stream()
.map(HomeBook::getBookId)
.toList();

// 根据小说ID列表查询相关的小说信息列表
QueryWrapper<BookInfo> bookInfoQueryWrapper = new QueryWrapper<>();
bookInfoQueryWrapper.in(DatabaseConsts.CommonColumnEnum.ID.getName(), bookIds);
List<BookInfo> bookInfos = bookInfoMapper.selectList(bookInfoQueryWrapper);

// 组装 HomeBookRespDto 列表数据并返回
if (!CollectionUtils.isEmpty(bookInfos)) {
Map<Long, BookInfo> bookInfoMap = bookInfos.stream()
.collect(Collectors.toMap(BookInfo::getId, Function.identity()));
return homeBooks.stream().map(v -> {
BookInfo bookInfo = bookInfoMap.get(v.getBookId());
HomeBookRespDto bookRespDto = new HomeBookRespDto();
bookRespDto.setType(v.getType());
bookRespDto.setBookId(v.getBookId());
bookRespDto.setBookName(bookInfo.getBookName());
bookRespDto.setPicUrl(bookInfo.getPicUrl());
bookRespDto.setAuthorName(bookInfo.getAuthorName());
bookRespDto.setBookDesc(bookInfo.getBookDesc());
return bookRespDto;
}).toList();

}

}

return Collections.emptyList();
}
}

一般的业务的写法

(自己写的,对以后的业务编写挺有参考意义的,感动QAQ)

上面的manager层编写业务的写法主要是在使用缓存时使用

一般在写业务逻辑的时候还是在service层下的Impl层比较常用,下面是该业务的常规写法

mapper层

先准备好需要的mapper接口

HomeBookMapper
1
2
public interface HomeBookMapper extends BaseMapper<HomeBook> {
}
BookInfoMapper
1
2
3
public interface BookInfoMapper extends BaseMapper<BookInfo> {

}
service层

然后在service层下定义好方法

1
2
3
4
public interface HomeService extends IService<HomeBook> {

List<HomeBookRespDto> listHomeBooks();
}
impl层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
@Service
@RequiredArgsConstructor
public class HomeServiceImpl extends ServiceImpl<HomeBookMapper, HomeBook> implements HomeService {
//@Autowired可能会导致找不到Mapper的bean(常见错误),而使用@Resource则会按照名字来搜素,可以解决找不到Mapper的bean的问题
@Resource
private final HomeBookMapper homeBookMapper;
@Resource
private final BookInfoMapper bookInfoMapper;

@Override
public List<HomeBookRespDto> listHomeBooks() {
// 从首页小说推荐表中查询出需要推荐的小说
QueryWrapper<HomeBook> queryWrapper = new QueryWrapper<>();
// orderByAsc(实现递增排序)
// orderByDesc(实现递减排序)
queryWrapper.orderByAsc(DatabaseConsts.CommonColumnEnum.SORT.getName());
//selectlist:根据 entity 条件,查询全部记录
//selectOne:根据 entity 条件,查询一条记录
List<HomeBook> homeBooks = homeBookMapper.selectList(queryWrapper);
//CollectionUtils.isEmpty(list): false
//CollectionUtils.isNotEmpty(list): true


//这段代码的功能是将一个名为homeBooks的集合中的HomeBook对象的bookId属性取出来,并将它们组成一个Long类型的List集合bookIds。
//代码的执行过程如下:
//1、判断homeBooks集合是否为空,使用了CollectionUtils.isEmpty()方法来判断,该方法是Spring框架提供的一个工具类方法,用于判断集合是否为空。

//2、如果homeBooks集合不为空,则使用Java 8中的Stream流对集合进行处理,调用map()方法将HomeBook对象中的bookId属性取出来,然后使用toList()方法将结果转换为List集合。

//3、最终得到一个名为bookIds的List集合,其中包含了homeBooks集合中所有HomeBook对象的bookId属性值。

// 获取推荐小说ID列表
if (!CollectionUtils.isEmpty(homeBooks)) {
List<Long> bookIds = homeBooks.stream()
.map(HomeBook::getBookId)
.toList();

//这段代码的功能是根据给定的bookIds列表,从数据库中查询对应的BookInfo记录,并将查询结果存储在一个名为bookInfos的List集合中。
//代码的执行过程如下:
//1、创建一个QueryWrapper对象bookInfoQueryWrapper,该对象是MyBatis-Plus框架提供的一个查询条件构造器,可以通过该对象构建查询条件。
//2、调用QueryWrapper对象的in()方法,该方法用于构建一个in查询条件,查询的是数据库中名为ID的列,该列的值必须包含在bookIds列表中。
//3、创建一个名为bookInfos的List集合,用于存储查询结果。
//4、调用bookInfoMapper对象的selectList()方法,该方法用于执行查询操作,查询符合指定条件的BookInfo记录,并将查询结果存储在bookInfos集合中。
//5、最终得到一个名为bookInfos的List集合,其中包含了数据库中所有ID值在bookIds列表中的BookInfo记录。

// 根据小说ID列表查询相关的小说信息列表
QueryWrapper<BookInfo> bookInfoQueryWrapper = new QueryWrapper<>();
bookInfoQueryWrapper.in(DatabaseConsts.CommonColumnEnum.ID.getName(), bookIds);
List<BookInfo> bookInfos = bookInfoMapper.selectList(bookInfoQueryWrapper);

//这段代码的功能是将查询出来的BookInfo记录与给定的HomeBook集合进行关联,并将结果组成一个名为bookRespDto的List集合返回。
//代码的执行过程如下:
//1、判断查询结果bookInfos是否为空,如果为空,则直接返回一个空的List集合。
//2、如果bookInfos不为空,则将查询结果转换为一个Map集合bookInfoMap,以BookInfo记录的id作为key,BookInfo对象本身作为value,使用Java 8中的Stream流对查询结果进行处理,调用collect()方法和toMap()方法实现转换。
//3、使用Java 8中的Stream流和Lambda表达式对给定的HomeBook集合进行处理,调用map()方法将每个HomeBook对象映射为一个HomeBookRespDto对象,其中的属性值从关联的BookInfo对象中获取。
//4、使用Map集合bookInfoMap中的get()方法获取每个HomeBook对象关联的BookInfo对象,然后将获取到的属性值设置到HomeBookRespDto对象中,最终得到一个名为bookRespDto的List集合。
//5、返回名为bookRespDto的List集合,其中包含了与HomeBook集合关联的BookInfo记录的属性信息。

// 组装 HomeBookRespDto 列表数据并返回
if (!CollectionUtils.isEmpty(bookInfos)) {
Map<Long, BookInfo> bookInfoMap = bookInfos.stream()
.collect(Collectors.toMap(BookInfo::getId, Function.identity()));
return homeBooks.stream().map(v -> {
BookInfo bookInfo = bookInfoMap.get(v.getBookId());
HomeBookRespDto bookRespDto = new HomeBookRespDto();
bookRespDto.setType(v.getType());
bookRespDto.setBookId(v.getBookId());
bookRespDto.setBookName(bookInfo.getBookName());
bookRespDto.setPicUrl(bookInfo.getPicUrl());
bookRespDto.setAuthorName(bookInfo.getAuthorName());
bookRespDto.setBookDesc(bookInfo.getBookDesc());
return bookRespDto;
}).toList();

}

}
return Collections.emptyList();
}
}
衍生QueryWrapper的知识点

orderByAsc:实现递增排序

orderByDesc:实现递减排序

衍生BaseMapper接口CRUD的知识点

引入:extends BaseMapper

selectlist:根据 entity 条件,查询全部记录
selectOne:根据 entity 条件,查询一条记录

衍生Service接口CRUD

引入implements IService(T:实体层)

衍生的工具类

CollectionUtils.isEmpty(list): false

CollectionUtils.isNotEmpty(list): true

上面的!CollectionUtils.isEmpty(bookInfos)其实相当于第二种,也同时是第一种判断为flase后相反(!)为true

衍生的final的作用

在这里将 HomeBookMapper 声明为 final 主要是出于两个原因:
1、线程安全:当我们将一个对象声明为 final 时,它的引用就不能再指向其他对象,也就是说这个对象是不可变的,这可以避免多线程访问时发生冲突。在Spring中,@Autowired 自动注入的属性默认是可变的,将其声明为 final 可以使其不可变,从而提高线程安全性。
2、提高可读性:使用 final 关键字来声明变量,可以让人们更加明确地知道这个变量不会被修改,也可以让编译器进行更多的优化,提高代码的性能。此外,通过将类中的属性声明为 final,可以让读者更容易地看出这个属性是类的“特征”,不会在运行过程中被修改。
总的来说,将 HomeBookMapper 声明为 final 可以提高代码的可读性和线程安全性。

衍生的stream的知识点:
 .stream是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
 .stream.map(方法):其中的方法获取了小说ID,而map是一种键值对存储结构,用于存储一组不重复的键和对应的值,例如此处的ID键和它所对应的值

​ .toList():将数组转化为List

衍生的Lambda表达式的知识点:

Lambda表达式:(方法参数) ->{方法实现}
(单个参数可以省略括号)

  lambda遍历List集合
          集合的遍历,采用lambda表达式会更简洁:
1
2
3
4
5
6
7
8
9
10
11
12
13
// Java 8之前:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String feature : features) {
System.out.println(feature);
}

// Java 8之后:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
features.forEach(n -> System.out.println(n));

// 使用Java 8的方法引用更方便,方法引用由::双冒号操作符标示,
features.forEach(System.out::println);
//方法引用是使用两个冒号::这个操作符号。
衍生的toMap(BookInfo::getId, Function.identity())解释:

​ Function.identity() 是一个静态方法,它返回一个函数,这个函数接收一个参数并返回其自身。也就是说,它返回一个标识函数,将输入对象直接返回,不做任何处理。
​ 在这个例子中,Function.identity() 方法用作 toMap() 方法的第二个参数。toMap() 方法期望一个将键映射到值的函数,所以使用 Function.identity() 将对象自身作为键的映射值。因此,对于一个 BookInfo 对象 bookInfo,bookInfo.getId() 作为键,bookInfo 自身作为值。
​ 简而言之,这里使用 Function.identity() 是为了将 BookInfo 对象本身作为值,方便后续从 Map 中获取对象并使用其属性。

stream流(List bookIds = homeBooks.stream())\

在 Java8 之前,我们通常是通过 for 循环或者 Iterator 迭代来重新排序合并数据,又或者通过重新定义 Collections.sorts 的 Comparator 方法来实现,这两种方式对于大数据量系统来说,效率并不是很理想。Stream 的聚合操作与数据库 SQL 的聚合操作 sorted、filter、map 等类似。我们在应用层就可以高效地实现类似数据库 SQL 的 聚合操作了,而在数据操作方面,Stream 不仅可以通过串行的方式实现数据操作,还可以通过并行的方式处理大批量数据,提高数据 的处理效率

总结的方法:

1、List类型的数据库查找方法

主要是return部分

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public List<BookInfoRespDto> listHomeCategory() {
QueryWrapper<BookInfo>CategoryQueryWrapper = new QueryWrapper<>();
return bookInfoMapper.selectList(CategoryQueryWrapper).stream()
.map(v -> BookInfoRespDto.builder()
.id(v.getId())
.categoryId(v.getCategoryId())
.categoryName(v.getCategoryName())
.bookDesc(v.getBookDesc())
.bookName(v.getBookName())
.build()).toList();
}

2、Dto类型的数据库查找方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public BookChapterRespDto getChapter(Long chapterId){
QueryWrapper<BookChapter>chapterQueryWrapper=new QueryWrapper<>();
chapterQueryWrapper.eq("id", chapterId);
BookChapter bookChapter = bookChapterMapper.selectOne(chapterQueryWrapper);
return BookChapterRespDto.builder()
//.BookChapterRespDto的属性(bookChapter的get方法),这样就把筛选过的数据存入Dto中
.id(chapterId)
.bookId(bookChapter.getBookId())
.chapterNum(bookChapter.getChapterNum())
.chapterName(bookChapter.getChapterName())
.chapterWordCount(bookChapter.getWordCount())
.chapterUpdateTime(bookChapter.getUpdateTime())
.build();
}
controller层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RestController
@RequestMapping(ApiRouterConsts.API_FRONT_HOME_URL_PREFIX)
@RequiredArgsConstructor
public class HomeController {

//final的作用:同impl层
private final HomeBookMapper homeBookMapper;

private final BookInfoMapper bookInfoMapper;

private final HomeService homeService;

@GetMapping("books")
//因为前端axios作检验的时候要看code,所以这里必须要用通用返回类RestResp<T>
public RestResp<List<HomeBookRespDto>> listHomeBooks() {
//这里使用了通用返回类RestResp的方法RestResp<T>(T data)
return RestResp.ok(homeService.listHomeBooks());
//直接这么写会报错,刚好点击报错,就可以直接帮你在service层写出这个方法,算小技巧吧,同样,直接这么在service层创建也会报错,同样的方式,会帮你在serviceIpl层创建好方法
}
}

core层编写

config层

MybatisPlusConfig

卡了我两个下午QAQ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 兼容 mybatis-plus 3.5.1
* mybatis-plus 的 MybatisSqlSessionFactoryBean 中使用到了这个异常
* Spring 6 开始移除了该异常
*mybatis puls的坑:使用mybatis-plus时不能使用自带的SqlSessionFactory,要使用
*MybatisSqlSessionFactory,在配置类中加入如下配置(springboot)
*啊啊啊啊啊啊啊卡我两个下午
*/
@Configuration
public class MybatisPlusConfig {
@Primary
@Bean("db1SqlSessionFactory")
public SqlSessionFactory db1SqlSessionFactory(DataSource dataSource) throws Exception {
/**
* 使用 mybatis plus 配置
*/
MybatisSqlSessionFactoryBean b1 = new MybatisSqlSessionFactoryBean();
System.out.println("dataSourceLyz"+dataSource.toString());
b1.setDataSource(dataSource);
b1.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return b1.getObject();
}
}
CorsProperties

解决跨域问题

1
2
3
@ConfigurationProperties(prefix = "novel.cors")
public record CorsProperties(List<String> allowOrigins) {
}
CorsConfig

解决跨域问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Configuration
@EnableConfigurationProperties(CorsProperties.class)
@RequiredArgsConstructor
public class CorsConfig {
private final CorsProperties corsProperties;

@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// 允许的域,不要写*,否则cookie就无法使用了
for (String allowOrigin : corsProperties.allowOrigins()) {
config.addAllowedOrigin(allowOrigin);
}
// 允许的头信息
config.addAllowedHeader("*");
// 允许的请求方式
config.addAllowedMethod("*");
// 是否允许携带Cookie信息
config.setAllowCredentials(true);

UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource();
// 添加映射路径,拦截一切请求
configurationSource.registerCorsConfiguration("/**", config);
return new CorsFilter(configurationSource);
}
}

common层

comstant层(通用常量层)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
@Getter
//提供类的全参构造,减少了@Autowired的书写
@AllArgsConstructor
//enum:通常用于常量类型
public enum ErrorCodeEnum {

/**
* 正确执行后的返回
*/
OK("00000", "一切 ok"),

/**
* 一级宏观错误码,用户端错误
*/
USER_ERROR("A0001", "用户端错误"),

/**
* 二级宏观错误码,用户注册错误
*/
USER_REGISTER_ERROR("A0100", "用户注册错误"),

/**
* 用户未同意隐私协议
*/
USER_NO_AGREE_PRIVATE_ERROR("A0101", "用户未同意隐私协议"),

/**
* 注册国家或地区受限
*/
USER_REGISTER_AREA_LIMIT_ERROR("A0102", "注册国家或地区受限"),

/**
* 用户验证码错误
*/
USER_VERIFY_CODE_ERROR("A0240", "用户验证码错误"),

/**
* 用户名已存在
*/
USER_NAME_EXIST("A0111", "用户名已存在"),

/**
* 用户账号不存在
*/
USER_ACCOUNT_NOT_EXIST("A0201", "用户账号不存在"),

/**
* 用户密码错误
*/
USER_PASSWORD_ERROR("A0210", "用户密码错误"),

/**
* 二级宏观错误码,用户请求参数错误
*/
USER_REQUEST_PARAM_ERROR("A0400", "用户请求参数错误"),

/**
* 用户登录已过期
*/
USER_LOGIN_EXPIRED("A0230", "用户登录已过期"),

/**
* 访问未授权
*/
USER_UN_AUTH("A0301", "访问未授权"),

/**
* 用户请求服务异常
*/
USER_REQ_EXCEPTION("A0500", "用户请求服务异常"),

/**
* 请求超出限制
*/
USER_REQ_MANY("A0501", "请求超出限制"),

/**
* 用户评论异常
*/
USER_COMMENT("A2000", "用户评论异常"),

/**
* 用户评论异常
*/
USER_COMMENTED("A2001", "用户已发表评论"),

/**
* 作家发布异常
*/
AUTHOR_PUBLISH("A3000", "作家发布异常"),

/**
* 小说名已存在
*/
AUTHOR_BOOK_NAME_EXIST("A3001", "小说名已存在"),

/**
* 用户上传文件异常
*/
USER_UPLOAD_FILE_ERROR("A0700", "用户上传文件异常"),

/**
* 用户上传文件类型不匹配
*/
USER_UPLOAD_FILE_TYPE_NOT_MATCH("A0701", "用户上传文件类型不匹配"),

/**
* 一级宏观错误码,系统执行出错
*/
SYSTEM_ERROR("B0001", "系统执行出错"),

/**
* 二级宏观错误码,系统执行超时
*/
SYSTEM_TIMEOUT_ERROR("B0100", "系统执行超时"),

/**
* 一级宏观错误码,调用第三方服务出错
*/
THIRD_SERVICE_ERROR("C0001", "调用第三方服务出错"),

/**
* 一级宏观错误码,中间件服务出错
*/
MIDDLEWARE_SERVICE_ERROR("C0100", "中间件服务出错");

/**
* 错误码
*/
private final String code;

/**
* 中文描述
*/
private final String message;

}

resp层

Http Rest 响应工具及数据格式封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@Getter
public class RestResp<T> {

/**
* 响应码
*/
@Schema(description = "错误码,00000-没有错误")
private String code;

/**
* 响应消息
*/
@Schema(description = "响应消息")
private String message;

/**
* 响应数据
*/
@Schema(description = "响应数据")
private T data;

private RestResp() {
this.code = ErrorCodeEnum.OK.getCode();
this.message = ErrorCodeEnum.OK.getMessage();
}

private RestResp(ErrorCodeEnum errorCode) {
this.code = errorCode.getCode();
this.message = errorCode.getMessage();
}

private RestResp(T data) {
this();
this.data = data;
}

/**
* 业务处理成功,无数据返回
*/
public static RestResp<Void> ok() {
return new RestResp<>();
}

/**
* 业务处理成功,有数据返回
*/
public static <T> RestResp<T> ok(T data) {
return new RestResp<>(data);
}

/**
* 业务处理失败
*/
public static RestResp<Void> fail(ErrorCodeEnum errorCode) {
return new RestResp<>(errorCode);
}


/**
* 系统错误
*/
public static RestResp<Void> error() {
return new RestResp<>(ErrorCodeEnum.SYSTEM_ERROR);
}

/**
* 判断是否成功
*/
public boolean isOk() {
return Objects.equals(this.code, ErrorCodeEnum.OK.getCode());
}

}

req层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@Getter
public class RestResp<T> {
/**
* 响应码
*/
private String code;

/**
* 响应消息
*/
private String message;

/**
* 响应数据
*/
private T data;

//ErrorCodeEnum为常量层下的类
private RestResp() {
this.code = ErrorCodeEnum.OK.getCode();
this.message = ErrorCodeEnum.OK.getMessage();
}

private RestResp(ErrorCodeEnum errorCode) {
this.code = errorCode.getCode();
this.message = errorCode.getMessage();
}

private RestResp(T data) {
this();
this.data = data;
}

/**
* 业务处理成功,无数据返回
*/
public static RestResp<Void> ok() {
return new RestResp<>();
}

/**
* 业务处理成功,有数据返回
*/
public static <T> RestResp<T> ok(T data) {
return new RestResp<>(data);
}

/**
* 业务处理失败
*/
public static RestResp<Void> fail(ErrorCodeEnum errorCode) {
return new RestResp<>(errorCode);
}


/**
* 系统错误
*/
public static RestResp<Void> error() {
return new RestResp<>(ErrorCodeEnum.SYSTEM_ERROR);
}

/**
* 判断是否成功
*/
public boolean isOk() {
return Objects.equals(this.code, ErrorCodeEnum.OK.getCode());
}
}

启动类

1
2
3
4
5
6
7
8
9
10
11
12
@Slf4j
@SpringBootApplication
//mapper包扫描
@MapperScan("com.tec.vuepractice.dao.mapper")
@ServletComponentScan
@EnableTransactionManagement
public class VuePracticeApplication {
public static void main(String[] args) {
SpringApplication.run(VuePracticeApplication.class, args);
log.info("项目启动成功!!!");
}
}