谷粒商城—分布式高级②.md

认证服务

1. 环境搭建

创建gulimall-auth-server模块,导依赖,引入login.htmlreg.html,并把静态资源放到nginx的static目录下

2. 注册功能

(1) 验证码倒计时

在这里插入图片描述

//点击发送验证码按钮触发下面函数
$("#sendCode").click(function () {
   
		//如果有disabled,说明最近已经点过,则什么都不做
		if($(this).hasClass("disabled")){
   

		}else {
   
            //调用函数使得当前的文本进行倒计时功能
			timeOutChangeStyle();
			//发送验证码
			var phone=$("#phoneNum").val();
			$.get("/sms/sendCode?phone="+phone,function (data){
   
				if (data.code!=0){
   
					alert(data.msg);
				}
			})
		}
	})

	let time = 60;
	function timeOutChangeStyle() {
   
		//开启倒计时后设置标志属性disable,使得该按钮不能再次被点击
		$("#sendCode").attr("class", "disabled");
        //当时间为0时,说明倒计时完成,则重置
		if(time==0){
   
			$("#sendCode").text("点击发送验证码");
			time=60;
			$("#sendCode").attr("class", "");
		}else {
   
            //每秒调用一次当前函数,使得time--
			$("#sendCode").text(time+"s后再次发送");
			time--;
			setTimeout("timeOutChangeStyle()", 1000);
		}
	}
(2) 整合短信服务

在阿里云网页购买试用的短信服务

gulimall-third-party中编写发送短信组件,其中hostpathappcode可以在配置文件中使用前缀spring.cloud.alicloud.sms进行配置

@Data
@ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
@Controller
public class SmsComponent {
   

    private String host;
    private String path;
    private String appcode;

    public void sendCode(String phone,String code) {
   
//        String host = "http://dingxin.market.alicloudapi.com";
//        String path = "/dx/sendSms";
        String method = "POST";
//        String appcode = "你自己的AppCode";
        Map<String, String> headers = new HashMap<String, String>();
        //最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
        headers.put("Authorization", "APPCODE " + appcode);
        Map<String, String> querys = new HashMap<String, String>();
        querys.put("mobile",phone);
        querys.put("param", "code:"+code);
        querys.put("tpl_id", "TP1711063");
        Map<String, String> bodys = new HashMap<String, String>();


        try {
   
            /**
             * 重要提示如下:
             * HttpUtils请从
             * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java
             * 下载
             *
             * 相应的依赖请参照
             * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml
             */
            HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
            System.out.println(response.toString());
            //获取response的body
            //System.out.println(EntityUtils.toString(response.getEntity()));
        } catch (Exception e) {
   
            e.printStackTrace();
        }
    }
}

编写controller,给别的服务提供远程调用发送验证码的接口

@Controller
@RequestMapping(value = "/sms")
public class SmsSendController {
   

    @Resource
    private SmsComponent smsComponent;

    /**
     * 提供给别的服务进行调用
     * @param phone 电话号码
     * @param code 验证码
     * @return
     */
    @ResponseBody
    @GetMapping(value = "/sendCode")
    public R sendCode(@RequestParam("phone") String phone, @RequestParam("code") String code) {
   

        //发送验证码
        smsComponent.sendCode(phone,code);
        System.out.println(phone+code);
        return R.ok();
    }
}
(3) 接口防刷

由于发送验证码的接口暴露,为了防止恶意攻击,我们不能随意让接口被调用。

  • 在redis中以phone-code将电话号码和验证码进行存储并将当前时间与code一起存储
    • 如果调用时以当前phone取出的v不为空且当前时间在存储时间的60s以内,说明60s内该号码已经调用过,返回错误信息
    • 60s以后再次调用,需要删除之前存储的phone-code
    • code存在一个过期时间,我们设置为10min,10min内验证该验证码有效
