Spring Boot实战-文章管理系统(1.用户相关接口)

news2024/9/20 3:08:42

一、用户相关接口

1.注解

  • @RestController:是一个组合注解,它结合了 @Controller 和 @ResponseBody 注解的功能(就相当于把两个注解组合在一起)。 在使用 @RestController 注解标记的类中,每个方法的返回值都会以 JSON 或 XML 的形式直接写入 HTTP 响应体中,相当于在每个方法上都添加了 @ResponseBody 注解。

  • @RequestMapping:表示共享映射,如果没有指定请求方式,将接收GET、POST、HEAD、OPTIONS、PUT、PATCH、DELETE、TRACE、CONNECT所有的HTTP请求方式。

  • @GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping 都是HTTP方法特有的快捷方式@RequestMapping的变体,分别对应具体的HTTP请求方式的映射注解

  • @Pattern:通常用于对字符串字段进行正则表达式(regex)匹配的约束。 这种注解通常和框架(例如 Spring、Hibernate Validator)一起使用,以确保输入的数据满足特定的格式(@Pattern(regexp = "^\\S{5,16}$") String username:约束用户名为5-16位非空字符)

    • 正则表达式的含义如下:^:匹配字符串的开始位置; \\S匹配非空白字符(空白字符包括空格、制表符、换行符等); {5,16}表示前面的\\S(非空白字符)重复 5 到 16 次,也就是说输入的字符串必须由 5 到 16 个非空白字符组成; $:匹配字符串的结束位置。

  • @RequestBody和@RequestParam:

    • @RequestParam接收的参数是来自requestHeader中,通常用于GET请求,比如常见的url

    • @RequestParam有三个配置参数:required是否必须传递参数,默认为true,必须; defaultValue可设置请求参数的默认值; value 为接收url的参数名(相当于key值)

    • @RequestParam用来处理 Content-Type 为 multipart/form-data;编码的内容,Content-Type默认为该属性

    • @RequestBody接收的参数是来自requestBody中,即请求体。一般用于处理非 Content-Type: application/form-data编码格式的数据,比如:application/json、application/xml、applicatioin/x-www-form-urlencoded等类型的数据

    • 就application/json类型的数据而言,使用注解@RequestBody可以将body里面所有的json数据传到后端,后端再进行解析

   @PutMapping("/update")
    public Result update(@RequestBody @Validated User user){
        userService.update(user);
        return Result.success();
    }

    @PatchMapping("/updateAvatar")
    public Result updateAvatar(@RequestParam @URL @NotEmpty String avatarUrl){
        userService.updateAvatar(avatarUrl);
        return Result.success();
    }

        根据文档要求:更新用户基本信息的请求参数为application/json格式,所以用@RequestBody标签

        根据文档要求:更新用户头像基本信息的请求参数为queryStringn格式,所以用@RequestParam标签

  • @Data:lombok依赖下的注解,能够自动注入get,set等方法

  • @NoArgsConstructor:生成一个无参数的构造方法

  • @AllArgsConstructor:生成一个包含所有变量的构造方法,默认生成的方法是 public 的

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class Student {
   public String name;
   public int age;
}


public class Student {
    public String name;
    public int age;
 
    public Student() {
    }
 
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return this.name;
    }
 
    public int getAge() {
        return this.age;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
}
  •  @NotNull:非空标签,可以为 empty,不能为Null,一般用在 Integer 类型的基本数据类型的非空校验上

  • @NotEmpty:不能为 null,且长度必须大于 0,一般用在集合类上或者数组上

  • @NotBlank:只能作用在接收的 String 类型上,不能为 null,而且调用 trim() 后,长度必须大于 0,即必须有实际字符

  • 上述参数在实体类中应用,然后在Controller层和@Validated标签搭配使用,能够限制参数输入类型

@Data
public class User {
    @NotNull
    private Integer id;//主键ID

    private String username;//用户名

    @JsonIgnore//让springMVC吧当前对象转换成json字符串的时候,忽略password,最终的json字符串就没有password这个属性了
    private String password;//密码

    @NotEmpty
    @Pattern(regexp = "^\\S{1,10}$")
    private String nickname;//昵称

    @NotEmpty
    @Email
    private String email;//邮箱

    private String userPic;//用户头像地址

    private LocalDateTime createTime;//创建时间
    private LocalDateTime updateTime;//更新时间
}


