springboot项目:瑞吉外卖 前后端 代码、思路 详细分析 part6

news2025/1/20 1:01:48

part1
part2
part3
part4
part5
part6(本页)

7. 菜品展示、购物车、下单 功能开发

7.1 导入用户地址簿相关功能代码
7.2 菜品展示
7.3 购物车
7.4 下单

7.1 导入用户地址簿相关功能代码

7.1.1 整体分析

  1. 需求分析
    在这里插入图片描述
  2. 数据模型在这里插入图片描述
  3. 需要开发的模块:新增收获地址、设置默认地址

7.1.2 前端代码分析

  1. 把addressList(vue 中的data模块)中的数据展示到页面上代码
<div class="divContent">
    <div class="divItem" v-for="(item,index) in addressList" :key="index" @click.capture="itemClick(item)">
        <div class="divAddress">
            <span :class="{spanCompany:item.label === '公司',spanHome:item.label === '',spanSchool:item.label === '学校'}">{{item.label}}</span>
            <!--把addressList中的数据循环出来-->
            {{item.detail}}
        </div>
        <div class="divUserPhone">
            <span>{{item.consignee}}</span>
            <span>{{item.sex === '0' ? '女士' : '先生'}}</span>
            <span>{{item.phone}}</span>
        </div>
        <!--
        @click.stop.prevent 是 Vue.js 中的指令,用于阻止事件的默认行为和事件冒泡。
        当该指令绑定在一个元素上时,当该元素被点击时,
        会阻止事件的默认行为(如链接跳转)和事件冒泡(即不会向父元素传递事件)。
        toAddressEditPage,点击后跳转到对应的页面,编辑,后面的添加也一样
        -->
        <img src="./../images/edit.png" @click.stop.prevent="toAddressEditPage(item)"/>
        <div class="divSplit"></div>
        <div class="divDefault" >
            <!--就是默认地址选中了没有-->
            <img src="./../images/checked_true.png" v-if="item.isDefault === 1">
            <img src="./../images/checked_false.png" @click.stop.prevent="setDefaultAddress(item)" v-else>设为默认地址
        </div>
    </div>
</div>
<!--页面底部显示的-->
<div class="divBottom" @click="toAddressCreatePage">+ 添加收货地址</div>
</div>

页面效果如下:
在这里插入图片描述
点击添加收获地址之后触发函数发送跳转,类似的编辑也是这样

toAddressCreatePage(){
    window.requestAnimationFrame(()=>{
        window.location.href= '/front/page/address-edit.html'
    })
},
  1. 编辑 address-edit.html
    页面效果
    在这里插入图片描述
    钩子函数created() 执行之后,执行了initData(),这里面有一个根据id查询地址的功能,从数据库查询到之后 ,显示到页面上addressFindOneApi;
async initData(){
     /*
         window.location.search 是 JavaScript 中的一个属性,
         它表示当前页面的 URL 中的查询字符串部分。
         例如,如果当前页面的 URL 是 "https://www.example.com/search?q=javascript&page=2",
         那么 window.location.search 的值就是 "?q=javascript&page=2"。
     * */
     const params = parseUrl(window.location.search)
     this.id = params.id
     if(params.id){
         this.title = '编辑收货地址'
         const res = await addressFindOneApi(params.id)
         if(res.code === 1){
             this.form = res.data
         }else{
             this.$notify({ type:'warning', message:res.msg});
         }
     }
 }

增加和删除的前端代码,还是很好理解的.

async saveAddress(){
  const form = this.form
  if(!form.consignee){
      this.$notify({ type:'warning', message:'请输入联系人'});
      return 
  }
  if(!form.phone){
      this.$notify({ type:'warning', message:'请输入手机号'});
      return 
  }
  if(!form.detail){
      this.$notify({ type:'warning', message:'请输入收货地址'});
      return 
  }
  const reg = /^1[3|4|5|7|8][0-9]{9}$/
  if(!reg.test(form.phone)){
      this.$notify({ type:'warning', message:'手机号码不合法'});
      return  
  }
  let res= {}
  if(this.id){
      res = await updateAddressApi(this.form)
  }else{
      res = await addAddressApi(this.form)
  }
  
  if(res.code === 1){
      window.requestAnimationFrame(()=>{
          window.location.replace('/front/page/address.html')
      })
  }else{
      this.$notify({ type:'warning', message:res.msg});
  }
},
deleteAddress(){
  this.$dialog.confirm({
      title: '确认删除',
      message: '确认要删除当前地址吗?',
  })
  .then( async () => {
      const res = await deleteAddressApi({ids:this.id })
      if(res.code === 1){
          window.requestAnimationFrame(()=>{
              window.location.replace('/front/page/address.html')
          })
      }else{
          this.$notify({ type:'warning', message:res.msg});
      }
  })
  .catch(() => {
  });
},