@GetMapping("/sms/sendCode")
@ResponseBody
public R sendCode(@RequestParam("phone")String phone) {
   
   //接口防刷,在redis中缓存phone-code
    ValueOperations<String, String> ops = redisTemplate.opsForValue();
    String prePhone = AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone;
    String v = ops.get(prePhone);
    if (!StringUtils.isEmpty(v)) {
   
        long pre = Long.parseLong(v.split("_")[1]);
        //如果存储的时间小于60s,说明60s内发送过验证码
        if (System.currentTimeMillis() - pre < 60000) {
   
            return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(), BizCodeEnum.SMS_CODE_EXCEPTION.getMsg());
        }
    }
    //如果存在的话,删除之前的验证码
    redisTemplate.delete(prePhone);
    //获取到6位数字的验证码
    String code = String.valueOf((int)((Math.random() + 1) * 100000));
    //在redis中进行存储并设置过期时间
    ops.set(prePhone,code+"_"+System.currentTimeMillis(),10, TimeUnit.MINUTES);
    thirdPartFeignService.sendCode(phone, code);
    return R.ok();
}
(4) 注册接口编写

gulimall-auth-server服务中编写注册的主体逻辑

  • 若JSR303校验未通过,则通过BindingResult封装错误信息,并重定向至注册页面
  • 若通过JSR303校验,则需要从redis中取值判断验证码是否正确,正确的话通过会员服务注册
  • 会员服务调用成功则重定向至登录页,否则封装远程服务返回的错误信息返回至注册页面

注: RedirectAttributes可以通过session保存信息并在重定向的时候携带过去

 @PostMapping("/register")
    public String register(@Valid UserRegisterVo registerVo, BindingResult result, RedirectAttributes attributes) {
   
        //1.判断校验是否通过
        Map<String, String> errors = new HashMap<>();
        if (result.hasErrors()){
   
            //1.1 如果校验不通过,则封装校验结果
            result.getFieldErrors().forEach(item->{
   
                errors.put(item.getField(), item.getDefaultMessage());
                //1.2 将错误信息封装到session中
                attributes.addFlashAttribute("errors", errors);
            });
            //1.2 重定向到注册页
            return "redirect:http://auth.gulimall.com/reg.html";
        }else {
   
            //2.若JSR303校验通过
            //判断验证码是否正确
            String code = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + registerVo.getPhone());
            //2.1 如果对应手机的验证码不为空且与提交上的相等-》验证码正确
            if (!StringUtils.isEmpty(code) && registerVo.getCode().equals(code.split("_")[0])) {
   
                //2.1.1 使得验证后的验证码失效
                redisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + registerVo.getPhone());

                //2.1.2 远程调用会员服务注册
                R r = memberFeignService.register(registerVo);
                if (r.getCode() == 0) {
   
                    //调用成功,重定向登录页
                    return "redirect:http://auth.gulimall.com/login.html";
                }else {
   
                    //调用失败,返回注册页并显示错误信息
                    String msg = (String) r.get("msg");
                    errors.put("msg", msg);
                    attributes.addFlashAttribute("errors", errors);
                    return "redirect:http://auth.gulimall.com/reg.html";
                }
            }else {
   
                //2.2 验证码错误
                errors.put("code", "验证码错误");
                attributes.addFlashAttribute("errors", errors);
                return "redirect:http://auth.gulimall.com/reg.html";
            }
        }
    }

通过gulimall-member会员服务注册逻辑

  • 通过异常机制判断当前注册会员名和电话号码是否已经注册,如果已经注册,则抛出对应的自定义异常,并在返回时封装对应的错误信息
  • 如果没有注册,则封装传递过来的会员信息,并设置默认的会员等级、创建时间
 @RequestMapping("/register")
    public R register(@RequestBody MemberRegisterVo registerVo) {
   
        try {
   
            memberService.register(registerVo);
            //异常机制:通过捕获对应的自定义异常判断出现何种错误并封装错误信息
        } catch (UserExistException userException) {
   
            return R.error(BizCodeEnum.USER_EXIST_EXCEPTION.getCode(), BizCodeEnum.USER_EXIST_EXCEPTION.getMsg());
        } catch (PhoneNumExistException phoneException) {
   
            return R.error(BizCodeEnum.PHONE_EXIST_EXCEPTION.getCode(), BizCodeEnum.PHONE_EXIST_EXCEPTION.getMsg());
        }
        return R.ok();
    }

