B071-项目实战-用户模块--手机注册 管理员登录

news2024/11/27 8:24:53

目录

      • 完成注册功能
        • 后端开发完成
          • UserController
          • UserServiceImpl
          • LogininfoMapper
        • 前端页面完成
          • 绑定数据
          • 绑定事件
          • 准备登录页
      • 管理员登录1
        • 需求分析
        • 登录设计
          • 页面设计
          • 表设计
          • 流程设计
          • 所需技术
      • 员工新增级联操作登录信息
        • EmployeeServiceImpl
        • ShopServiceImpl
      • 管理员登录2
        • 前端页面
        • 后端接口
          • LoginController
          • LoginServiceImpl
          • logininfoMapper
        • 前端登录完成
        • 后端拦截
          • PetHomeWebMvcConfigurer
          • LoginInterceptor
        • 前端拦截
        • 路由拦截
        • 其他

分别启动后端,后台,前台和redis
ph-admin:npm run dev,ph-web:live-server --port=80
redis:redis-server.exe redis.windows.conf

完成注册功能

在这里插入图片描述

后端开发完成

UserController
    /**
     *  用户注册接口
     * @param userDto 自定义Dto接收前端参数
     * @return
     */
    @PostMapping("/register/code")
    public AjaxResult redisterCode(@RequestBody UserDto userDto){
        try {
            userService.redisterCode(userDto);
            return AjaxResult.me();
        } catch (BusinessException e) {
            return AjaxResult.me().setMessage(e.getMessage());
        }catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.me().setMessage("系统繁忙,稍后重试!!");
        }
    }
UserServiceImpl
	@Override
    public void redisterCode(UserDto userDto) {
        //1.校验
        //  1.1空校验
        if(StringUtils.isEmpty(userDto.getPhone()) ||
                StringUtils.isEmpty(userDto.getVerifycode()) ||
                StringUtils.isEmpty(userDto.getPassword()) ||
                StringUtils.isEmpty(userDto.getConfirmPwd()) ){
           throw new BusinessException("请输入完整信息在进行注册!");
        }
        //  1.2两次密码是否一致
        if(!userDto.getPassword().equals(userDto.getConfirmPwd())){
            throw new BusinessException("密码不一致,请重新输入!");
        }
        //  1.3是否被注册
        Logininfo obj = logininfoMapper.loadByDto(userDto);
        if(obj != null){
            throw new BusinessException("用户已经存在!");
        }
        //2.验证码是否存在
        Object codeObj = redisTemplate.opsForValue().get(UserConstant.USER_VERFIY_CODE+":"+userDto.getPhone());
        //  2.1 验证码是否过期
        if(codeObj == null){
            throw new BusinessException("验证码已经过期,请重新发送验证码!");
        }
        String code = ((String)codeObj).split(":")[0];
        //  2.2 验证码是否正确
        if(!userDto.getVerifycode().equalsIgnoreCase(code)){
            throw new BusinessException("请输入正确的验证码!");
        }
        //3.登录成功
        // 3.1登录信息表
        User user = userDto2User(userDto);
        Logininfo logininfo = user2LoginInfo(user);
        logininfoMapper.save(logininfo);//返回自增id
        // 3.2用户表
        user.setInfo(logininfo);//引用属性传值
        userMapper.save(user);
    }

    /**
     * 将dto转换成实体对象
     * @param userDto
     * @return
     */
    private User userDto2User(UserDto userDto) {
        //1.定义需要返回的对象
        User user = new User();
        //2.封装数据
        user.setUsername(userDto.getPhone());
        user.setPhone(userDto.getPhone());
        /*private String salt;
        private String password;*/
        String salt = StrUtils.getComplexRandomString(32);
        String md5Pwd = MD5Utils.encrypByMd5(userDto.getPassword() + salt);
        user.setSalt(salt);//存储加密使用的盐值
        user.setPassword(md5Pwd);//存储使用盐值加密之后的密码
        //3.返回
        return user;
    }

    /**
     * 通过user拷贝LoginInfo
     * @param user
     * @return
     */
    private Logininfo user2LoginInfo(User user) {
        Logininfo info = new Logininfo();
        //直接使用工具类拷贝  同名原则拷贝属性值
        BeanUtils.copyProperties(user, info);
        info.setType(1);//设置前端用户默认值
        return info;
    }