其中三个关键的axios请求:

//新增地址
function  addAddressApi(data){
    return $axios({
        'url': '/addressBook',
        'method': 'post',
        data
      })
}

//修改地址
function  updateAddressApi(data){
    return $axios({
        'url': '/addressBook',
        'method': 'put',
        data
      })
}

//删除地址
function deleteAddressApi(params) {
    return $axios({
        'url': '/addressBook',
        'method': 'delete',
        params
    })
}

7.1.3 后端代码分析

这部分比较简单,就是接收前端的数据,从前面看到,提交的都是大多json格式的数据,需要使用@RequestBody 获取。

  1. 页面根据id查询地址,显示出来。

前端请求是这样的

function addressFindOneApi(id) {
  return $axios({
    'url': `/addressBook/${id}`,
    'method': 'get',
  })
}

controller:

@GetMapping("/{id}")
public RetObj get(@PathVariable Long id) {
    AddressBook addressBook = addressBookService.getById(id);
    if (addressBook != null) {
        return RetObj.success(addressBook);
    } else {
        return RetObj.error("没有找到该对象");
    }
}
  1. 把所有地址显示到页面上
    前端:
async initData(){
    const res = await addressListApi()
    if(res.code === 1){
        this.addressList = res.data
    }else{
        this.$message.error(res.msg)
    }
},

后端:

 @GetMapping("/list")
 public RetObj<List> getAddressById(){
     Long userId = BaseContext.getThreadLocal();
     LambdaQueryWrapper<AddressBook> lambdaQueryWrapper = new LambdaQueryWrapper<>();
     lambdaQueryWrapper.eq(AddressBook::getUserId,userId)
             .orderByDesc(AddressBook::getUpdateTime);
     //SQL:select * from address_book where user_id = ? order by update_time desc
     List<AddressBook> addressBooks = addressBookService.list(lambdaQueryWrapper);
     return RetObj.success(addressBooks);
 }
  1. 新增
@PostMapping("")
public RetObj<String> addAddressController(@RequestBody AddressBook addressBook){
    //注意需要 将当前操作插入的用户注入,使用ThreadLocal
    addressBook.setUserId(BaseContext.getThreadLocal());
    boolean res = addressBookService.save(addressBook);
    if (res){
        return RetObj.success("成功新增地址");
    }else {
        return RetObj.error("地址添加失败!");
    }
}
  1. 设置默认地址
    前端,点击按钮,设置为默认,注意参数是json
async setDefaultAddress(item){
   if(item.id){
       const res = await setDefaultAddressApi({id:item.id})
       if(res.code === 1){
       this.initData()
       }else{
           this.$message.error(res.msg)
       }
   }
},

后端,根据id 设置默认地址,有很多注意的点,比如先得把表中所有地址设置为非默认,否则就有两个默认地址

/**
 * 设置默认的地址,需要把其他地址都先设置为非默认的!!(而且是当前user对应的地址)
 * @param addressBook
 * @return
 */
@PutMapping("/default")
public RetObj setDefaultAddress(@RequestBody AddressBook addressBook){
    log.info("addressBook:{}", addressBook);
    LambdaUpdateWrapper<AddressBook> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
    lambdaUpdateWrapper.eq(addressBook != null,AddressBook::getUserId
                    ,BaseContext.getThreadLocal())
            .set(AddressBook::getIsDefault,0);
    boolean res = addressBookService.update(lambdaUpdateWrapper);
    
    //上面先把所有的地址都设置为非默认
    //现在把指定的地址设置为默认
    addressBookService.updateById(addressBook);//会根据非null的字段进行更新!!
    return RetObj.success("成功设置为默认地址");
}
  1. 查询默认地址(一开始把所有地址显示出来之后,要查一下哪个地址是默认的,然后在页面显示,对应地址是默认地址)