public void register(MemberRegisterVo registerVo) {
   
    //1 检查电话号是否唯一
    checkPhoneUnique(registerVo.getPhone());
    //2 检查用户名是否唯一
    checkUserNameUnique(registerVo.getUserName());
    //3 该用户信息唯一,进行插入
    MemberEntity entity = new MemberEntity();
    //3.1 保存基本信息
    entity.setUsername(registerVo.getUserName());
    entity.setMobile(registerVo.getPhone());
    entity.setCreateTime(new Date());
    //3.2 使用加密保存密码
    BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    String encodePassword = passwordEncoder.encode(registerVo.getPassword());
    entity.setPassword(encodePassword);
    //3.3 设置会员默认等级
    //3.3.1 找到会员默认登记
    MemberLevelEntity defaultLevel = memberLevelService.getOne(new QueryWrapper<MemberLevelEntity>().eq("default_status", 1));
    //3.3.2 设置会员等级为默认
    entity.setLevelId(defaultLevel.getId());

    // 4 保存用户信息
    this.save(entity);
}

private void checkUserNameUnique(String userName) {
   
    Integer count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("username", userName));
    if (count > 0) {
   
        throw new UserExistException();
    }
}

private void checkPhoneUnique(String phone) {
   
    Integer count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("mobile", phone));
    if (count > 0) {
   
        throw new PhoneNumExistException();
    }
}

3. 用户名密码登录

gulimall-auth-server模块中的主体逻辑

  • 通过会员服务远程调用登录接口
    • 如果调用成功,重定向至首页
    • 如果调用失败,则封装错误信息并携带错误信息重定向至登录页
@RequestMapping("/login")
public String login(UserLoginVo vo,RedirectAttributes attributes){
   
    R r = memberFeignService.login(vo);
    if (r.getCode() == 0) {
   
        return "redirect:http://gulimall.com/";
    }else {
   
        String msg = (String) r.get("msg");
        Map<String, String> errors = new HashMap<>();
        errors.put("msg", msg);
        attributes.addFlashAttribute("errors", errors);
        return "redirect:http://auth.gulimall.com/login.html";
    }
}

gulimall-member模块中完成登录

  • 当数据库中含有以当前登录名为用户名或电话号且密码匹配时,验证通过,返回查询到的实体
  • 否则返回null,并在controller返回用户名或密码错误
@RequestMapping("/login")
public R login(@RequestBody MemberLoginVo loginVo) {
   
    MemberEntity entity=memberService.login(loginVo);
    if (entity!=null){
   
        return R.ok();
    }else {
   
        return R.error(BizCodeEnum.LOGINACCT_PASSWORD_EXCEPTION.getCode(), BizCodeEnum.LOGINACCT_PASSWORD_EXCEPTION.getMsg());
    }
}

	@Override
    public MemberEntity login(MemberLoginVo loginVo) {
   
        String loginAccount = loginVo.getLoginAccount();
        //以用户名或电话号登录的进行查询
        MemberEntity entity = this.getOne(new QueryWrapper<MemberEntity>().eq("username", loginAccount).or().eq("mobile", loginAccount));
        if (entity!=null){
   
            BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
            boolean matches = bCryptPasswordEncoder.matches(loginVo.getPassword(), entity.getPassword());
            if (matches){
   
                entity.setPassword("");
                return entity;
            }
        }
        return null;
    }

4. 社交登录

(1) oauth2.0

在这里插入图片描述

(2) 在微博开放平台创建应用

在这里插入图片描述

(3) 在登录页引导用户至授权页
GET
https://api.weibo.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI
  • client_id: 创建网站应用时的app key
  • YOUR_REGISTERED_REDIRECT_URI: 认证完成后的跳转链接(需要和平台高级设置一致)

在这里插入图片描述

如果用户同意授权,页面跳转至 YOUR_REGISTERED_REDIRECT_URI/?code=CODE

code是我们用来换取令牌的参数