@PutMapping("/update")
    public Result update(@RequestBody @Validated User user){
        userService.update(user);
        return Result.success();
    }
  • @jsonIgnore:让springMVC吧当前对象转换成json字符串的时候,忽略注解对象,最终的json字符串就没有注解这个属性了

  • @RestControllerAdvice 与 @ExceptionHandler:两个标签结合使用时,它可以对所有 @RestController 控制器方法中的异常进行统一处理 。

    • @RestControllerAdvice注解用于类前

    • @ExceptionHandler注解用于标记方法,用于捕获指定类型的异常

@RestControllerAdvice
public class GlobalExceptionHandler {
//    @RestControllerAdvice 与 @ExceptionHandler 结合使用时,它可以对所有 @RestController 控制器方法中的异常进行统一处理
//    该注解用于标记方法,用于捕获指定类型的异常。在这个例子中,Exception.class 表示处理所有类型的异常
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e){
        e.printStackTrace();
//        StringUtils.hasLength(e.getMessage()):这是 Spring 提供的 StringUtils 工具类中的方法,作用是判断异常消息 e.getMessage() 是否有内容(即消息长度是否大于 0)。
//        如果 e.getMessage() 不是空的或不为空白,则返回 true,否则返回 false
        return Result.error(StringUtils.hasLength(e.getMessage())?e.getMessage():"操作失败");
    }
}

2. 用户接口项目逻辑

2.1 项目执行过程整体逻辑

        用户通过访问浏览器向服务器发送请求或从服务器接收请求;然后通过Controller层(controller层主要面向用户,用于实现相关代码逻辑);其次Controller层通过调用Service层,Service层主要用于实现面向Controller层的接口(多数Service接口都需要重写@Override);最后Service层通过连接Mapper层实现对数据库的操作;Mapper层是面向数据库的,在最底层实现对数据库的相关操作。

2.2 用户注册逻辑

2.2.1 UserController层逻辑
  • 根据文档,确定传入的参数为username和password,通过@Pattern注解实现传入参数要求5~16位非空字符的要求

  • 调用Userservice接口中的 findByUsername方法来判断当前注册的用户名是否存在,若存在,则返回用户名已经被占用;否则调用Userservice接口中的register方法来完成用户注册,在注册过程中使用post方法来让用户向服务器传递参数

@Autowired
    private UserService userService;
    @PostMapping("/register")
    public Result register(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password) {

        //查询用户
        User u = userService.findByUsername(username);
        if (u == null) {
            //没有占用
            //注册
            userService.register(username, password);
            return Result.success();
        } else {
            //占用
            return Result.error("用户名已被占用");
        }
    }
2.2.2 UserService层逻辑
  • 首先定义Userservice接口,findByUsername方法中,通过username来进行查询;register方法中传递username和password来完成用户注册(Userservice接口只是一个方法的定义,具体实现方法要重写)
  • 重写Userservice接口:调用UserMapper接口中的findByUsername方法和add方法,来完成对数据库的操作

        在存储password时,为了保证安全性,不能直接将用户的密码显性的存储在数据库中,通过使用加密工具Md5Util来完成对用户密码的加密(该加密在Userservice层中实现,因为Controller层面向用户,用于实现登录和注册的主题逻辑,不宜用于加密,Mapper层面向数据库,若是传入明文在加密,安全性不高,Userservice层在两者中间,既能够保证面相用户的逻辑不被破坏,又可以保证一定的安全性)

        上述内容纯纯个人理解!!!小白一个!!!

public interface UserService {

    User findByUsername(String username);

    void register(String username, String password);
}
@Service

public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public User findByUsername(String username) {
        User user = userMapper.findByUsername(username);
        return user;
    }

    @Override
    public void register(String username, String password) {
    //加密
        String md5String = Md5Util.getMD5String(password);
        userMapper.add(username,md5String);


    }
}
2.2.3 UserMapper层逻辑
  • 分别使用@Select和@Insert注解和数据库的相关方法来完成对数据库的操作
@Mapper

public interface UserMapper {
    @Select("select * from user where username=#{username}")
    User findByUsername(String username);
    @Insert("insert into user(username,password,create_time,update_time)" +
            " values(#{username},#{password},now(),now())")
    void add(String username, String password);

}

2.3 用户登录逻辑