LogininfoMapper
    <!--Logininfo loadByDto(UserDto userDto);-->
    <select id="loadByDto" parameterType="cn.itsource.user.dto.UserDto" resultType="Logininfo">
        SELECT * FROM t_logininfo WHERE phone =  #{phone} and type = #{type}
    </select>

前端页面完成

绑定数据
		data:{
			phoneUserForm:{
				phone:"",
				verifycode:"",
				password:"1",
				confirmPwd:"1",
				type:1
			}
		},
<form method="post">
	 <div class="user-phone">
		<label for="phone"><i class="am-icon-mobile-phone am-icon-md"></i></label>
		<input type="tel" v-model="phoneUserForm.phone" name="" id="phone" placeholder="请输入手机号">
	 </div>
	<div class="verification">
		<label for="code"><i class="am-icon-code-fork"></i></label>
		<input type="tel" name="" v-model="phoneUserForm.verifycode" id="code" placeholder="请输入验证码">
		<!--<a class="btn" href="javascript:void(0);" οnclick="sendMobileCode();" id="sendMobileCode">
			<span id="dyMobileButton">获取</span></a>-->
		<button type="button" @click="sendMobileCode">获取</button>
	</div>
	 <div class="user-pass">
		<label for="password"><i class="am-icon-lock"></i></label>
		<input type="password" name="" v-model="phoneUserForm.password" id="password" placeholder="设置密码">
	 </div>
	 <div class="user-pass">
		<label for="passwordRepeat"><i class="am-icon-lock"></i></label>
		<input type="password" name="" v-model="phoneUserForm.confirmPwd" id="passwordRepeat" placeholder="确认密码">
	 </div>
</form>
绑定事件
<div class="am-cf">
	<input type="submit" name="" @click="register" value="注册" class="am-btn am-btn-primary am-btn-sm am-fl">
</div>
            register(){
                this.$http.post("/user/register/code",this.phoneUserForm)
					.then(result=>{
                        result = result.data;
                        if(result.success){
                            //成功之后? 提示成功
                            alert("注册成功!");
							//跳转到登录页
							location.href="login.html";
						}else{
                            alert("注册失败:"+result.message);
						}
					})
					.catch(result=>{
					    alert("系统异常!");
					})

			},
准备登录页

拷贝login.html到根目录并修改路径

管理员登录1

需求分析,页面,表设计,流程设计,

需求分析

见文档

登录设计

页面设计

见文档

表设计

见文档

流程设计

见文档

所需技术

见文档

员工新增级联操作登录信息

店铺入驻保存shop表和employee表的时候要同步存入logininfo表
员工新增保存employee表的时候也要保存logininfo表

做法:分别在EmployeeServiceImpl和UserServiceImpl重写add,update,del方法,方法内加入同步操作logininfo表的代码