(4) 换取token
POST
https://api.weibo.com/oauth2/access_token?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&grant_type=authorization_code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI&code=CODE
  • client_id: 创建网站应用时的app key
  • client_secret: 创建网站应用时的app secret
  • YOUR_REGISTERED_REDIRECT_URI: 认证完成后的跳转链接(需要和平台高级设置一致)
  • code:换取令牌的认证码

返回数据如下

在这里插入图片描述

(5) 获取用户信息

https://open.weibo.com/wiki/2/users/show

结果返回json

(6) 代码编写

认证接口

  • 通过HttpUtils发送请求获取token,并将token等信息交给member服务进行社交登录
  • 若获取token失败或远程调用服务失败,则封装错误信息重新转回登录页
@Controller
public class OauthController {
   

    @Autowired
    private MemberFeignService memberFeignService;

    @RequestMapping("/oauth2.0/weibo/success")
    public String authorize(String code, RedirectAttributes attributes) throws Exception {
   
        //1. 使用code换取token,换取成功则继续2,否则重定向至登录页
        Map<String, String> query = new HashMap<>();
        query.put("client_id", "2144***074");
        query.put("client_secret", "ff63a0d8d5*****29a19492817316ab");
        query.put("grant_type", "authorization_code");
        query.put("redirect_uri", "http://auth.gulimall.com/oauth2.0/weibo/success");
        query.put("code", code);
        //发送post请求换取token
        HttpResponse response = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", "post", new HashMap<String, String>(), query, new HashMap<String, String>());
        Map<String, String> errors = new HashMap<>();
        if (response.getStatusLine().getStatusCode() == 200) {
   
            //2. 调用member远程接口进行oauth登录,登录成功则转发至首页并携带返回用户信息,否则转发至登录页
            String json = EntityUtils.toString(response.getEntity());
            SocialUser socialUser = JSON.parseObject(json, new TypeReference<SocialUser>() {
   
            });
            R login = memberFeignService.login(socialUser);
            //2.1 远程调用成功,返回首页并携带用户信息
            if (login.getCode() == 0) {
   
                String jsonString = JSON.toJSONString(login.get("memberEntity"));
                MemberResponseVo memberResponseVo = JSON.parseObject(jsonString, new TypeReference<MemberResponseVo>() {
   
                });
                attributes.addFlashAttribute("user", memberResponseVo);
                return "redirect:http://gulimall.com";
            }else {
   
                //2.2 否则返回登录页
                errors.put("msg", "登录失败,请重试");
                attributes.addFlashAttribute("errors", errors);
                return "redirect:http://auth.gulimall.com/login.html";
            }
        }else {
   
            errors.put("msg", "获得第三方授权失败,请重试");
            attributes.addFlashAttribute("errors", errors);
            return "redirect:http://auth.gulimall.com/login.html";
        }
    }

登录接口

  • 登录包含两种流程,实际上包括了注册和登录
  • 如果之前未使用该社交账号登录,则使用token调用开放api获取社交账号相关信息,注册并将结果返回
  • 如果之前已经使用该社交账号登录,则更新token并将结果返回
@RequestMapping("/oauth2/login")
public R login(@RequestBody SocialUser socialUser) {
   
    MemberEntity entity=memberService.login(socialUser);
    if (entity!=null){
   
        return R.ok().put("memberEntity",entity);
    }else {
   
        return R.error();
    }
}

