环境准备 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.labelFROM course_category `one `JOIN course_category twoON two.parentid = one.idWHERE one.parentid = '1' AND one.is_show = '1' AND two.is_show = '1' ORDER BY one.orderby, two.orderbyWITH recursive t1 AS ( SELECT 1 AS n UNION ALL SELECT n+ 1 FROM t1 WHERE n< 5 )SELECT * FROM t1WITH 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.idWITH 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 <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
本地运行添加这个参数, 就会按照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 = mediaFileService.addMediaFilesToDb(); } @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测试
这个service模块是没有启动类的, 如果想在这下面使用@Test进行代码测试, 需要创建出来对应的启动类, 否则无法测试
乐观锁, 不用先拿到锁, 尽管去执行, 执行失败了, 接着重试再执行 悲观锁, 要先拿到锁
nginx部署门户 启动nginx
修改配置后重新加载nginx
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')") public String r1 () { return "访问r1资源" ; }@RequestMapping("/r/r2") @PreAuthorize("hasAuthority('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 @Configuration public class TokenConfig { private String SIGNING_KEY = "mq123" ; @Bean public TokenStore tokenStore () { return new JwtTokenStore (accessTokenConverter()); } @Bean public JwtAccessTokenConverter accessTokenConverter () { JwtAccessTokenConverter converter = new JwtAccessTokenConverter (); converter.setSigningKey(SIGNING_KEY); return converter; } }
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 @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) .tokenStore(tokenStore) .stateless(true ); } @Override public void configure (HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/r/**" ,"/course/**" ).authenticated() .anyRequest().permitAll() ; } }
这个请求是以 /content
开头的 之前的请求在请求头不需要Authorization就可以直接查询课程信息
request 1 2 3 4 5 6 7 8 9 10 ### 查询课程信息 POST {{gateway_host}}/content/course/list?pageNo=1&pageSize=2Authorization : Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsieHVlY2hlbmctcGx1cyJdLCJ1c2VyX25hbWUiOiJ6aGFuZ3NhbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2OTE5NDMyMTEsImF1dGhvcml0aWVzIjpbInAxIl0sImp0aSI6Ijk4ZTUyMTkwLTUwNmUtNGQ4My04NjM0LWNlMGVhYjA5ZmUyMiIsImNsaWVudF9pZCI6IlhjV2ViQXBwIn0.qJ8hPG4KYJzGgav9LAPvBN4jXIbmppzIKpy28zR6k14Content-Type : application/json { "auditStatus": "202004", "courseName": "java", "publishStatus": "" }
通过SecurityContextHolder获取用户身份 当传入了jwt令牌, 可以使用SecurityContextHolder获取当前用户的身份
1 2 3 4 Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); log.info("当前用户身份: {}" , principal);
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)
微服务网关的作用
路由转发
jwt认证
网站的白名单维护
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) ;
内网穿透工具 natapphttps://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" , state : token, style : "" , href : "" }); }
设置内网穿透的端口是63070http://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 <dependency > <groupId > com.alipay.sdk</groupId > <artifactId > alipay-sdk-java</artifactId > <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.热点数据不过期
分布式引发的问题, 当大量请求来到网关, 网关负载均衡给每个微服务, 每个微服务都去查缓存然后查数据库, 这时每个数据库都有一次查询