学成在线项目笔记

环境准备

IDEA 中的字符编码集都选择是UTF8

将解压的虚拟机进行赋值, 不选择移动

虚拟机账号密码 root / centos

mysql中的账号密码 root / mysql

gogs的账号密码 gogs / gogs

启动docker

1
2
systemctl start docker
sh /data/soft/restart.sh

将数据导入到数据库中, 使用代码生成器, 创建po类

添加swagger依赖 , 然后进行测试http://localhost:63040/content/swagger-ui.html

添加config的配置类, LocalDateTimeConfig, 将日期进行展示yyyy-MM-dd HH:mm:ss格式
“createDate”: “2023-06-25 20:36:45”,

拷贝代码生成器生成的mapper到service

创建数据字典表

使用 http client 插件进行接口测试

部署前端工程 和 系统管理服务

使用全局Cors过滤器, 解决前端工程的跨域问题

前端工程还没提交代码

编写mapper, 查询category树形结构类型的数据, 然后测试

自连接的sql语句

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
SELECT one.id, one.label, two.id, two.label
FROM course_category `one`
JOIN course_category two
ON two.parentid = one.id
WHERE one.parentid = '1'
AND one.is_show = '1'
AND two.is_show = '1'
ORDER BY one.orderby, two.orderby

WITH recursive t1 AS (
SELECT 1 AS n
UNION ALL
SELECT n+1 FROM t1 WHERE n<5
)
SELECT * FROM t1

-- 向上递归
WITH recursive t1 AS (
SELECT * FROM course_category WHERE id = '1'
UNION ALL
SELECT t2.* FROM course_category t2 JOIN t1 ON t1.id = t2.parentid
)
SELECT * FROM t1 ORDER BY t1.id

-- 向上递归
WITH recursive t1 AS (
SELECT * FROM course_category WHERE id = '1-1-1'
UNION ALL
SELECT t2.* FROM course_category t2 INNER JOIN t1 ON t1.parentid = t2.id
)
SELECT * FROM t1 ORDER BY t1.id

课程分类查询

dev03

使用JSR303校验参数是否为空
在实体对象上添加注解 @NotEmpty , 然后再Controller接收参数使用@Validated激活
当增删改共用一个对象时, 还需要抛出不同的信息内容, 这时需要定义一个分组接口
注解内部 @Validated 需要指定下是用的哪个分组

1
2
3
4
public CourseBaseInfoDto createCourseBase(
@RequestBody @Validated(ValidationGroups.Insert.class) AddCourseDto addCourseDto){
...
}
1
2
3
4
5
6
7
8
9
10
/**
* 校验分组
*/
public class ValidationGroups {

public interface Insert{};
public interface Update{};
public interface Delete{};

}

修改课程, 先按照courseid查询出当前接口

关于mybatis

插入语句之后返回主键
首先数据库的主键要自动递增
然后再insert标签上添加useGeneratedKeys="true" keyProperty="id"