 @Override
    public MemberEntity login(SocialUser socialUser){
   
        MemberEntity uid = this.getOne(new QueryWrapper<MemberEntity>().eq("uid", socialUser.getUid()));
        //1 如果之前未登陆过,则查询其社交信息进行注册
        if (uid == null) {
   
            Map<String, String> query = new HashMap<>();
            query.put("access_token",socialUser.getAccess_token());
            query.put("uid", socialUser.getUid());
            //调用微博api接口获取用户信息
            String json = null;
            try {
   
                HttpResponse response = HttpUtils.doGet("https://api.weibo.com", "/2/users/show.json", "get", new HashMap<>(), query);
                json = EntityUtils.toString(response.getEntity());
            } catch (Exception e) {
   
                e.printStackTrace();
            }
            JSONObject jsonObject = JSON.parseObject(json);
            //获得昵称,性别,头像
            String name = jsonObject.getString("name");
            String gender = jsonObject.getString("gender");
            String profile_image_url = jsonObject.getString("profile_image_url");
            //封装用户信息并保存
            uid = new MemberEntity();
            MemberLevelEntity defaultLevel = memberLevelService.getOne(new QueryWrapper<MemberLevelEntity>().eq("default_status", 1));
            uid.setLevelId(defaultLevel.getId());
            uid.setNickname(name);
            uid.setGender("m".equals(gender)?0:1);
            uid.setHeader(profile_image_url);
            uid.setAccessToken(socialUser.getAccess_token());
            uid.setUid(socialUser.getUid());
            uid.setExpiresIn(socialUser.getExpires_in());
            this.save(uid);
        }else {
   
            //2 否则更新令牌等信息并返回
            uid.setAccessToken(socialUser.getAccess_token());
            uid.setUid(socialUser.getUid());
            uid.setExpiresIn(socialUser.getExpires_in());
            this.updateById(uid);
        }
        return uid;
    }

5. SpringSession

(1) session 原理

jsessionid相当于银行卡,存在服务器的session相当于存储的现金,每次通过jsessionid取出保存的数据

问题:但是正常情况下session不可跨域,它有自己的作用范围

在这里插入图片描述

(2) 分布式下session共享问题

在这里插入图片描述

(3) 解决方案
1) session复制

在这里插入图片描述

2) 客户端存储

在这里插入图片描述

3) hash一致性

在这里插入图片描述

4) 统一存储

在这里插入图片描述

(4) SpringSession整合redis

通过SpringSession修改session的作用域

在这里插入图片描述

1) 环境搭建

导入依赖

    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

修改配置

spring:
  redis:
    host: 192.168.56.102
  session:
    store-type: redis

添加注解

@EnableRedisHttpSession
public class GulimallAuthServerApplication {
   
2) 自定义配置
  • 由于默认使用jdk进行序列化,通过导入RedisSerializer修改为json序列化

  • 并且通过修改CookieSerializer扩大session的作用域至**.gulimall.com

@Configuration
public class GulimallSessionConfig {
   

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
   
        return new GenericJackson2JsonRedisSerializer();
    }

    @Bean
    public CookieSerializer cookieSerializer() {
   
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setCookieName("GULISESSIONID");
        serializer.setDomainName("gulimall.com");
        return serializer;
    }
}
(5) SpringSession核心原理 - 装饰者模式
  • 原生的获取session时是通过HttpServletRequest获取的
  • 这里对request进行包装,并且重写了包装request的getSession()方法
@Override
protected void doFilterInternal(HttpServletRequest request,
      HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {
   
   request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);

    //对原生的request、response进行包装
   SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
         request, response, this.servletContext);
   SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
         wrappedRequest, response);

   try {
   
      filterChain.doFilter(wrappedRequest, wrappedResponse);
   }
   finally {
   
      wrappedRequest.commitSession();
   }
}

购物车

1. 数据模型分析

(1) 数据存储

购物车是一个读多写多的场景,因此放入数据库并不合适,但购物车又是需要持久化,因此这里我们选用redis存储购物车数据。

(2) 数据结构

在这里插入图片描述

一个购物车是由各个购物项组成的,但是我们用List进行存储并不合适,因为使用List查找某个购物项时需要挨个遍历每个购物项,会造成大量时间损耗,为保证查找速度,我们使用hash进行存储

在这里插入图片描述

(3) VO编写

购物项vo

在这里插入图片描述

public class CartItemVo {
   

    private Long skuId;
	
    //是否选中
    private Boolean check = true;

    //标题
    private String title;
	
    //图片
    private String image;

    //商品套餐属性
    private List<String> skuAttrValues;

    //价格
    private BigDecimal price;

    //数量
    private Integer count;

    //总价
    private BigDecimal totalPrice;
    