2.3.1 UserController层逻辑

  • 通过@Pattern注解来判断用户输入的username和password是否符合要求
  • 调用Userservice接口中的 findByUsername方法来判断当前注册的用户名是否存在,若不存在,则显示用户名错误提示
  • 调用Md5Util类中的.getMD5String方法来将用户在浏览器输入的密码进行加密,然后用.equals方法来将用户在浏览器端输入的密码与数据库中存储的密码进行比对(因为user对象中存储的password是密文,所以只能密文比较)
  • 若是用户名和密码都正确,则要给登录成功的用户分发一个JWT令牌,然后在后续的每次请求中,浏览器都需要在请求头header中携带到服务端,请求头的名称为 Authorization,值为登录时下发的JWT令牌。
    @PostMapping("/login")
    public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password) {
        //根据用户名查询用户
        User loginUser = userService.findByUsername(username);
        //判断该用户是否存在
        if (loginUser == null) {
            return Result.error("用户名错误");
        }
        //判断密码是否正确  loginUser对象中的password是密文
        if (Md5Util.getMD5String(password).equals(loginUser.getPassword())) {
            //登录成功
            //创建HashMap用来存储载荷
            Map<String,Object> claims = new HashMap<>();
            //Hashmap中的value值为id,username
            claims.put("id",loginUser.getId());
            claims.put("username",loginUser.getUsername());
            //生成令牌
            String token = JwtUtil.genToken(claims);
            return Result.success(token);
        }
        return Result.error("密码错误");
    }
2.3.1.2 JWT令牌
package com.liumaji.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;

import java.util.Date;
import java.util.Map;

public class JwtUtil {

    private static final String KEY = "Liumaji";
	
	//接收业务数据,生成token并返回
    public static String genToken(Map<String, Object> claims) {
        //创建JWT令牌
        return JWT.create()
                //添加令牌载荷,在这里用的是HashMap存储载荷(KEY:claims,value:Object集合)
                .withClaim("claims", claims)
                //令牌过期时间
                .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))
                //带有秘钥的加密算法
                .sign(Algorithm.HMAC256(KEY));
    }

	//接收token,验证token,并返回业务数据
    public static Map<String, Object> parseToken(String token) {
        //加密算法
        return JWT.require(Algorithm.HMAC256(KEY))
                //生成验证器
                .build()
                //验证token并解析
                .verify(token)
                //获取key名为claims的value值
                .getClaim("claims")
                //以hashmap形式存储解析出来的值
                .asMap();
    }

}

JWT令牌:

2.3.2 UserService层逻辑

  • 同注册逻辑

2.3.3 UserMapper层逻辑

  • 同注册逻辑

2.3.4 拦截器

        为了防止用户没有登录,而是直接通过输入网址来进行信息查询和更改,所以要引入拦截器以保证只有进行过登录的用户才能进行相应操作。

  • @Component:将拦截器对象注册到IOC容器里面

        因为用户的所有请求数据都在HttpServletRequest对象里面,所以在拦截器对象中定义的方法里面传入HttpServletRequest参数,来保证所有的用户请求都可以被拦截(登录和注册请求除外)

  • 首先根据用户请求参数里面名为header(即Authorization)的部分来获取令牌
  • 其次调用JwtUtil类中的解析办法parsetoken进行解析,若是能够解析,则说明用户已经登录;将成功解析的用户数据放到一个线程池(ThreadLocal中)并放行,否则就是用户未登录,则浏览器报401错误。

成功解析令牌的用户返回应该是JWT有效载荷部分(id,username等)
成功解析的令牌例子:

package com.liumaji.interceptors;

import com.liumaji.dto.Result;
import com.liumaji.utils.JwtUtil;
import com.liumaji.utils.ThreadLocalUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.Map;
//拦截器的逻辑代码
@Component//拦截器对象注册到IOC容器里面
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception{
        //令牌验证
        //所有请求数据都在Request对象里面
        String token = request.getHeader("Authorization");
        try {
            Map<String, Object> claims = JwtUtil.parseToken(token);
            //把业务数据存储到ThreadLocal中
            ThreadLocalUtil.set(claims);
            return true;//放行
        } catch (Exception e) {
            //http 响应状态码401
            response.setStatus(401);
            return false;//不放行
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //清空ThreadLocal中的数据
        ThreadLocalUtil.remove();
    }
}
/注册拦截器
//配置类(WebConfig)也需要注册到IOC容器里面
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        //登录接口和注册接口不拦截
        registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login","/user/register");
    }

}
 2.3.5 线程池

        ThreadLocal是为了让每个用户只能使用自己部分的数据,防止与其他用户数据共享发生冲突,ThreadLocal类中每个方法的用处已在代码中注释。