EmployeeServiceImpl

	/**
     * 针对于loginInfo的级联操作:员工和用户都应该有
     * 这里我们只写 员工的
     */
    @Override
    @Transactional
    public void add(Employee employee) {
        initEmployee(employee);
        Logininfo logininfo = employee2LoginInfo(employee);
        logininfoMapper.save(logininfo);
        employee.setLogininfo(logininfo);
        employeeMapper.save(employee);
    }

    @Override
    public void update(Employee employee) {
        //1.修改loginInfo
        Logininfo logininfo = logininfoMapper.loadById(employee.getLogininfo_id());
        if(logininfo != null){
            BeanUtils.copyProperties(employee, logininfo);
            logininfoMapper.update(logininfo);
            employeeMapper.update(employee);
        }
    }

    @Override
    public void delete(Long id) {
        Employee employee = employeeMapper.loadById(id);
        if(employee != null){
            logininfoMapper.remove(employee.getLogininfo_id());
            employeeMapper.remove(id);
        }
    }

    private void initEmployee(Employee employee) {
        //设置盐值
        String salt = StrUtils.getComplexRandomString(32);
        String md5Pwd = MD5Utils.encrypByMd5(employee.getPassword()+salt);
        employee.setSalt(salt);
        employee.setPassword(md5Pwd);
    }

    private Logininfo employee2LoginInfo(Employee employee) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(employee, logininfo);
        logininfo.setType(0);
        return logininfo;
    }

ShopServiceImpl

        //2.先保存员工数据 保存之后返回自增id
//        employeeMapper.save(admin);
        employeeService.add(admin);

管理员登录2

前端页面

ph-admin - login.vue
标签元素 - data - 按钮 - methods

<template>
  <el-form :model="ruleForm2" :rules="rules2" ref="ruleForm2" label-position="left" label-width="0px" class="demo-ruleForm login-container">
    <h3 class="title">系统登录</h3>
    <el-form-item prop="account">
      <el-input type="text" v-model="ruleForm2.username" auto-complete="off" placeholder="账号"></el-input>
    </el-form-item>
    <el-form-item prop="checkPass">
      <el-input type="password" v-model="ruleForm2.password" auto-complete="off" placeholder="密码"></el-input>
    </el-form-item>
    <el-checkbox v-model="checked" checked class="remember">记住密码</el-checkbox>
    <el-form-item style="width:100%;">
      <el-button type="primary" style="width:47%;" @click.native.prevent="login" :loading="logining">登录</el-button>
      <el-button type="success" style="width:47%;" @click.native.prevent="goRegister">店铺入驻</el-button>
    </el-form-item>
  </el-form>
</template>

<script>
  import { requestLogin } from '../api/api';
  //import NProgress from 'nprogress'
  export default {
    data() {
      return {
        logining: false,
        ruleForm2: {
          username: '3',
          password: '3',
          type:0//表名是后端员工登录
        },
        rules2: {
          account: [
            { required: true, message: '请输入账号', trigger: 'blur' },
            //{ validator: validaePass }
          ],
          checkPass: [
            { required: true, message: '请输入密码', trigger: 'blur' },
            //{ validator: validaePass2 }
          ]
        },
        checked: true
      };
    },
    methods: {
      goRegister(){//这里是拷贝的登录成功只有跳转主页
        this.$router.push({ path: '/shopRegister' });/*店铺入驻*/
      },
      handleReset2() {
        this.$refs.ruleForm2.resetFields();
      },
      login(ev) {
        this.$refs.ruleForm2.validate((valid) => {  //点击登录按钮的时候,触发表单校验
          if (valid) {
            this.logining = true; //开启忙等框
            this.$http.post("/login/account",this.ruleForm2)
                    .then(result=>{
                      this.logining = false;//关闭忙等框
                      result = result.data;
                      if(result.success){
                        //提示登录成功
                        this.$message({
                          message: "登录成功!",
                          type: 'success'
                        });
                        //跳转到主页
                        this.$router.push({ path: '/echarts' });
                      }else{
                        this.$message({
                          message: result.message,
                          type: 'error'
                        });
                      }
                    })
                    .catch(result=>{
                      this.logining = false;//关闭忙等框
                      this.$message({
                        message: "系统异常",
                        type: 'error'
                      });
                    })
          } else {
            console.log('error submit!!');
            return false;
          }
        });
      }
    }
  }
</script>

后端接口

LoginController
/**
 * 统一登录接口
 */
@RestController
@RequestMapping("/login")
public class LoginController {
    @Autowired
    ILoginService loginService;