/**
 * 查询默认地址,因为可能没有查到地址等情况,所以返回值需要判别一下
 * SQL:select * from address_book where user_id = ? and is_default = 1
 */
@GetMapping("default")
public RetObj<AddressBook> getDefault() {
    LambdaQueryWrapper<AddressBook> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(AddressBook::getUserId, BaseContext.getThreadLocal());
    queryWrapper.eq(AddressBook::getIsDefault, 1);

    //SQL:select * from address_book where user_id = ? and is_default = 1
    AddressBook addressBook = addressBookService.getOne(queryWrapper);

    if (null == addressBook) {
        return RetObj.error("没有找到该对象");
    } else {
        return RetObj.success(addressBook);
    }
}

7.2 移动端菜品展示

7.2.1 整体分析

  1. 需求分析
    在这里插入图片描述
    2.交互过程分析
    在这里插入图片描述
    分类数据展示到左边的菜单栏上,在之前的分类管理中,其实已经写好了 (就是之前,选中菜品的分类的时候,下拉框就需要显示有哪些分类,正好现在直接复用) ;
    一打开页面也要默认展示第一个分类下的菜品,不要点击了才展示出来(比如现在的湘菜)。类似的,代码之前也写的差不多了,之前在新增分类中,选择该分类下的菜品(下拉框中)。但是之前返回值是List<Dish>,现在不仅仅需要Dish,还需要Dish对应的口味信息,后端那边要对这个进行改造(使用DishDto)!
    在这里插入图片描述

7.2.2 前端代码分析

  1. 显示分类,循环读取categoryList的内容。如果点击了对应的套餐,显示套餐下的菜品,展示到右边
<div class="divType">
  <ul>
    <li v-for="(item,index) in categoryList" :key="index" @click="categoryClick(index,item.id,item.type)" :class="{active:activeType === index}">{{item.name}}</li>
  </ul>
</div>

显示出套餐下的菜品,调用getDishList()

//分类点击
categoryClick(index,id,type){
  this.activeType = index
  this.categoryId = id
  if(type === 1){//菜品
    this.getDishList()
  }else{
    this.getSetmealData()
  }
},

getDishList()具体方法如下,获取了后端分装好的data数据,保存在dishList中进行数据的双向绑定。