package com.liumaji.utils;

import java.util.HashMap;
import java.util.Map;

/**
 * ThreadLocal 工具类
 */
@SuppressWarnings("all")
public class ThreadLocalUtil {
    //提供ThreadLocal对象,维护全局唯一一个THREAD_LOCAL
    //private static final 一起使用:当这三个关键字一起使用时,表示该成员是类级别的常量,并且只能在类内部访问
    //private:该常量只能在 ThreadLocalUtil 类内部访问。
    // static:该常量属于类本身,而不是某个实例。所有实例共享同一个值,并且可以在没有实例的情况下访问。
    //final:该常量一旦被赋值后就不能再修改
    private static final ThreadLocal THREAD_LOCAL = new ThreadLocal();

    //根据键获取值
    //采用泛型方法get,因为对于每个用户的THREAD_LOCAL可能存放多种数据类型,所以采用泛型方法以适配各种数据类型
    //在取出相应数据后进行相应的数据转换
    public static <T> T get(){
        return (T) THREAD_LOCAL.get();
    }
	
    //存储键值对
    public static void set(Object value){
        THREAD_LOCAL.set(value);
    }


    //清除ThreadLocal 防止内存泄漏
    public static void remove(){
        THREAD_LOCAL.remove();
    }
}

2.4 获取用户详细信息逻辑

2.4.1 UserController层逻辑

  • 从线程池ThreadLocal中获取key为(claims)的用户相关信息(因为只要成功登录的用户,他的相关信息都放在了线程池中(在拦截器中实习))
  • 获取登录的用户的username
  • 根据用户名返回用户的相关信息
 @GetMapping("/userInfo")
    public Result<User> userInfo(/*@RequestHeader(name = "Authorization") String token*/){
        //根据用户名查询用户
//        Map<String, Object> map = JwtUtil.parseToken(token);
//        String username =(String) map.get("username")
        Map<String,Object> map = ThreadLocalUtil.get();
        String username =(String) map.get("username");
        User user = userService.findByUsername(username);
        return Result.success(user);
    }

2.4.2 UserService层逻辑

  • 同登录逻辑

2.4.3 UserMapper层逻辑

  • 同登录逻辑

2.5 更新用户基本信息逻辑

2.5.1 UserController层逻辑

  • 根据接口文档可知,用户请求的数据格式为application/json格式,所以参数选择用@RequestBody注解封装User类信息
  • 调用service层的update方法,完成用户信息更新
  • 为满足接口文档中的参数要求:
    • 在实体类中用相应标签进行限制
    • 在Controller层搭配使用@Validated注解来完成对实体类User中相关参数的限制

@PutMapping("/update")
    public Result update(@RequestBody @Validated User user){
        userService.update(user);
        return Result.success();
    }
@Data
public class User {
    @NotNull
    private Integer id;//主键ID

    private String username;//用户名

    @JsonIgnore//让springMVC吧当前对象转换成json字符串的时候,忽略password,最终的json字符串就没有password这个属性了
    private String password;//密码

    @NotEmpty
    @Pattern(regexp = "^\\S{1,10}$")
    private String nickname;//昵称

    @NotEmpty
    @Email
    private String email;//邮箱

    private String userPic;//用户头像地址

    private LocalDateTime createTime;//创建时间
    private LocalDateTime updateTime;//更新时间
}

2.5.2 UserService层逻辑 

  • 在service接口中定义update方法
  • 重写接口中的update方法,调用Mapper层的update方法,完成对数据库的更新操作
 @Override
    public void update(User user) {
        user.setUpdateTime(LocalDateTime.now());
        userMapper.update(user);

    }

2.5.3 UserMapper层逻辑

  • 在这里updatetime是接收从service接口中传递过来的时间进行更新,也可以使用数据库函数now()来完成更新时间的更新(同注册Mapper)
 @Update("update user set nickname=#{nickname},email=#{email},update_time=#{updateTime} where id = #{id}")
    void update(User user);