    /**
     * 账号登录,支持前后端账号登录
     */
    @PostMapping("/account")
    public AjaxResult account(@RequestBody LoginDto loginDto){
        try {
            return loginService.account(loginDto);
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.me().setMessage("系统繁忙,稍后重试!");
        }
    }
}
LoginServiceImpl

在这里插入图片描述

/**
 * 统一登录业务层
 */
@Service
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 账号登录
     * loginDto 中表明了是哪一个端登录 type = 0
     */
    @Override
    public AjaxResult account(LoginDto loginDto) {
        //1.校验
        // 1.1 空校验  loginDto所有字段都要校验
        if(StringUtils.isEmpty(loginDto.getUsername()) ||
                StringUtils.isEmpty(loginDto.getPassword()) ||
                StringUtils.isEmpty(loginDto.getType()) ){
            return AjaxResult.me().setMessage("用户或密码不能为空!");
        }
        // 1.2 判断用户是否存在  查询loginInfo表
        Logininfo logininfo = logininfoMapper.loadByLoginDto(loginDto);
        if (logininfo == null) {
            return AjaxResult.me().setMessage("用户不存在!");
        }
        // 1.3 账号是否被禁用
        if(logininfo.getDisable() != 1){
            return AjaxResult.me().setMessage("账号被禁用,请联系管理员!");
        }
        // 2.判断密码是否正确
        String salt = logininfo.getSalt();
        String md5pwd = logininfo.getPassword();
        String md5PwdTmp = MD5Utils.encrypByMd5(loginDto.getPassword() + salt);
        if(!md5pwd.equals(md5PwdTmp)){
            // 2.2 密码不等  抛错
            return AjaxResult.me().setMessage("用户名或密码错误!");
        }
        // 2.1 如果密码相等  登录成功  存redis,封装返回值
        /**
         * 这里的redis  key就是前端需要存储的token
         */
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
        Map<String,Object> map = new HashMap<>();
        map.put("token", token);
        map.put("logininfo",logininfo);
        return AjaxResult.me().setResultObj(map);
    }
}
logininfoMapper
    <select id="loadByLoginDto" parameterType="cn.ming.basic.dto.LoginDto" resultType="Logininfo">
        SELECT * FROM t_logininfo
        WHERE (username =  #{username} or phone = #{username} or email = #{username})
        and type = #{type}
    </select>

前端登录完成

保存信息到localstorage

	login(ev) {
        this.$refs.ruleForm2.validate((valid) => {  //点击登录按钮的时候,触发表单校验
          if (valid) {
            this.logining = true; //开启忙等框
            this.$http.post("/login/account",this.ruleForm2)
                    .then(result=>{
                      this.logining = false;//关闭忙等框
                      result = result.data;
                      if(result.success){
                        //保存信息到localstorage
                        var resultObj =  result.resultObj;
                        localStorage.setItem("token",resultObj.token);
                        localStorage.setItem("logininfo",JSON.stringify(resultObj.logininfo));
                        //提示登录成功
                        this.$message({
                          message: "登录成功!",
                          type: 'success'
                        });
                        //跳转到主页
                        this.$router.push({ path: '/echarts' });
                      }else{
                        this.$message({
                          message: result.message,
                          type: 'error'
                        });
                      }
                    })
                    .catch(result=>{
                      this.logining = false;//关闭忙等框
                      this.$message({
                        message: "系统异常",
                        type: 'error'
                      });
                    })
          } else {
            console.log('error submit!!');
            return false;
          }
        });
      }

前端右上角展示登录人信息

		mounted() {
			var user = localStorage.getItem('logininfo');
			if (user) {
				user = JSON.parse(user);
				this.sysUserName = user.username || user.email || user.phone || '';
				this.sysUserAvatar = user.avatar || '';
			}
		}

后端拦截

除了登录和注册相关的其他所有ajax请求都应该拦截
如:注册,发送验证码,店铺入驻,图片上传,

PetHomeWebMvcConfigurer
@Configuration
public class PetHomeWebMvcConfigurer implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login/**")
                .excludePathPatterns("/user/register/**")
                .excludePathPatterns("/verifycode/**")
                .excludePathPatterns("/fastDfs")
                .excludePathPatterns("/shop/settlement");
    }
}
LoginInterceptor
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Autowired
    private RedisTemplate redisTemplate;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        //判断是否登录?  前端需要给我们传递 token
        String token = request.getHeader("token");// fde6185f-36ac-4df0-9c0e-9d4c4ebb617c
        if(!StringUtils.isEmpty(token)){
            Object obj = redisTemplate.opsForValue().get(token);
            if(obj !=null){//已经登录过
                //刷新redis的token存储时间
                redisTemplate.opsForValue().set(token, obj, 30, TimeUnit.MINUTES);
                //放行
                return true;
            }
        }
        //如果已经登录过期
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        writer.write("{\"success\":false,\"message\":\"noLogin\"}");
        writer.flush();
        writer.close();

        //@TODO 权限校验
        return false;
    }
}

前端拦截

axios前置拦截:发送axios请求前给请求加上token

axios后置拦截:接收到后端拦截器的nologin信息后跳到登录页

写在main.js,vue下面

/**axios前置拦截器
 *作用:每次发送axios请求,需要携带token给后端
 */
axios.interceptors.request.use(config=>{
  //携带token
  let uToken =  localStorage.getItem("token");
  if(uToken){
    config.headers['token']=uToken;
  }
  return config;//一定要返回配置
},error => {
  Promise.reject(error);
})

/**
 * axios后置拦截:       作用:接收后端拦截器报错,在这里处理
 */
axios.interceptors.response.use(result=>{
  console.log(result.data+"jjjjjjj");
  let data = result.data;
  if(!data.success && data.message==="noLogin"){
    localStorage.removeItem('token');
    localStorage.removeItem('logininfo');
    router.push({ path: '/login' });
  }
  return result;
},error => {
  Promise.reject(error);
})

路由拦截

http://localhost:8081/#/
根首页并没有触发axios请求,是直接访问直接打开的,也需要拦截
登录和注册页面要放行

router.beforeEach((to, from, next) => {
  if (to.path == '/login' || to.path == '/shopRegister') {
    localStorage.removeItem('token');
    localStorage.removeItem('logininfo');
    next()//需要放行
  }else{
    let user = JSON.parse(localStorage.getItem('logininfo'));
    if (!user) {//如果没有值,定位到登录页
      next({ path: '/login' })
    } else {//如果有值,正常访问页面
      next()
    }
  }
})

其他

管理员登录

前提:login.vue中集成axios和vue
整体思路  先不要考虑登录拦截,先把登录做好
1)后台登录接口
2)前台登录实现并且保存loginInfo和token到localStorage,登录成功跳转首页,并展示用户名
3)前台通过axios的前置拦截器携带token到后台
4)后台做token的登录拦截器,如果没有回报错给前台
5)前台通过axios后置拦截器对后台登录拦截错误进行跳转到登录页面
6)前台也要做拦截-有的地址是不需要访问后台