      /**
     * 当前购物车项总价等于单价x数量
     * @return
     */
    public BigDecimal getTotalPrice() {
   
        return price.multiply(new BigDecimal(count));
    }

    public void setTotalPrice(BigDecimal totalPrice) {
   
        this.totalPrice = totalPrice;
    }

购物车vo

在这里插入图片描述

public class CartVo {
   

    /**
     * 购物车子项信息
     */
    List

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Nginx(详解以及如何使用)

目录 1. 什么是Nginx&#xff1f; 2. 为什么使用nginx? 3. 安装nginx 3.1?安装nginx的依赖插件 3.2 下载nginx ?3.3?创建一个目录作为nginx的安装路径 ?3.4?解压 ?3.5?进入解压后的目录 3.6?指定nginx的安装路径 ?3.7?编译和安装nginx 3.8 启动nginx ?…

STM32 HAL库标准库+ESP8266+机智云

前言 最近在项目中需要云平台对接&#xff0c;前面使用的是阿里云物理平台&#xff0c;具体开发可以看看我的这个文章&#xff1a;手把手教会使用阿里云平台&#xff0c;不过好像没有可以在手机很方便打开连接的&#xff0c;所以我在网上找到一些资料&#xff0c;发现机智云是…

【前端框架】Vue3 面试题深度解析

本文详细讲解了VUE3相关的面试题&#xff0c;从基础到进阶到高级&#xff0c;分别都有涉及&#xff0c;希望对你有所帮助&#xff01; 基础题目 1. 简述 Vue3 与 Vue2 相比有哪些主要变化&#xff1f; 答案&#xff1a; 响应式系统&#xff1a;Vue2 使用 Object.definePrope…

DarkLabel 2.4使用指南:高效标注视频数据目标检测标签

工具概述 DarkLabel 2.4 是一款强大的多功能标注工具&#xff0c;专为计算机视觉开发者设计&#xff0c;旨在提升标注工作的效率和精确度。其智能标注引擎支持两项核心功能&#xff1a;线性插值标注与多目标跟踪&#xff0c;极大地优化了视频标注过程。 &#xff08;1&#x…

js解析后端传来的如图示的list集合,怎么获取每个map的key和value

如图示&#xff0c;后端传到前端的questTypeList是一个HashMap的list集合 使用c标签将传来的集合放到下拉单选框中&#xff0c; <% taglib prefix"c" uri"http://java.sun.com/jsp/jstl/core" %><html> <body><form action"yo…

SpringSecurity请求流转的本质

1. SpringSecurity核心源码分析 分析SpringSecurity的核心原理,那么我们从哪开始分析?以及我们要分析哪些内容? 系统启动的时候SpringSecurity做了哪些事情?第一次请求执行的流程是什么?SpringSecurity中的认证流程是怎么样的?1.1 系统启动 当我们的Web服务启动的时候,…

[论文阅读] SeeSR: Towards Semantics-Aware Real-World Image Super-Resolution

文章目录 一、前言二、主要贡献三、Introduction四、Methodology4.1 Motivation &#xff1a;4.2Framework Overview.** 一、前言 通信作者是香港理工大学 & OPPO研究所的张磊教授&#xff0c;也是图像超分ISR的一个大牛了。 论文如下 SeeSR: Towards Semantics-Aware Rea…

在VS中通过vcpkg包管理器来安装使用qt5

常用指令 .\vcpkg install 库名 .\vcpkg install 库名版本号.\vcpkg install 库名 --trip x86-windows.\vcpkg list.\vcpkg search 库名 .\vcpkg x-all-installed --7zip PS G:\vcpkg> .\vcpkg help usage: vcpkg <command> [--switches] [--optionsvalues] [argume…

ESXI 8.0 linux vSphere Client service has stopped working.手动启动服务

1、首先在你的esxi中进入到你的VC系统中&#xff0c;我这个是linux部署 #查看每个服务及状态 service-control --status2、你会发现有停止的服务和正在启动的&#xff0c;在不知道具体哪些服务具体负责的功能&#xff0c;那你就一个一个起&#xff0c;边起边试----重要服务我后…

基于SpringBoot+Vue的装修装潢管理系统的设计与实现

获取源码&#xff1a;SpringBootVue的装修装潢公司管理系统: 用户&#xff1a;登录、注册、忘记密码、首页、产品展示、装修案例、装修套餐、装修预约、新闻动态、合作伙伴、在线留言、我的装修、个人中心、我的留言、我的预约、关于我们等功能管理员&#xff1a;登录、首页、户…

DeepSeek部署到本地(解决ollama模型下载失败问题)

一、下载ollama软件安装 1、下载ollama软件 Ollama 下载完成后可以直接进行安装&#xff08;外网&#xff0c;速度可能会有点慢&#xff09; 2、修改安装目录 进去下载的目录&#xff0c;使用cmd打开终端输入OllamaSetup.exe /DIRE:\MySoftware\Ollama 输入完成后会自动打开…

【拥抱AI】GPT Researcher的诞生

一、GPT Researcher 研究过程总结 GPT Researcher 是一个开源的自主智能体&#xff0c;旨在通过利用人工智能技术实现高效、全面且客观的在线研究。它通过一系列创新的设计和优化&#xff0c;解决了传统研究工具&#xff08;如 AutoGPT&#xff09;中存在的问题&#xff0c;如…

什么是Dubbo?Dubbo框架知识点,面试题总结

本篇包含什么是Dubbo&#xff0c;Dubbo的实现原理&#xff0c;节点角色说明&#xff0c;调用关系说明&#xff0c;在实际开发的场景中应该如何选择RPC框架&#xff0c;Dubbo的核心架构&#xff0c;Dubbo的整体架构设计及分层。 主页还有其他的面试资料&#xff0c;有需要的可以…

软件单元测试的技术要求

文章目录 一、软件单元测试的概念二、测试对象三、测试目的四、进入条件五、测试内容六、测试环境七、测试实施方一、软件单元测试的概念 单元测试(Unit Testing),是指对软件中的最小可测试单元进行测试验证。单元测试是白盒测试,主要依据软件详细设计和软件代码进行,不仅…

GPT-Sovits:语音克隆训练-遇坑解决

前言 本来以为3050完全无法执行GPT-Sovits训练的&#xff0c;但经过实践发现其实是可以&#xff0c;并且仅花费了十数分钟便成功训练和推理验证了自己的语音模型。 官方笔记&#xff1a;GPT-SoVITS指南 语雀 项目地址&#xff1a;https://github.com/RVC-Boss/GPT-SoVITS 本人…

如何调用 DeepSeek API:详细教程与示例

目录 一、准备工作 二、DeepSeek API 调用步骤 1. 选择 API 端点 2. 构建 API 请求 3. 发送请求并处理响应 三、Python 示例&#xff1a;调用 DeepSeek API 1. 安装依赖 2. 编写代码 3. 运行代码 四、常见问题及解决方法 1. API 调用返回 401 错误 2. API 调用返回…

成员函数定义后面加const是什么功能:C++中const成员函数的作用

成员函数定义后面加const是什么功能&#xff1a;C中const成员函数的作用 前言C中const成员函数的作用总结 前言 在PX4的代码中的位置控制模块中&#xff0c;有这样一个成员函数 void getAttitudeSetpoint(vehicle_attitude_setpoint_s &attitude_setpoint) const;该函数的…

数据结构-----双向链表

一、双向循环列表 head.h #ifndef __head_h__ #define __head_h__ #include <stdio.h> #include <string.h>…

基于Flask的第七次人口普查数据分析系统的设计与实现

【Flask】基于Flask的第七次人口普查数据分析系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 基于Flask的人口普查可视化分析系统 二、项目界面展示 登录/注册 首页/详情 …

纯手工搭建整套CI/CD流水线指南

目录 一、前言 二、环境准备 1、服务器开荒&#xff08;192.168.1.200&#xff09; 2、离线资源清单&#xff08;提前用U盘拷好&#xff09; 三、硬核安装&#xff1a;比拧螺丝还细的步骤 Step1&#xff1a;搭建GitLab&#xff08;注意&#xff01;这是只内存饕餮&#xf…