1
2
3
4
5
6
7
8
9
10
11
<!--Integer addCourseTeacher(CourseTeacher courseTeacher);-->
<insert id="addCourseTeacher" useGeneratedKeys="true" keyProperty="id">
insert into course_teacher(course_id,
teacher_name,
position,
introduction)
values(#{courseId},
#{teacherName},
#{position},
#{introduction})
</insert>

插入之后, 主键返回在传入的对象当中

1
2
3
4
5
Integer integer = courseTeacherMapper.addCourseTeacher(courseTeacher);
// 影响的行数
System.out.println("integer = " + integer);
// 返回的主键
System.out.println(courseTeacher.getId());

各种返回值

insert:插入n条记录,返回影响行数n。(n>=1,n为0时实际为插入失败)
update:更新n条记录,返回影响行数n。(n>=0)
delete:删除n条记录,返回影响行数n。(n>=0)
参考: https://www.cnblogs.com/aspirant/p/12213428.html

待完成: 通过远程调用传入字典值, 获取code

配置本地优先

在nacos中配置override-none, 然后让微服务读取这个配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
servlet:
context-path: /content
port: 63040
# 配置本地优先
spring:
cloud:
config:
override-none: true
# datasource:
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://192.168.101.65:3306/xc402_content?serverTimezone=UTC&userUnicode=true&useSSL=false&
# username: root
# password: mysql

本地运行添加这个参数, 就会按照63041端口运行微服务
-Dserver.port=63041

同样可以添加一个参数用来切换不同环境-Dserver.port=63041 spring.profiles.active=test

事务优化

一个普通方法(非事务方法)调用一个事务方法, 此时的事务会失效

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
@Service
public class MediaFileServiceImpl implements MediaFileService {

@Autowired
MediaFileService mediaFileService;

/**
* 普通方法
*/
public UploadFileResultDto uploadFile() {
// 不会回滚
// MediaFiles mediaFiles = addMediaFilesToDb();
// 不会回滚
// MediaFiles mediaFiles = this.addMediaFilesToDb();

// 事务控制必须是代理对象调用才有效果, 将自己注入给自己
// 这个`mediaFileService`就是一个代理对象, 此时调用的方法出现异常会进行事务的回滚
MediaFiles mediaFiles = mediaFileService.addMediaFilesToDb();
}

/**
* 事务方法
* @return
*/
@Transactional
public MediaFiles addMediaFilesToDb(){
// 进行数据库的操作
// ...

// 异常
int i = 1/0;
}
}

事务控制必须是代理对象调用才有效果

任务调度中心

http://192.168.101.65:8088/xxl-job-admin

admin
123456

在使用任务调度的时候, 需要在配置中心将appname指定好

1
2
3
4
5
6
7
8
9
10
11
12
xxl:
job:
admin:
addresses: http://192.168.101.65:8088/xxl-job-admin
executor:
appname: coursepublish-job
address:
ip:
port: 8999
logpath: /data/applogs/xxl-job/jobhandler
logretentiondays: 30
accessToken: default_token

然后在执行器管理中创建对应的appname
接着在 任务管理 中可以创建一些定时任务

在需要执行定时调度任务的方法上添加注解 @XxlJob

1
2
3
//任务调度入口
@XxlJob("CoursePublishJobHandler")
public void coursePublishJobHandler() throws Exception {}

要注意的一点, service中的配置, 使用的数据库并不是最新的数据库, 现在暂时还没有全部修改

关于test测试

/assets/img.png

这个service模块是没有启动类的, 如果想在这下面使用@Test进行代码测试, 需要创建出来对应的启动类, 否则无法测试

乐观锁, 不用先拿到锁, 尽管去执行, 执行失败了, 接着重试再执行
悲观锁, 要先拿到锁

/assets/img_1.png

nginx部署门户

启动nginx

1
.\nginx.exe

修改配置后重新加载nginx

1
./nginx.exe -s reload

tips: nginx的安装目录不能在有中文的路径下
但是请求访问的静态资源的路径是可以存在中文的

事务的特性ACID

atomicity

consistency

isolation

durability

CAP理论
Consistency 一致性

Availability 可用性

Partition tolerance 分区容忍性

AP的处理方式:
采用消息表+定时任务调度

nginx代理

模板引擎就是服务端渲染
可以将渲染出来的html页面存储到minio, 当有用户来访问的时候直接从minio中读取该html静态资源, 减少服务器压力
部署完成之后访问地址:
http://192.168.101.65:9000/mediafiles/course/120.html

采用文件域名访问
http://file.51xuecheng.cn/mediafiles/course/120.html

主站域名访问
http://www.51xuecheng.cn/course/120.html

只有访问主站的静态资源才会加载css等其他的静态资源, 所以要对nginx进行设置代理
请求了 www.51xuecheng.cn/course 之后, 给他代理给 file.51xuecheng.cn/mediafiles/course

1
2
3
4
5
6
7
server {
listen 80;
server_name www.51xuecheng.cn localhost;
location /course/ {
proxy_pass http://fileserver/mediafiles/course/;
}
}

Kibana无法启动

Kibana server is not ready yet

curl -XDELETE localhost:9200/.kibana_task_manager_7.12.1_001

之后重启elasticsearch和kibana

docker restart kibana elasticsearch

kibana的web访问连接: http://192.168.101.65:5601/

elasticsearch: http://192.168.101.65:9200/

发布课程

提交审核之后, 需要在course_publish_pre表中的status的状态修改成202004
然后再course_base中修改audit_status字段成202004 status保持203001
此时就手动从数据库中审核过了

SpringSecurity部署

添加依赖

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

然后修改配置文件, 然后指定mysql的datasource.url(数据源)

给mapping设置权限, 当拥有此权限的登录用户才可以进行访问该mapping下的方法

1
2
3
4
5
6
7
8
9
10
11
12

@RequestMapping("/r/r1")
@PreAuthorize("hasAuthority('p1')")//拥有p1权限方可访问
public String r1() {
return "访问r1资源";
}

@RequestMapping("/r/r2")
@PreAuthorize("hasAuthority('p2')")//拥有p2权限方可访问
public String r2() {
return "访问r2资源";
}

IDEA程序包不存在

需设置Maven > Runner > Delegate IDE build/run actions to Maven。
勾选 Delegate IDE build/run actions to Maven
然后再启动程序就可以了

这种方式虽然说可以处理这个问题, 但仍在其他的项目任会出现一些其他的问题,
或者可以将.iml文件直接删除, 然后再运行该项目

整合微服务加入SpringSecurity管理

添加依赖

1
2
3
4
5
6
7
8
9
<!--认证相关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

添加config配置

TokenConfig配置

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
/**
* @author Administrator
* @version 1.0
**/
@Configuration
public class TokenConfig {

//jwt签名密钥,与认证服务保持一致
private String SIGNING_KEY = "mq123";

@Bean
public TokenStore tokenStore() {
//JWT令牌存储方案
return new JwtTokenStore(accessTokenConverter());
}

@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(SIGNING_KEY);
return converter;
}

/* @Bean
public TokenStore tokenStore() {
//使用内存存储令牌(普通令牌)
return new InMemoryTokenStore();
}*/
}