2.6 更新用户头像逻辑

2.6.1 UserController层逻辑

  • 根据接口文档,用户请求数据格式为url,所以使用@RequestParam注解来封装avtaUrl,同时,因为它的数据格式得保证为url形式,所以加了一个@URL注解
  • 该url存放的是图片所在的url

        然后本人在实验过程中发现当传递空的url时,也可以更新头像,所以为了防止这种情况,自己加入了@NotEmpty注解。

        在刚开始时,想想更新用户基本信息一样,在User实体类中用户头像地址参数中加入@URL然后在Controller层和@Validated注解搭配使用,从而实现参数为URL地址的限制,但是没有实现。

        原因说明:因为是在User实体类里面限制的用户头像地址参数,但在Controller层里面更新用户头像的请求参数并不是User实体类,而搭配@Validated注解只能限制将User实体类作为请求参数的限制,所以@URL使用在User实体类并不能限制

    @PatchMapping("/updateAvatar")
    public Result updateAvatar(@RequestParam @URL @NotEmpty String avatarUrl){
        userService.updateAvatar(avatarUrl);
        return Result.success();
    }

2.6.2 UserService层逻辑

  • 在service层首先调用ThreadLocalUtil.get(),获取用户的相关信息
  • 然后获取用户的相关id(为什么在更新用户信息不用获取id呢?因为在controller层传递的参数为user对象,里面有id,username等唯一性信息,所以不会定位错;而在更新头像时,只是传递了url在controller层,无法确定要更新哪个用户的头像,所以在service层需要获取用户id,以调用mapper层的方法来完成对数据库的更新)
  • 调用mapper层的相关方法,完成对数据库的更新
    void updateAvatar(String avatarUrl);

 @Override
    public void updateAvatar(String avatarUrl) {
        Map<String,Object> map = ThreadLocalUtil.get();
        Integer id = (Integer) map.get("id");
        userMapper.updateAvatar(avatarUrl,id);
        
    }

2.6.3 UserMapper层逻辑

 @Update("update user set user_pic = #{avatarUrl},update_time=now() where id = #{id}")
    void updateAvatar(String avatarUrl,Integer id);

2.7 更新用户密码逻辑

2.7.1 UserController层逻辑

  • 根据接口文档:请求参数类型为json类型,然后是HashMap<String,String>类型
  • 首先获取请求参数的相关信息
  • 其次判断三个请求参数是否都有,若有空的,则返回错误
  • 因为能够更新用户密码的前提是已经登录,所以从线程池中获取用户的相关信息
  • 然后获取用户的username,然后根据username获取用户User实体类的相关参数
  • 调用User实体类中的loginUser.getPassword()方法获取其在数据库中已经加密了的密码,和用户已经加密的原密码进行比较
  • 最后在比较两次输入的新密码是否一致
  • 若上述全部通过,则调用service层的更新密码的方法

  @PatchMapping("/updatePwd")
    public Result updatePwd(@RequestBody Map<String,String> params){
        //校验参数
        String oldPwd = params.get("old_pwd");
        String newPwd = params.get("new_pwd");
        String rePwd = params.get("re_pwd");

        if (!StringUtils.hasLength(oldPwd)||!StringUtils.hasLength(newPwd)||!StringUtils.hasLength(rePwd))
            return Result.error("缺少必要的参数");
        //校验原密码是否正确
        //调用Userservice拿到用户原密码,再和old_pwd比对
        Map<String,Object> map = ThreadLocalUtil.get();
        String username = (String) map.get("username");
        User loginUser = userService.findByUsername(username);
        if(!loginUser.getPassword().equals(Md5Util.getMD5String(oldPwd))){
            return Result.error("原密码填写不正确");
        }
        if (!rePwd.equals(newPwd)){
            return Result.error("两次填写的新密码不一样");
        }
        //调用sevice完成密码更新

        userService.updatePwd(newPwd);
        return Result.success();
    }