后端接口
LoginDto 	
LoginController	/login/acount	loginAccount
LoginInfoServiceImpl 	loginAcount
xml

后台登录
Login.vue	/login/account

axios携带token到后台 main.js
为了后端校验是否已经登录,只要用axios的请求都要携带token

后端登录拦截
写拦截器	配置拦截器
前台对后台拦截结果跳转登录页面 main.js			后台拦截器实现后端已经退出登录的跳转登录页面
前端拦截器-页面没有和后台进行数据交互		 	  登录和注册在没有登录情况下也能访问

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

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

相关文章

Learning to cartoonize using white-box cartoon representations

论文笔记--漫画生成--White-box Cartoon Representations - 知乎论文 Learning to Cartoonize Using White-box Cartoon Representations 源码https://github.com/SystemErrorWang/White-box-Cartoonization效果算法概述这篇论文是将图像风格转成漫画风格&#xff0c;作者认为可…

MySQL索引index

目录 1.索引的概念 2.索引的优缺点 3.索引的数据结构 4.索引结构 1.二叉树&#xff1a; ​编辑2.红黑树&#xff1a; 3.BTree&#xff08;B-Tree&#xff09;&#xff1a; ​编辑4.传统BTree&#xff1a; ​编辑5.MySQL中的BTree&#xff1a; ​编辑6.Hash&#xf…