ResouceServerConfig配置

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


/**
* @author Administrator
* @version 1.0
**/
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {


//资源服务标识
public static final String RESOURCE_ID = "xuecheng-plus";

@Autowired
TokenStore tokenStore;

@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID)//资源 id
.tokenStore(tokenStore)
.stateless(true);
}

@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/r/**","/course/**").authenticated()//所有/r/**的请求必须认证通过
.anyRequest().permitAll()
;
}

}

这个请求是以 /content 开头的
之前的请求在请求头不需要Authorization就可以直接查询课程信息

request
1
2
3
4
5
6
7
8
9
10
### 查询课程信息
POST {{gateway_host}}/content/course/list?pageNo=1&pageSize=2
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsieHVlY2hlbmctcGx1cyJdLCJ1c2VyX25hbWUiOiJ6aGFuZ3NhbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2OTE5NDMyMTEsImF1dGhvcml0aWVzIjpbInAxIl0sImp0aSI6Ijk4ZTUyMTkwLTUwNmUtNGQ4My04NjM0LWNlMGVhYjA5ZmUyMiIsImNsaWVudF9pZCI6IlhjV2ViQXBwIn0.qJ8hPG4KYJzGgav9LAPvBN4jXIbmppzIKpy28zR6k14
Content-Type: application/json

{
"auditStatus": "202004",
"courseName": "java",
"publishStatus": ""
}

通过SecurityContextHolder获取用户身份

当传入了jwt令牌, 可以使用SecurityContextHolder获取当前用户的身份

1
2
3
4

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.info("当前用户身份: {}", principal); // principal: zhangsan

request
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

### 校验jwt令牌
POST {{auth_host}}/auth/oauth/check_token?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsieHVlY2hlbmctcGx1cyJdLCJ1c2VyX25hbWUiOiJ6aGFuZ3NhbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2OTE5NDExNTUsImF1dGhvcml0aWVzIjpbInAxIl0sImp0aSI6IjI5NDUwOGZiLTljYmEtNDAxZS1iMDNhLTRjYzFjYmFiZTBhNiIsImNsaWVudF9pZCI6IlhjV2ViQXBwIn0.Tv7RHqTU9gMChfwjcx1IKxogB9hse9eJL9mYU74bCEU

#{
# "aud": [
# "xuecheng-plus"
# ],
# "user_name": "zhangsan",
# "scope": [
# "all"
# ],
# "active": true,
# "exp": 1691941155,
# "authorities": [
# "p1"
# ],
# "jti": "294508fb-9cba-401e-b03a-4cc1cbabe0a6",
# "client_id": "XcWebApp"
#}
#Response file saved.
#> 2023-08-13T222412.200.json
#
#Response code: 200; Time: 58ms (58 ms); Content length: 184 bytes (184 B)

微服务网关的作用

  1. 路由转发
  2. jwt认证
  3. 网站的白名单维护

feign远程调用

GET方式
错误写法

1
2
3
4

@PostMapping(value = "/verify")
public Boolean verify(String key, String code);

启动服务的时候,会报如下异常:

1
2
Caused by: java.lang.IllegalStateException: Method has too many Body parameters: public abstract java.lang.Boolean com.xuecheng.ucenter.feignclient.CheckCodeClient.verify(java.lang.String,java.lang.String)

异常原因:当使用Feign时,如果发送的是get请求,那么需要在请求参数前加上@RequestParam注解修饰,Controller里面可以不加该注解修饰。

正确写法

1
2
3
4

@PostMapping(value = "/verify")
public Boolean verify(@RequestParam("key") String key, @RequestParam("code") String code);

内网穿透工具

natapp
https://natapp.cn/

微信二维码

E:\Code\学成在线\xuecheng-plus-project\xc-ui-pc-static-portal\wxsign.html

修改这个html的部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

//请用微信生成二维码
function generateWxQrcode(token) {
var wxObj = new WxLogin({
self_redirect:true,
id:"login_container",
appid: "wxed9954c01bb89b47",
scope: "snsapi_login",
redirect_uri: "http://localhost:8160/auth/wxLogin",
// redirect_uri: "http://62qj2i.natappfree.cc/auth/wxLogin",
// redirect_uri: "http://tjxt-user-t.itheima.net/xuecheng/auth/wxLogin",
state: token,
style: "",
href: ""
});
}

设置内网穿透的端口是63070
http://62qj2i.natappfree.cc -> 127.0.0.1:63070

然后修改AuthApplication微服务的端口是8160

xxl-job 的数据库玩坏了还没修 QAQ, 修好了

可以参考的项目地址: https://github.com/Cyborg2077/xuecheng-plus

https://www.anlemon.cn/xiangmu/xuehcheng/xuecheng05/

E:\Code\学成在线\视频\day11 微服务授权 选课学习 支付接口测试

E:\Code\学成在线\学成在线项目—资料\day11 微服务授权 选课学习 支付接口测试

支付宝沙箱账号

https://open.alipay.com/develop/sandbox/account

买家账号agrahh6047@sandbox.com
登录密码111111

关于支付宝订单支付

2023-8-19 14:54:53
首先需要开启natapp进行内网穿透, 穿透到指定的微服务来处理支付请求

之后配置好支付宝支付成功的回调地址

支付成功之后, 可以先采用如下的连接进行商品的查询, 然后把这段查询的代码拷贝到自己的测试类跑通

https://open.alipay.com/api/apiDebug?frontProdCode=I1080300001000041949&backProdCode=I1011000100000000003&apiNames=alipay.trade.query

注意跑通的时候pom.xml的 AlipayConfig 不是自己创建的配置类, 而是jar包自带的配置类

俺用的是4.38.55.ALL版本, 测试通过了, 也不知道为啥他妈的他家的代码冗余了两个AlipayConfig , 成功的把我误导了

1
2
3
4
5
6
7
<!-- 支付宝SDK -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<!--<version>3.7.73.ALL</version>-->
<version>4.38.55.ALL</version>
</dependency>

账号如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
商户账号rjewti2612@sandbox.com
登录密码111111
商户PID2088721008878120
账户余额999910.00
买家信息
买家账号agrahh6047@sandbox.com
登录密码111111
支付密码111111
用户UID2088722008861299
用户名称agrahh6047
证件类型IDENTITY_CARD
证件账号579355195104126366

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADIAQAAAACFI5MzAAABx0lEQVR42u2YTYqDQBCFS1z00iP0TeLFAgpeTG/SR3DpQqx5rzoxEpjlvAzDCAnan4uiql79aP7dZf/kt5PRzLqp+LK2OFjbgudWSib3o8PZDGy3zovHmZLU53R0u9GsssFUPZnceh+zzx8jo6UFPknLOnyAMD6Tbze7Gw7fIycgjxzdbs+/9+z9cRLXmLfe94x33hUsIOGYFhmy3jO8g0RtTr9pyJ6ZF54OeCXKRZo7l5LIkBDIUFir3PmilJStoW0Lo3IwOV61SkRCIBkYjkHdLMY7Jdlx6CyU9fCe03FGTkMctnXEKypFj54FUwcpgTob6BSJihydzXKNlJSwaxZoA3qBQAoMHKRkxI9mHVUlBr10LiXMUfSszZCoFOtVwRoCdzxyFFYudJaLyU5ZRMeASvooHK96LSHwDtqVJd6haA8xPkgJh0eEpg4wjI+9FCwiJeqDxwTZ8O4SOQmpFpXkqFUQCEaZ/twkNKTOLmzZUatav9RRDYn5DTmKZ/jp2USV5LHL1NmZfRsqcS2JOT7HOkHFXiY7JUHHWFiwsNDYGR8t8RqfpmLXEsZnz9GyY7FNLibPHK2bBDfK/rpNCcj/V5w/Rr4AHf0kPkDOgeoAAAAASUVORK5CYII=

查询测试

查询支付结果测试, 需要先进行支付才能进行查询订单的详情

rabbitmq

账号密码 root / root
guest / guest

瞎逼逼

截止到2023-8-22 14:48:53快学完了
问题来了, 课程审核的部分代码在哪???
该不会每次审核都是要自己手动修改数据库吧

关于nginx

nginx其实不是在这个项目的路径下的, nginx存在的路径应该是非中文下的路径, 而我这个项目的路径在中文目录下…

关于redis项目优化

缓存穿透 , 就是在缓存中没有要的数据, 大量请求跳过了缓存, 直接查询数据库
解决缓存穿透的三种方法: 1. 增加校验机制, 如果传入的参数非法, 直接返回
2. 使用布隆过滤器
3. 不管来的值是什么, 都将这个值进行缓存起来, 空值也缓存起来(需要一个过期时间,
假设请求的时候, 由缓存之间返回来空值, 但是数据库中存在该值的这种情况)

缓存雪崩, 缓存中的key恰好都同时失效了, 大量请求压力又给到了数据库中
解决方案 1. 用同步锁
2. 给缓存中的key设置不同的过期时间, 防止这些缓存中的数据同时在相同的时间内过期
3. 缓存预热, 开启一个定时任务, 在缓存中的数据快要过期时, 或者是每隔一段时间, 就源源不断的更新缓存中的数据

缓存击穿 , 大量并发请求同一个热点数据, 当热点数据失效之后, 就都会请求数据库, 瞬间耗尽数据库资源
1.使用同步锁
2.热点数据不过期

分布式引发的问题, 当大量请求来到网关, 网关负载均衡给每个微服务, 每个微服务都去查缓存然后查数据库,
这时每个数据库都有一次查询


学成在线项目笔记
https://xiamu.icu/Java/学成在线项目笔记/
作者
肉豆蔻吖
发布于
2023年7月2日
许可协议