IDEA 中的字符编码集都选择是UTF8
将解压的虚拟机进行赋值, 不选择移动
虚拟机账号密码 root / centos
mysql中的账号密码 root / mysql
gogs的账号密码 gogs / gogs
启动docker
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语句
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
课程分类查询
使用JSR303校验参数是否为空
在实体对象上添加注解 @NotEmpty
, 然后再Controller接收参数使用@Validated
激活
当增删改共用一个对象时, 还需要抛出不同的信息内容, 这时需要定义一个分组接口
注解内部 @Validated
需要指定下是用的哪个分组
public CourseBaseInfoDto createCourseBase(
@RequestBody @Validated(ValidationGroups.Insert.class) AddCourseDto addCourseDto){
...
}
/**
* 校验分组
*/
public class ValidationGroups {
public interface Insert{};
public interface Update{};
public interface Delete{};
}
修改课程, 先按照courseid查询出当前接口
插入语句之后返回主键
首先数据库的主键要自动递增
然后再insert标签上添加useGeneratedKeys="true" keyProperty="id"
<!--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>
插入之后, 主键返回在传入的对象当中
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, 然后让微服务读取这个配置
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
一个普通方法(非事务方法)调用一个事务方法, 此时的事务会失效
@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指定好
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
//任务调度入口
@XxlJob("CoursePublishJobHandler")
public void coursePublishJobHandler() throws Exception {}
这个service模块是没有启动类的, 如果想在这下面使用@Test进行代码测试, 需要创建出来对应的启动类, 否则无法测试
乐观锁, 不用先拿到锁, 尽管去执行, 执行失败了, 接着重试再执行 悲观锁, 要先拿到锁
启动nginx
.\nginx.exe
修改配置后重新加载nginx
./nginx.exe -s reload
tips: nginx的安装目录不能在有中文的路径下 但是请求访问的静态资源的路径是可以存在中文的
事务的特性ACID
atomicity
consistency
isolation
durability
CAP理论 Consistency 一致性
Availability 可用性
Partition tolerance 分区容忍性
AP的处理方式: 采用消息表+定时任务调度
模板引擎就是服务端渲染 可以将渲染出来的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
server {
listen 80;
server_name www.51xuecheng.cn localhost;
location /course/ {
proxy_pass http://fileserver/mediafiles/course/;
}
}
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
此时就手动从数据库中审核过了
添加依赖
<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下的方法
@RequestMapping("/r/r1")
@PreAuthorize("hasAuthority('p1')")//拥有p1权限方可访问
public String r1() {
return "访问r1资源";
}
@RequestMapping("/r/r2")
@PreAuthorize("hasAuthority('p2')")//拥有p2权限方可访问
public String r2() {
return "访问r2资源";
}
需设置Maven > Runner > Delegate IDE build/run actions to Maven。
勾选 Delegate IDE build/run actions to Maven
然后再启动程序就可以了
这种方式虽然说可以处理这个问题, 但仍在其他的项目任会出现一些其他的问题, 或者可以将.iml文件直接删除, 然后再运行该项目
添加依赖
<!--认证相关-->
<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配置
/**
* @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配置
/**
* @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就可以直接查询课程信息
### 查询课程信息
POST {{gateway_host}}/content/course/list?pageNo=1&pageSize=2
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsieHVlY2hlbmctcGx1cyJdLCJ1c2VyX25hbWUiOiJ6aGFuZ3NhbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2OTE5NDMyMTEsImF1dGhvcml0aWVzIjpbInAxIl0sImp0aSI6Ijk4ZTUyMTkwLTUwNmUtNGQ4My04NjM0LWNlMGVhYjA5ZmUyMiIsImNsaWVudF9pZCI6IlhjV2ViQXBwIn0.qJ8hPG4KYJzGgav9LAPvBN4jXIbmppzIKpy28zR6k14
Content-Type: application/json
{
"auditStatus": "202004",
"courseName": "java",
"publishStatus": ""
}
当传入了jwt令牌, 可以使用SecurityContextHolder获取当前用户的身份
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.info("当前用户身份: {}", principal); // principal: zhangsan
### 校验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)
- 路由转发
- jwt认证
- 网站的白名单维护
GET方式 错误写法
@PostMapping(value = "/verify")
public Boolean verify(String key, String code);
启动服务的时候,会报如下异常:
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里面可以不加该注解修饰。
正确写法
@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的部分
//请用微信生成二维码
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
买家账号[email protected] 登录密码111111
2023-8-19 14:54:53 首先需要开启natapp进行内网穿透, 穿透到指定的微服务来处理支付请求
之后配置好支付宝支付成功的回调地址
支付成功之后, 可以先采用如下的连接进行商品的查询, 然后把这段查询的代码拷贝到自己的测试类跑通
注意跑通的时候pom.xml的 AlipayConfig 不是自己创建的配置类, 而是jar包自带的配置类
俺用的是4.38.55.ALL版本, 测试通过了, 也不知道为啥他妈的他家的代码冗余了两个AlipayConfig , 成功的把我误导了
<!-- 支付宝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>
账号如下
商户账号[email protected]
登录密码111111
商户PID2088721008878120
账户余额999910.00
买家信息
买家账号[email protected]
登录密码111111
支付密码111111
用户UID2088722008861299
用户名称agrahh6047
证件类型IDENTITY_CARD
证件账号579355195104126366
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADIAQAAAACFI5MzAAABx0lEQVR42u2YTYqDQBCFS1z00iP0TeLFAgpeTG/SR3DpQqx5rzoxEpjlvAzDCAnan4uiql79aP7dZf/kt5PRzLqp+LK2OFjbgudWSib3o8PZDGy3zovHmZLU53R0u9GsssFUPZnceh+zzx8jo6UFPknLOnyAMD6Tbze7Gw7fIycgjxzdbs+/9+z9cRLXmLfe94x33hUsIOGYFhmy3jO8g0RtTr9pyJ6ZF54OeCXKRZo7l5LIkBDIUFir3PmilJStoW0Lo3IwOV61SkRCIBkYjkHdLMY7Jdlx6CyU9fCe03FGTkMctnXEKypFj54FUwcpgTob6BSJihydzXKNlJSwaxZoA3qBQAoMHKRkxI9mHVUlBr10LiXMUfSszZCoFOtVwRoCdzxyFFYudJaLyU5ZRMeASvooHK96LSHwDtqVJd6haA8xPkgJh0eEpg4wjI+9FCwiJeqDxwTZ8O4SOQmpFpXkqFUQCEaZ/twkNKTOLmzZUatav9RRDYn5DTmKZ/jp2USV5LHL1NmZfRsqcS2JOT7HOkHFXiY7JUHHWFiwsNDYGR8t8RqfpmLXEsZnz9GyY7FNLibPHK2bBDfK/rpNCcj/V5w/Rr4AHf0kPkDOgeoAAAAASUVORK5CYII=
查询支付结果测试, 需要先进行支付才能进行查询订单的详情
账号密码 root / root guest / guest
截止到2023-8-22 14:48:53快学完了 问题来了, 课程审核的部分代码在哪??? 该不会每次审核都是要自己手动修改数据库吧
nginx其实不是在这个项目的路径下的, nginx存在的路径应该是非中文下的路径, 而我这个项目的路径在中文目录下...
缓存穿透 , 就是在缓存中没有要的数据, 大量请求跳过了缓存, 直接查询数据库 解决缓存穿透的三种方法: 1. 增加校验机制, 如果传入的参数非法, 直接返回 2. 使用布隆过滤器 3. 不管来的值是什么, 都将这个值进行缓存起来, 空值也缓存起来(需要一个过期时间, 假设请求的时候, 由缓存之间返回来空值, 但是数据库中存在该值的这种情况)
缓存雪崩, 缓存中的key恰好都同时失效了, 大量请求压力又给到了数据库中 解决方案 1. 用同步锁 2. 给缓存中的key设置不同的过期时间, 防止这些缓存中的数据同时在相同的时间内过期 3. 缓存预热, 开启一个定时任务, 在缓存中的数据快要过期时, 或者是每隔一段时间, 就源源不断的更新缓存中的数据
缓存击穿 , 大量并发请求同一个热点数据, 当热点数据失效之后, 就都会请求数据库, 瞬间耗尽数据库资源 1.使用同步锁 2.热点数据不过期
分布式引发的问题, 当大量请求来到网关, 网关负载均衡给每个微服务, 每个微服务都去查缓存然后查数据库, 这时每个数据库都有一次查询