浅谈电子设备之电磁屏蔽箱设计要点

屏蔽箱又称隔离箱、屏蔽盒和电磁屏蔽箱&#xff0c;可以对传导和辐射进行处理&#xff0c;为无线通讯产品生产制造提供高效的隔离测试环境&#xff0c;测试内容包含耦合测试、RF功能测试、电磁干扰测试和电磁兼容性测试。屏蔽箱多采用铝合金材料设计&#xff0c;屏蔽材料阻隔能…

C语言模拟实现字符串处理函数

需要多一点点勇气&#xff0c;来面对变差的自己 大家好&#xff0c;我是纪宁。 这篇文章为大家带来的是5大字符串处理函数的模拟实现。 文章目录 1.strlen函数的模拟实现 2.strcpy函数的模拟实现 3.strcmp函数的模拟实现 4.strcat函数的模拟实现 5.strstr函数的模拟实现…

相机标定-基础(一)

1. 何为相机标定? 当相机拍摄照片时,我们看到的图像通常与我们实际看到的不完全相同。这是由相机镜头引起的,而且发生的频率比我们想象的要高。 这种图像的改变就是我们所说的畸变。一般来说,畸变是指直线在图像中出现弯曲或弯曲。 这种畸变我们可以通过相机标定来进行解…

反常积分题目

目录 题目1&#xff1a; 题目2&#xff1a; 题目3&#xff1a;​ 题目4&#xff1a; 题目5&#xff1a; 题目6&#xff1a; 题目7&#xff1a; 我们首先引入反常积分的定义&#xff1a; CSDNhttps://mp.csdn.net/mp_blog/creation/editor/131676865 题目1&#xff1a; 题目2…

STM32 ws2812b多屏驱动程序

文章目录 前言一、ws2812b的数据传输以及屏幕的组合二、代码ws2812screen.c文件ws2812screen.h文件主函数 前言 在上篇文章中使用了stm32的dmatim的方式点亮了ws2812b的灯 但是我的需求不仅仅是点亮他&#xff0c;我需要他像屏幕一样显示某一些东西&#xff0c;ws2812显示有一…

深入学习 Redis - 全局命令、过期策略如何实现、高效定时器原理

目录 Redis 全局命令 get 和 set keys keys 使用注意事项 exists exists 使用注意事项 del del 使用注意事项 expire 【面试经典】redis 中 key 的过期策略是怎么实现的&#xff1f; 定时器实现原理&#xff08;非 Redis 实现&#xff0c;拓展&#xff09; 1.基于 …

21 - 队列 - 循环队列——队列的顺序表示和实现

前面我们学习数组队列,链式队列,我们今天来学习循环队列。 队列的定义 队列(Queue)也是一种线性表, 但是它仅能在一端进行插入,而另一端进行删除的操作 ,插入的一端称为 队尾rear,删除的一端称为 队头front 。 向一个栈插入新元素又称作进队或入队, 从一个栈删除元素…

从实体按键看 Android 车载的自定义事件机制

在汽车数字化、智能化变革的进程中&#xff0c;越来越多的车机设计或部分、或全部地舍弃了实体按键&#xff0c;进而把车主操作的入口转移到了车机 UI 以及语音助手。 但统一、高效的零层级 UI 颇为困难&#xff0c;语音的准确率、覆盖率亦不够完善&#xff0c;那么在当下的阶段…

【数据结构】文件的归并排序