//获取菜品数据
async getDishList(){
  if(!this.categoryId){
    return
  }
  const res = await dishListApi({categoryId:this.categoryId,status:1})
  if(res.code === 1){
    let dishList = res.data
    const cartData  = this.cartData
    if(dishList.length > 0 && cartData.length > 0){
      dishList.forEach(dish=>{ //循环为DishList插入cart值
        cartData.forEach(cart=>{
          if(dish.id === cart.dishId){
            dish.number = cart.number
          
      })
    }
    this.dishList = dishList
  }else{
    this.$notify({ type:'warning', message:res.msg});
  }
},

具体发送的axios请求。

function dishListApi(data) {
    return $axios({
        'url': '/dish/list',
        'method': 'get',
        params:{...data}
    })
}
  1. 接着上面,点击对应的菜品分类后(数据已经存到dishList中),现在要循环显示出该分类下的菜品,并且能够对菜品进行操作,包括显示月销,选择规格等。同时,如果点击进去,会显示具体的细节,对应@click="dishDetails(item)
<div class="divMenu">
   <div>
     <div class="divItem" v-for="(item,index) in dishList" :key="index" @click="dishDetails(item)">
       <el-image :src="imgPathConvert(item.image)" >
         <div slot="error" class="image-slot">
           <img src="./images/noImg.png"/>
         </div>
       </el-image>
       <div>
         <div class="divName">{{item.name}}</div>
         <div class="divDesc">{{item.description}}</div>
         <div class="divDesc">{{'月销' + (item.saleNum ? item.saleNum : 0)  }}</div>
         <div class="divBottom"><span></span><span>{{item.price/100}}</span></div>
         <div class="divNum">
           <div class="divSubtract" v-if="item.number > 0">
             <img src="./images/subtract.png" @click.prevent.stop="subtractCart(item)"/>
           </div>
           <div class="divDishNum">{{item.number}}</div>
           <div class="divTypes" v-if="item.flavors && item.flavors.length > 0 && !item.number " @click.prevent.stop="chooseFlavorClick(item)">选择规格</div>
           <div class="divAdd" v-else>
             <img src="./images/add.png" @click.prevent.stop="addCart(item)"/>
           </div>
         </div>
       </div>
     </div>
   </div>

进行了后端交互,调用setMealDishDetailsApi()

async dishDetails(item){
    //先清除对象数据,如果不行的话dialog使用v-if
    this.detailsDialog.item = {}
    this.setMealDialog.item = {}
    if(Array.isArray(item.flavors)){
      this.detailsDialog.item = item
      this.detailsDialog.show = true
    }else{
      //显示套餐的数据
      const res = await setMealDishDetailsApi(item.id)
      if(res.code === 1){
        this.setMealDialog.item = {...item,list:res.data}
        this.setMealDialog.show = true
      }else{
        this.$notify({ type:'warning', message:res.msg});
      }
    }
  }

axios交互:

//获取套餐的全部菜品
function setMealDishDetailsApi(id) {
    return $axios({
        'url': `/setmeal/dish/${id}`,
        'method': 'get',
    })
}
  1. 初始化数据,一进来不点击任何东西应该也要显示出对应第一个分类的菜品.
    注意Promise.all必须是购物车和分类都加载完毕了,才把数据初始化到界面上。只有这里面所有的请求都完成了,才会成功执行接下来的代码,才会把categoryList等等数据提交成功。所以没写购物车逻辑的时候,就算分类写好了也显示不出来
mounted(){
  this.initData()
},
methods:{
  //初始化数据
  initData(){
    Promise.all([categoryListApi(),cartListApi({})]).then(res=>{
      //获取分类数据
      if(res[0].code === 1){
        this.categoryList = res[0].data
        if(Array.isArray(res[0].data) && res[0].data.length > 0){
          this.categoryId = res[0].data[0].id
          if(res[0].data[0].type === 1){
            this.getDishList()
          }else{
            this.getSetmealData()
          }
        }
      }else{
        this.$notify({ type:'warning', message:res[0].msg});
      }
      //获取菜品数据
      if(res[1].code === 1){
      this.cartData = res[1].data
      }else
        this.$notify({ type:'warning', message:res[1].msg});
      }
    })
  },
  1. 上面的代码完成所有分类和套餐的名字的展示,没有把具体的套餐和菜品的信息显示出来,上面代码中调用了getDishList()方法,具体如下(对应type === 1)
//获取菜品数据
async getDishList(){
  if(!this.categoryId){
    return
  }
  const res = await dishListApi({categoryId:this.categoryId,status:1})
  if(res.code === 1){
    let dishList = res.data
    const cartData  = this.cartData
    if(dishList.length > 0 && cartData.length > 0){
      dishList.forEach(dish=>{ //为每一条dishList插入cart值
        cartData.forEach(cart=>{
          if(dish.id === cart.dishId){
            dish.number = cart.number
          }
        })
      })
    }
    this.dishList = dishList
  }else{
    this.$notify({ type:'warning', message:res.msg});
  }
},

对应api:

function dishListApi(data) {
    return $axios({
        'url': '/backend/page/food/list/getDishByCategoryId.do',
        'method': 'get',
        params:{...data}
    })
}
  1. 现在type不是1,那就是要展示套餐的具体信息:getSetmealData(),和上面很类似
//获取套餐数据setmealId
async getSetmealData(){
  if(!this.categoryId){
    return
  }
  const res = await setmealListApi({categoryId:this.categoryId,status:1})
  if(res.code === 1){
      let dishList = res.data
      const cartData  = this.cartData
      if(dishList.length > 0 && cartData.length > 0){
        dishList.forEach(dish=>{
          cartData.forEach(cart=>{
            if(dish.id === cart.setmealId){
              dish.number = cart.number
            }
          })
        })
      }
      this.dishList = dishList
  }else{
    this.$notify({ type:'warning', message:res.msg});
  }
},

setmealListApi如下

function setmealListApi(data) {
    return $axios({
        'url': '/setmeal/list',
        'method': 'get',
        params:{...data}
    })
}
  1. 以上完成的是具体菜品或者套餐在页面右边的展示,现在如果点击了 具体菜品或者套餐(如:麻辣兔头、二逼套餐A计划),要显示该菜品的具体信息。
async dishDetails(item){
   //先清除对象数据,如果不行的话dialog使用v-if
   this.detailsDialog.item = {}
   this.setMealDialog.item = {}
   if(Array.isArray(item.flavors)){
     this.detailsDialog.item = item
     this.detailsDialog.show = true
   }else{
     //显示套餐的数据
     const res = await setMealDishDetailsApi(item.id)
     if(res.code === 1){
       this.setMealDialog.item = {...item,list:res.data}
       this.setMealDialog.show = true
     }else{
       this.$notify({ type:'warning', message:res.msg});
     }
   }
 }

7.2.3 后端分析

  1. 查出所有套餐、菜品的分类,显示出来(左边的栏目)。之前写好了,直接复用,注意加上category.getType() != null。
@GetMapping("/food/list/getCategory.do")
public RetObj getCategoryList(Category category){
    LambdaQueryWrapper<Category> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    //category.getType() != null 加上了这个,因为在移动端front中会直接查询,不带type,把菜品和套餐全部查出来
    lambdaQueryWrapper.eq(category.getType() != null,Category::getType,category.getType())
            .orderByAsc(Category::getSort)
            .orderByDesc(Category::getUpdateTime);
    List<Category> categoryList = categoryService.list(lambdaQueryWrapper);
    //log.info("查询出菜品:{}",categoryList);
    return RetObj.success(categoryList);
}
  1. 点击左边的菜品分类后,查出该菜品分类对应有哪些菜。如川菜有:麻辣兔头等。
@GetMapping("list/getDishByCategoryId.do")
public RetObj getDishByCategoryId(Dish dish){
    LambdaQueryWrapper<Dish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.eq(dish != null,Dish::getCategoryId,dish.getCategoryId())
            .orderByDesc(Dish::getSort);

    List<Dish> dishList = dishService.list(lambdaQueryWrapper);
    return RetObj.success(dishList);
}
  1. 点击套餐分类后弹出该套餐分类下有哪些套餐。(setmeal套餐表中有category_id字段,就根据这个字段进行查询即可(左边栏目中点击的就传category_id,如点击二逼套餐,传递二逼套餐对应的category_id,查出二逼套餐A计划),后端开发需要注意state==1,没有被禁用的才能查出。)
    如儿童套餐里面有 儿童套餐A计划、儿童套餐B计划等。每个具体套餐又含有对应的菜品。如二逼套餐中有二逼套餐A计划,二逼套餐A计划中有二逼(二逼这个具体的菜又属于二逼菜这个菜品分类)。 如下图添加套餐管理时就是这样添加的。
    在这里插入图片描述
/**
 * 查出套餐分类下有哪些套餐:如二逼套餐中有二逼套餐A计划、B计划等(二逼套餐A计划中有二逼(二逼这个具体的菜又属于二逼菜这个菜品分类)
 * 注意,
 * @param  主要就是categoryID,还有state是1,没被禁用的查出
 * @return
 */
@GetMapping("list/getSetMealByCategoryId.do")
public RetObj getSetMealByCategoryId(Long categoryId,Integer status){
    LambdaQueryWrapper<Setmeal> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.eq(Setmeal::getCategoryId,categoryId)
            .eq(Setmeal::getStatus,status)
            .orderByDesc(Setmeal::getUpdateTime);

    List<Setmeal> setmealList = setmealService.list(lambdaQueryWrapper);
    return RetObj.success(setmealList);
}

做完后效果如下
在这里插入图片描述
4. 以上是完成左边分类栏目的显示,以及点击分类栏目在右边示出该分类具体的项目,现在点击具体的项目,要显示出详情。比如点击上面的二逼套餐A计划,要能看到这个套餐中有哪些菜品。

  • 这部分一定要搞清楚表的结构! 举例:二逼套餐点击后有二逼套餐A计划,现在就是想点击二逼套餐A计划,看有哪些菜。
    1. 二逼套餐A计划 表中有id是自己的id,有categoryId是二逼套餐的Id。
    1. 在setmeal_dish表中有 setmeal_id、dish_id、id 现在这些id都很清楚明白了
    1. 用二逼套餐A计划自己的Id(主键)去查setmeal_dish中的setmeal_id(副键)
/**
* 获取套餐的全部菜品
* @param id 注意这个id是二逼套餐A计划套餐的id,查表setmeal_dish中的setmeal_id这个字段对应起来
* @return
*/
@GetMapping("setmeal/setMealDishDetails.do/{id}")
public RetObj setMealDishDetails(@PathVariable Long id){
   LambdaQueryWrapper<SetmealDish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();
   dishLambdaQueryWrapper.eq(SetmealDish::getSetmealId,id)
           .orderByDesc(SetmealDish::getUpdateTime);
   List<SetmealDish> setmealDishes = setmealDishService.list(dishLambdaQueryWrapper);
   return RetObj.success(setmealDishes);
}

持续更新中…

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

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

相关文章

【Mysql】初识 Mysql

文章目录 【Mysql】初识 Mysql数据库初识主流关系型数据库理解数据库mysql基本操作连接服务器理解服务器&#xff0c;数据库&#xff0c;表关系小案例数据逻辑存储 mysql架构sql分类存储引擎 【Mysql】初识 Mysql 数据库初识 数据库概念 数据库是“按照数据结构来组织、存储和…

v-model使用及原理

关于v-model&#xff0c;vue2与vue3用法不一致&#xff0c;本文学习采用了vue3官网文档。与vue2区别写在本文末尾。一、为什么使用v-model&#xff1f; v-model指令可以在表单input、textarea以及select元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素…

马哈鱼SQLFLow直接数据流介绍

直接数据流介绍 本文将介绍一些生成直接数据流的SQL元素&#xff0c;这些元素是生产数据流的主要原型。 1、Select 示例语句&#xff1a; SELECT a.empName "eName" FROM scott.emp a Where sal > 1000目标列“eName”的数据来自scott.emp.empName列&#xff…

【安卓源码】安卓app应用进程启动原理

目录 0. 应用app 调用 startActivity 1. AMS 调用 startActivity 方法 2. zygote socket 通信,通知 zygote 创建应用进程 1-1&#xff09; 去fork 一个子进程 Zygote.forkAndSpecialize 1-2&#xff09;执行子进程方法 handleChildProc 1-3&#xff09;执行父进程方法 ha…

用户管理 ---MySQL总结(七)

用户管理 对于MySQL的用户也是需要进行管理&#xff0c;这里的东西类似与Linux的多用户管理&#xff0c;基本相同 用户属性 MySQL的用户都是储存在数据库mysql的user之中。 这里使用desc table_name;查看user表的属性。 desc user;//下面的就是输出的结果host:登陆限制 user…

chatgpt赋能Python-python_ai_app

用Python编写AI应用程序 Python是目前最受欢迎的编程语言之一&#xff0c;被广泛用于各种应用程序的开发。其中&#xff0c;人工智能&#xff08;AI&#xff09;应用程序成为Python编程人员最感兴趣和热门的领域之一。这篇文章将重点介绍用Python编写AI应用程序的好处&#xf…

从零开始Vue3+Element Plus后台管理系统(16)——组合式函数hook二次封装el-table

终于写到组合式函数了&#xff0c;它类似vue2的mixin&#xff0c;但优于mixin&#xff0c;用好了可以事半功倍。 在 Vue 应用的概念中&#xff0c;“组合式函数”(Composables) 是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数。 官方文档&#xff1a;https://cn.vu…

chatgpt赋能Python-python_chan

Python的Channel模块&#xff1a;优化你的并发控制 Python是一门优秀的编程语言&#xff0c;在众多优秀的模块中&#xff0c;Channel模块是一个备受喜爱的模块。它是Python并发控制的建议之一&#xff0c;可以被用来在多个协程之间传递和传输消息。这个模块不仅仅是Python 3.5…

chatgpt赋能Python-python_cal

Python编程的神器——Cal 随着人工智能、机器学习等技术的发展&#xff0c;Python语言成为了最热门的编程语言之一。Python可以帮助程序员快速实现自己的想法&#xff0c;让程序的编写变得更加简单和容易。在Python中&#xff0c;有许多高效好用的工具和库&#xff0c;而其中最…

chatgpt赋能Python-python_chi2

Python中的Chi-Squared测试&#xff1a;一种用于统计分析的重要方法 数据分析是当今商业和科学中最重要的工具之一&#xff0c;它可以帮助人们了解他们的业务和科学领域。其中数据分析的技术以Python为代表的编程语言越来越受到欢迎&#xff0c;这些方法可以用于分类、回归、聚…

《数据可视化》课程期末项目_地理交通数据可视化

2022年上海疫情爆发期间交通数据可视化分析 《数据可视化》课程期末项目报告-选题&#xff1a;地理数据可视化 GitHub源码地址(如果有用点个 star 吧~谢谢&#xff01;) 文章目录 1.0 项目简介2.0 数据简介2.1 航线数据2.2 公交路线数据2.1 项目流程 3.0 数据处理3.1 航线数据…

【面试题】如何实现vue虚拟列表,纵享丝滑

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 前言 最近在工作中遇到了一个列表的需求&#xff0c;因为做的是C端&#xff0c;所以对性能…

什么是接口测试?接口测试流程有哪些?我来告诉你

目录 首先&#xff0c;什么是接口呢&#xff1f; 一、常见接口&#xff1a; 二、前端和后端&#xff1a; 三、什么是接口测试&#xff1a; 四、接口组成 五、为什么要做接口测试&#xff1a; 六、接口测试怎么测&#xff1a; 七、用什么工具测 八. 接口测试持续集成 九…

chatgpt赋能Python-python_canbus

Python Canbus&#xff1a;如何使用Python编程语言控制Canbus&#xff1f; 介绍 Canbus被广泛地应用于现代汽车中&#xff0c;是一个用于通讯的协议&#xff0c;允许汽车的各个部分进行通信。为了控制Canbus&#xff0c;很多工程师都使用Python编程语言&#xff0c;因为它简单…

爬虫练习-12306自动购票升级版

文章目录 前言代码更新 前言 hello兄弟们&#xff0c;偷懒归来了。别问为啥这么久没更&#xff0c;问就是失踪了 最近一直在学习Django以及爬虫进阶之类的知识&#xff0c;加上快期末了&#xff0c;一直没有想起来自己还有一个账号没有更新&#xff0c;sorry啦 言归正传&…

SpringBoot - Jackson详解

写在前面 JSON 是目前主流的前后端数据传输方式。在 Spring Boot 项目中&#xff0c;只要添加了 WEB依赖&#xff08;spring-boot-starter-web&#xff09;&#xff0c;就可以很方便地实现 JSON 转换。WEB 依赖默认加入了 jackson-databind 作为 JSON 处理器&#xff0c;我们不…

算法小试炼(差不多相当于重新过一遍ACWING,为了夏令营做点准备)

1.最长不重复子串 这个题目的具体意思就不用我说了&#xff0c;我这里给出两种算法 1&#xff09;暴力搜索 只要机器够快&#xff0c;没有什么是暴搜解决不了的^ ^&#xff08;开玩笑 很简单&#xff0c;我们只需要遍历长度&#xff0c;跟左边界就好了&#xff0c;这个应该没…

测试必知必会的Mock数据方法

Mock数据的含义 那么Mock数据是什么意思呢 首先Mock这个英文单词有模拟的意思&#xff0c;模拟数据通俗的理解就是构造假数据&#xff0c;即Mock数据就是通过构造假数据来达到测试的目的&#xff0c;它广泛运用于功能测试、接口测试、单元测试 在功能测试中&#xff0c;可以…

离散数学 | 图论 | 欧拉图 | 哈密顿图 | 割点 | 桥(欧拉图和哈密顿图有没有割点和桥?)

本文主要解决以下几个问题&#xff1a; 1.欧拉图能不能有割点&#xff0c;能不能有桥&#xff1f; 2.哈密顿图能不能有割点&#xff0c;能不能有桥&#xff1f; 首先我们要明白几个定义 割点的定义就是在一个图G中&#xff0c;它本来是连通的&#xff0c;去掉一个点v以后这个…

【firewalld防火墙】

目录 一、firewalld概述二、firewalld 与 iptables 的区别1、firewalld 区域的概念 三、firewalld防火墙默认的9个区域四、Firewalld 网络区域1、区域介绍2、firewalld数据处理流程 五、firewalld防火墙的配置方法1、使用firewall-cmd 命令行工具。2、使用firewall-config 图形…