2.7.2 UserService层逻辑

  • 使用ThreadLocal获取用户的相关信息

  • 获取用户的id

  • 根据用户的id将经过加密的新密码更新在数据库
 @Override
    public void updatePwd(String newPwd) {
        Map<String,Object> map = ThreadLocalUtil.get();
        Integer id = (Integer) map.get("id");
        userMapper.updatePwd(Md5Util.getMD5String(newPwd),id);

2.7.3 UserMapper层逻辑

@Update("update user set password = #{md5String},update_time=now() where id = #{id}")
    void updatePwd(String md5String, Integer id);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2131839.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构】带你初步了解排序算法

文章目录 1. 排序的概念及运用1.1 概念1.2 运用 2. 常见的排序算法2.1 插入排序2.1.1 直接插入排序&#xff08;简单插入排序&#xff09;2.1.2 希尔排序 2.2 选择排序2.2.1 直接选择排序&#xff08;简单选择排序&#xff09;2.2.2 堆排序 2.3 交换排序2.3.1 冒泡排序2.3.2 快…

python | 2行命令解决pip模块不存在问题

一、报错情况 有时&#xff0c;在执行 pip 更新命令后&#xff0c;会提示更新失败或错误警告。 报错提示如下&#xff1a; 然后&#xff0c;再次使用 pip 安装命令时&#xff0c;会提示 pip 模块找不到或不存在&#xff1a; ModuleNotFoundError: No module named pip 导致…

在线IP代理检测:保护您的网络安全

在互联网飞速发展的今天&#xff0c;越来越多的人开始意识到网络安全和隐私保护的重要性。在线IP代理检测工具作为一种有效的网络安全手段&#xff0c;能够帮助用户识别和检测IP代理的使用情况&#xff0c;从而更好地保护个人隐私和数据安全。本文将详细介绍在线IP代理检测的相…

SQL数据库(MySQL)

一、在Ubuntu系统下安装MySQL数据库 1、更新软件源&#xff0c;在确保ubuntu系统能正常上网的情况下执行以下命令 sudo apt-get update 2、安装MySQL数据库及相关软件包 # 安装过程中设置root用户的密码 123456 sudo apt-get install mysql-server ​ # 安装访问数据库的客…

Rsync——远程同步

目录 一、rsync远程同步概述 1、rsync 简介 2、rsync的同步方式 3、rsync的备份方式 4、rsync与cp、scp对比 二、常用rsync命令 1、基本格式 2、配置源的两种表达方法 三、搭建rsync下行同步 1、搭建环境 2、配置rsync源服务器&#xff08;172.16.88.44&#xff09;…

出版学术专著需要具备哪些条件?

出版学术专著通常需要具备以下条件&#xff1a; 一、学术价值 1. 创新性 - 你的专著应在研究主题、方法、观点等方面具有一定的创新性。这可以是提出新的理论框架、发现新的现象、采用新的研究方法或对已有理论进行新的阐释和拓展。 - 例如&#xff0c;在某一特定学科领域中&…

【北京迅为】《STM32MP157开发板使用手册》- 第二十七章Cortex-M4按键实验

iTOP-STM32MP157开发板采用ST推出的双核cortex-A7单核cortex-M4异构处理器&#xff0c;既可用Linux、又可以用于STM32单片机开发。开发板采用核心板底板结构&#xff0c;主频650M、1G内存、8G存储&#xff0c;核心板采用工业级板对板连接器&#xff0c;高可靠&#xff0c;牢固耐…

跟《经济学人》学英文:2024年09月07日这期 What to read about the British economy

What to read about the British economy Britain used to be the world’s richest country. These six books explain how it came to be, and why it is no longer 原文&#xff1a; IN RECENT YEARS the British economy has tended to be in the news for the wrong re…

凯伦股份融合®️TMP复合瓦系统实力硬扛摩羯台风

第11号台风“摩羯”&#xff0c;今年以来登陆我国的最强台风&#xff0c;也是继2014年“威马逊”之后登陆我国的最强台风。 沿海多地发布防风Ⅰ级应急响应&#xff0c;多市启动落实“六停”措施。面对17级台风&#xff0c;工商业厂房遭受严重的破坏。据前方报道&#xff0c;当地…

Vue实用操作篇-1-第一个 Vue 程序

安装 Vue 非常的简便&#xff0c;只需下载好 Vue 对应的 .js 文件&#xff0c;在 html 中引入 vue.js 即可使用 Vue 下载好了 vue.js 我们便可以编写我们的第一个 vue 程序了 <!doctype html> <html lang"zh-CN"><head><meta charset"utf…

【数据结构】十大经典排序算法总结与分析

文章目录 前言1. 十大经典排序算法分类2. 相关概念3. 十大经典算法总结4. 补充内容4.1 比较排序和非比较排序的区别4.2 稳定的算法就真的稳定了吗&#xff1f;4.3 稳定的意义4.4 时间复杂度的补充4.5 空间复杂度补充 结语 前言 排序算法是《数据结构与算法》中最基本的算法之一…

计算机视觉(一)—— 特刊推荐

特刊征稿 01 期刊名称&#xff1a; Computer Vision for Smart Cities 截止时间&#xff1a; 提交截止日期&#xff1a;2024 年 12 月 31 日 目标及范围&#xff1a; 以下是一些潜在的主题&#xff1a; 城市交通和交通管理&#xff1a; • 车辆检测和跟踪以实现高效的交通流…

相机SD卡删除的照片可以恢复吗?6个方法,快速找回删除照片!

相机SD卡的照片在相机中误删了&#xff0c;有什么恢复办法吗&#xff1f;今天我要和大家分享一些关于如何恢复相机SD卡中删除的照片的方法。相信很多摄影爱好者都遇到过不小心删除了重要照片的情况&#xff0c;这时候我们该怎么办呢&#xff1f;别担心&#xff0c;下面我将为大…

野兔在线工具箱系统(市面上最强最多)最新版本更新2024.9

野兔在线工具箱系统&#xff0c;采用最新ThinkPHP8框架开发完成&#xff0c;也是基于YETUADMIN开发的工具箱系统&#xff0c;这次野兔在线工具系统更新&#xff0c;更新了几个新的功能模块&#xff0c;和已知的问题&#xff0c;修复系统部分功能。 程序开发 程序名称&#xf…

【生产力必备工具】GPU加速计算的首选云服务——蓝耘GPU(点击我的链接注册登录,可获50使用卷)

点击下面我的链接注册并登录&#xff0c;可获50使用卷&#xff1a;https://cloud.lanyun.net/#/registerPage?promoterCode11f606c51ehttps://cloud.lanyun.net/#/registerPage?promoterCode11f606c51e获得广泛丰富的NVIDIA高端GPU选择。高可配置高可用&#xff0c;专为大规模…

Linux之CentOS 7.9-Minimal部署Oracle 11g r2 安装实测验证(桌面模式)

前言: 发个之前的库存… Linux之CentOS 7.9-Minimal部署Oracle 11g r2 安装实测验证(桌面模式) 本次验证的是CentOS_7_Minimal-2009桌面模式来部署Oracle 11g r2,大家可根据自身环境及学习来了解。 环境:下载地址都给你们超链好了 1、Linux系统镜像包: 1.1 CentOS-7-x86_…

系统出现d3dcompiler_47.dll缺失怎么修复?总结6种d3dcompiler_47.dll修复方法

在现代电脑游戏中&#xff0c;​d3dcompiler_47.dll​ 文件是一个非常重要的组件&#xff0c;它用于DirectX应用程序的编译。然而&#xff0c;许多用户在尝试运行游戏或应用程序时&#xff0c;都会遇到“d3dcompiler_47.dll缺失”的错误。本文将为您提供解决此问题的详细步骤和…

2024年江西省职业院校技能大赛赛项规程 (简要概括)

这里写目录标题 一、赛项说明二、大赛时间三、参赛资格四、名额分配五、竞赛规程六、选拔方式七、报名办法八、奖项设置九、大赛QQ群十、资格审查 一、赛项说明 二、大赛时间 2024年十月至十二月 具体时间 地点 参考 赛项信息表 三、参赛资格 四、名额分配 五、竞赛规程 六、…

安全、稳定、高速的跨国文件传输系统

在全球化的大潮中&#xff0c;跨国企业的合作日益频繁&#xff0c;这使得跨国文件传输变得至关重要。企业在这一过程中追求的是快速、安全且稳定的文件传输服务。然而&#xff0c;跨国传输文件时&#xff0c;企业往往会遇到一些挑战。 要实现跨国文件传输的高效、安全与稳定&am…

DAPIER™(大片儿),一款专业的计算机艺术软件,一键式计算机艺术品快速智能(非AI)生成程序

DAPIER™是北京联高软件开发有限公司开发的计算机艺术软件&#xff0c;艺术的计算机生成类软件。 看看一些效果图吧。