目录 1、归并排序引申出的问题 2、磁盘与文件的关系---包含与被包含的关系 3、思路&#xff1a; 4、代码实现 1、归并排序引申出的问题 归并排序是最常用的外排序的方法&#xff08;但归并排序既可进行内部排序也可进行外部排序&#xff09;&#xff0c;外排序就是在磁盘中…

LLaMA(Open and Efficient Foundation Language Models )论文解读(二)

此篇博客主题:LLAMA模型数据、训练时长、功耗及碳排放量 LLaMA: Open and Efficient Foundation Language Models paper https://arxiv.org/pdf/2302.13971v1.pdf 1 训练样本 Overall, our entire training dataset contains roughly 1.4T tokens after tokenization. For mo…

2023年Q2京东厨卫大电市场分析报告(京东运营数据分析)

随着新产品推广和消费需求升级&#xff0c;今年Q2&#xff0c;京东厨卫大电市场的销售额突破百亿&#xff0c;从同环比来看均呈增长趋势。百亿市场中&#xff0c;油烟机、电热水器、燃气热水器这三大品类占据较大份额&#xff0c;这一期&#xff0c;我们重点来看一下京东厨卫大…

怎么用Postman脚本中发送请求

Postman的Collection(集合)/Folder(集合的子文件夹)/Request(请求)都有Pre-request script和Tests两个脚本区域, 分别可以在发送请求前和请求后使用脚本(基于Javascript实现各种操作) 在遇到有依赖的接口时,比如需要登录或者需要从前一个接口的结果中获取参数时,我们往往需要在…

vscode 调试(linux )

一、配置程序运行依赖 1, 打开配置文件&#xff08;launch.json) (1) 从工具栏打开Run–>Add Configuration… &#xff08;2&#xff09;查看配置文件&#xff08;launch.json&#xff09; 2&#xff0c;添加配置 (1) 配置可执行程序&#xff08;program&#xff09;…

ceph存储的应用

ceph存储的应用 一&#xff1a;创建 CephFS 文件系统 MDS 接口1.服务端操作1&#xff09;在管理节点创建 mds 服务2&#xff09;查看各个节点的 mds 服务3&#xff09;创建存储池&#xff0c;启用 ceph 文件系统4&#xff09;查看mds状态&#xff0c;一个up&#xff0c;其余两个…

reggie优化01-缓存短信验证码和菜品数据

1、缓存短信验证码 1.1 Redis配置类RedisConfig 在config包下&#xff0c;创建Redis配置类RedisConfig&#xff1a; 纳入Git管理&#xff1a; package com.itheima.reggie.config;import org.springframework.cache.annotation.CachingConfigurerSupport; import org.sprin…

接口自动化测试框架unittest和pytest差异比较

前言 说到 Python 的单元测试框架&#xff0c;想必接触过 Python 的朋友脑袋里第一个想到的就是unittest。 的确&#xff0c;作为 Python 的标准库&#xff0c;它很优秀&#xff0c;并被广泛用于各个项目。但你知道吗&#xff1f;其实在 Python 众多项目中&#xff0c;主流的单…

【ROS机械臂入门教程】

首先声明一下&#xff0c;此项目是参考B站哈萨克斯坦UP的【ROS机械臂入门教程】&#xff0c;前期以复现【机械臂视觉抓取从理论到实战】 此内容为他研究生生涯的阶段性成果展示和技术分享&#xff0c;所有数据和代码均开源。所以鹏鹏我又特此来复现一下&#xff0c;我采用的硬件…

Redis源码篇 - Reactor设计模式 和 Redis Reactor设计模式

Reactor &#xff1a;反应器模式或者应答者模式&#xff0c;它是一种基于事件驱动的设计模式。拥有一个或者多个输入源&#xff0c;通过反应器分发给多个worker线程处理&#xff0c;实现并发场景下事件处理。 此图网上找的&#xff0c;画的很好&#xff1a;