Skip to content

Latest commit

 

History

History
686 lines (510 loc) · 19.1 KB

项目笔记.md

File metadata and controls

686 lines (510 loc) · 19.1 KB

环境准备

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

课程分类查询

dev03

使用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查询出当前接口

关于mybatis

插入语句之后返回主键 首先数据库的主键要自动递增 然后再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测试

/assets/img.png

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

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

/assets/img_1.png

nginx部署门户

启动nginx

.\nginx.exe

修改配置后重新加载nginx

./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

    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部署

添加依赖

<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资源";
    }

IDEA程序包不存在

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

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

整合微服务加入SpringSecurity管理

添加依赖

<!--认证相关-->
<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": ""
}

通过SecurityContextHolder获取用户身份

当传入了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)

微服务网关的作用

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

feign远程调用

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进行内网穿透, 穿透到指定的微服务来处理支付请求

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

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

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

注意跑通的时候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=

查询测试

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

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.热点数据不过期

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