苍穹外卖学习记录(二)

本节,主要是学习业务逻辑,我们以菜品管理为例:
在实现这部分前,我们要完成Mybatis的配置,即指定映射的mapper.xml文件路径以及对应的实体类,这部分配置是在application.yml文件中实现的。

mybatis:#mapper配置文件mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.sky.entityconfiguration:#开启驼峰命名map-underscore-to-camel-case: true

这样配置后,在mapper.xml中如果不指定的话,接收的实体类就是com.sky.entity中的,也就不需要每次都给指定接收类型了。
如下,我们没有指定传入的数据类型,因为在mapper接口中指定了,就可以省略。

 <insert id="insert" useGeneratedKeys="true" keyProperty="id">insert into dish (name, category_id, price, image, description, create_time, update_time, create_user,update_user, status)values (#{name}, #{categoryId}, #{price}, #{image}, #{description}, #{createTime}, #{updateTime}, #{createUser},#{updateUser}, #{status})
</insert>

业务规则:

  • 菜品名称必须是唯一的
  • 菜品必须属于某个分类下,不能单独存在
  • 新增菜品时可以根据情况选择菜品的口味
  • 每个菜品必须对应一张图片

在这里插入图片描述
在这里插入图片描述

文件上传功能实现

我们需要用到的是阿里云对象存储服务:

阿里云OSS简介

​ 阿里云对象存储服务(Object Storage Service,简称OSS)为您提供基于网络的数据存取服务。使用OSS,您可以通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种非结构化数据文件。
阿里云OSS将数据文件以对象(object)的形式上传到存储空间(bucket)中。

在这里插入图片描述
您可以进行以下操作:

  • 创建一个或者多个存储空间,向每个存储空间中添加一个或多个文件。
  • 通过获取已上传文件的地址进行文件的分享和下载。
  • 通过修改存储空间或文件的属性或元信息来设置相应的访问权限。
  • 在阿里云管理控制台执行基本和高级OSS任务。
  • 使用阿里云开发工具包或直接在应用程序中进行RESTful API调用执行基本和高级OSS任务

阿里云OSS开通与使用

那么我们具体该如何使用呢?进入阿里云官网:https://www.aliyun.com/
在这里插入图片描述
我们点击开通,这个是需要付费的。
在这里插入图片描述

开通成功:

在这里插入图片描述

进入控制台后,我们创建一个存储空间,即创建Bucket,这个Bucket的名称是唯一的。

在这里插入图片描述

文件配置

首先是引入依赖:

<dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.15.1</version>
</dependency>

随后在application-dev.yml中进行配置,可以看到原本的配置为:

sky:alioss:endpoint: oss-cn-hangzhou.aliyuncs.comaccess-key-id: LTAI5tPeFLzsPPT8gG3LPW64access-key-secret: U6k1brOZ8gaOIXv3nXbulGTUzy6Pd7bucket-name: sky-take-out

我们把自己的写进去即可,这个是收费的,因此不要暴露

在这里插入图片描述

随后我们创建AccessKey,并把值复制后我们的配置文件中。

在这里插入图片描述

至于其他的两个参数:

  1. endpoint:你选择杭州就是oss-cn-hangzhou.aliyuncs.com
    你选择北京就是oss-cn-beijing.aliyuncs.com,可以理解吧
  2. bucket-name就是创建的名字(直接点击一下就可以复制啦)

随后在配置文件中读取:

spring:profiles:active: dev    #设置环境
sky:alioss:endpoint: ${sky.alioss.endpoint}access-key-id: ${sky.alioss.access-key-id}access-key-secret: ${sky.alioss.access-key-secret}bucket-name: ${sky.alioss.bucket-name}

sky-common模块的properties包中的 AliOssProperties 中,定义配置实体类:

/*** 阿里云配置实体类*/
@Component
/*请注意,为了使用@ConfigurationProperties注解,你需要在Spring Boot应用程序中启用配置绑定功能。
你可以通过在主应用程序类上添加@EnableConfigurationProperties注解来实现。*/
@ConfigurationProperties(prefix = "sky.alioss")
@Data
public class AliOssProperties {private String endpoint; //表示OSS服务的访问域名。private String accessKeyId; //表示访问OSS服务所需的Access Key ID。private String accessKeySecret; //表示访问OSS服务所需的Access Key Secret。private String bucketName; //表示要操作的存储桶名称。

生成OSS工具类对象
在sky-server模块的config包中进行配置,读取上面的配置信息,并返回一个操作对象。

/*** 配置类,用于创建AliOssUtil对象*/
@Configuration
@Slf4j
public class OssConfiguration {/*在这个配置类中,定义了一个名为aliOssUtil的@Bean方法,用于创建一个AliOssUtil对象。*/@Bean/*@ConditionalOnMissingBean注解表示当不存在名为aliOssUtil的bean时,才会创建该bean。这意味着如果已经有其他地方定义了名为aliOssUtil的bean,那么这个方法将不会执行。*/@ConditionalOnMissingBean //保证整个Spring容器中只有一个这个工具类/*在方法体中,通过依赖注入的方式获取AliOssProperties对象,并使用它的属性值来创建AliOssUtil对象。*/public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){log.info("开始创建阿里云文件上传工具类对象:{}",aliOssProperties);return new AliOssUtil(aliOssProperties.getEndpoint(),aliOssProperties.getAccessKeyId(),aliOssProperties.getAccessKeySecret(),aliOssProperties.getBucketName());}
}

其中,AliOssUtil类定义在sky-commonutils工具包中,改工具类将图像上传到阿里云OSS后会生成一个访问地址,这个地址就是可以直接访问。

/*** AliOssUtil类是一个包含文件上传功能的工具类。*/
@Data
@AllArgsConstructor
@Slf4j
public class AliOssUtil {private String endpoint;private String accessKeyId;private String accessKeySecret;private String bucketName;/*** 文件上传** @param bytes* @param objectName* @return*/public String upload(byte[] bytes, String objectName) {// 创建OSSClient实例。/*在upload方法中,首先创建了一个OSSClient实例,用于与OSS服务进行交互。*/OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);try {// 创建PutObject请求。/*然后,通过调用ossClient.putObject方法将文件上传到指定的存储桶中。*/ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));/*在上传过程中,通过捕获OSSException和ClientException来处理可能出现的异常情况,并输出相应的错误信息。*/} catch (OSSException oe) {System.out.println("Caught an OSSException, which means your request made it to OSS, "+ "but was rejected with an error response for some reason.");System.out.println("Error Message:" + oe.getErrorMessage());System.out.println("Error Code:" + oe.getErrorCode());System.out.println("Request ID:" + oe.getRequestId());System.out.println("Host ID:" + oe.getHostId());} catch (ClientException ce) {System.out.println("Caught an ClientException, which means the client encountered "+ "a serious internal problem while trying to communicate with OSS, "+ "such as not being able to access the network.");System.out.println("Error Message:" + ce.getMessage());} finally {if (ossClient != null) {ossClient.shutdown();}}//文件访问路径规则 https://BucketName.Endpoint/ObjectName/*最后,构建文件的访问路径,并使用日志记录上传文件的路径。*/StringBuilder stringBuilder = new StringBuilder("https://");stringBuilder.append(bucketName).append(".").append(endpoint).append("/").append(objectName);//拼接路径返回到数据库里log.info("文件上传到:{}", stringBuilder.toString());return stringBuilder.toString();}
}

至此,阿里云OSS的配置完成了。

最后,我们定义对应文件上传接口

/*** 通用接口*/
@RestController
@RequestMapping("/admin/common")
@Api(tags = "通用接口")
@Slf4j
/*CommonController是一个使用AliOssUtil进行文件上传的控制器类。*/
public class CommonController {@Autowiredprivate AliOssUtil aliOssUtil;/*** 文件上传** @param file* @return*/@PostMapping("/upload")@ApiOperation("文件上传")public Result<String> upload(MultipartFile file) {log.info("文件上传:{}", file);try {//原始文件名/*首先通过file.getOriginalFilename()获取原始文件名*/String originalFilename = file.getOriginalFilename();//截取原始文件名的后缀   dfdfdf.png/*然后通过originalFilename.lastIndexOf(".")获取文件名的后缀。*/String extension = originalFilename.substring(originalFilename.lastIndexOf("."));//构造新文件名称/*使用UUID.randomUUID().toString()生成一个随机的文件名,并将后缀拼接在文件名后面,构造出新的文件名。*/String objectName = UUID.randomUUID().toString() + extension;//文件的请求路径/*然后,调用aliOssUtil.upload方法将文件上传到OSS,并获取文件的请求路径。*/String filePath = aliOssUtil.upload(file.getBytes(), objectName);/*最后,返回一个Result对象,其中包含上传文件的请求路径。*/return Result.success(filePath);} catch (IOException e) {log.error("文件上传失败:{}", e);}return Result.error(MessageConstant.UPLOAD_FAILED);}
}

随后我们在测试时发生报错:

nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to 127.0.0.1:6379] with root causejava.net.ConnectException: Connection refused: no further informationat sun.nio.ch.SocketChannelImpl.checkConnect(Native Method) ~[na:1.8.0_201]

这个其实是由于我们没有启动Redis导致的,我们将Redis配置好后,启动服务即可。

在这里插入图片描述

使用了@Configuration后,在服务启动过程中这些就会被加载执行。即这些类在服务启动过程中便会创建好对象从而方便我们使用。

在这里插入图片描述

菜品添加

完成文件上传功能后,这其实是一个公用接口,无论是菜品添加还是其他的文件上传都可以使用,随后,开始菜品添加模块的设计:
分析数据传输的组成,其有一个口味属性,这个属性中还包含许多属性:

在这里插入图片描述

因此,需要封装一个专门用于传递菜品数据的类:

package com.sky.dto;
import com.sky.entity.DishFlavor;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Data
public class DishDTO implements Serializable {private Long id;//菜品名称private String name;//菜品分类idprivate Long categoryId;//菜品价格private BigDecimal price;//图片private String image;//描述信息private String description;//0 停售 1 起售private Integer status;//口味private List<DishFlavor> flavors = new ArrayList<>();
}

随后定义Controller的添加菜品接口

	@PostMapping()@ApiOperation("新增菜品")public Result save(@RequestBody DishDTO dishDTO) {log.info("新增菜品:{}", dishDTO);dishService.saveWithFlavor(dishDTO);//        清理缓存数据String key = "dish_" + dishDTO.getCategoryId();clearCache(key);return Result.success();}

定义Service的操作,这里涉及到两个表,分别是菜品表与口味表,因此需要开启事务管理,同时由于口味可以有多个,因此可以使用SQL的动态拼接来插入多条数据:

@Override@Transactional//由于涉及两张表,因此需要开启事务,即全成功或者全失败public void saveWithFlavor(DishDTO dishDTO) {Dish dish = new Dish();BeanUtils.copyProperties(dishDTO, dish);//Spring提供的属性拷贝//        向菜品表插入1条数据dishMapper.insert(dish);//        获取insert语句生成的主键值Long dishId = dish.getId();List<DishFlavor> flavors = dishDTO.getFlavors();if (flavors != null && flavors.size() > 0) {flavors.forEach(dishFlavor -> dishFlavor.setDishId(dishId));
//            向口味表插入n条数据,口味有多种dishFlavorMapper.insertBatch(flavors);}}

关于上面代码中的forEach,其可以实现遍历赋值。

TreeSetTest t1= new TreeSetTest("李白",19);
TreeSetTest t2= new TreeSetTest("杜甫",18);
Set<TreeSetTest> s=new TreeSet<>((o1,  o2) -> o2.getAge()-o1.getAge());
s.add(t1);
s.add(t2);
s.forEach(ss->ss.setAge(11));//这两个for循环语句作用相同,都能够改变s的值,即修改里面的值
for (TreeSetTest ss:s) {ss.setAge(11);
}

编写批量插入的SQL语句,在这段语句中,collection的参数就是口味list,每个对象用item代表,这个随便起,保存与下面的对象一致即可,separator=","是用于在每个 (#{item.dishId},#{item.name},#{item.value})后面加一个逗号表示分割。

<insert id="insertBatch">insert into dish_flavor (dish_id, name, value) VALUES<foreach collection="flavors" separator="," item="item">(#{item.dishId},#{item.name},#{item.value})</foreach>
</insert>

这里还需要注意的是,我们需要获取到菜品的id,而此时我们是在添加菜品,在菜品对象中是没有id的,因此可以使用insert插入语句中的返回值
useGeneratedKeys="true"代表需要主键值, keyProperty="id"表示将主键值赋给菜品对象的id属性

<insert id="insert" useGeneratedKeys="true" keyProperty="id">insert into dish (name, category_id, price, image, description, create_time, update_time, create_user,update_user, status)values (#{name}, #{categoryId}, #{price}, #{image}, #{description}, #{createTime}, #{updateTime}, #{createUser},#{updateUser}, #{status})</insert>

如业务层中所编写的那样,在执行完insert后就可以从对象中获取到属性值了。

//        向菜品表插入1条数据
dishMapper.insert(dish);
//        获取insert语句生成的主键值
Long dishId = dish.getId();
//使用forEach将口味对象的菜品id赋值
flavors.forEach(dishFlavor -> dishFlavor.setDishId(dishId));

菜品分页查询

根据文档分析,菜品查询所接收的参数为:
在这里插入图片描述
因此定义DishPageQueryDTO

package com.sky.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class DishPageQueryDTO implements Serializable {private int page;private int pageSize;private String name;//分类idprivate Integer categoryId;//状态 0表示禁用 1表示启用private Integer status;
}

此外我们看到上面的实体类后面实现了一个Serializable接口,这样做的意义是什么呢?
为何要implements Serializable ?

Controller层定义:

	@GetMapping("/page")@ApiOperation("菜品分页查询")public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO) {log.info("菜品分页查询:{}", dishPageQueryDTO);PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);//后绪步骤定义return Result.success(pageResult);}

基于PageHelper实现分页查询的业务层代码:

	@Overridepublic PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());Page<DishVO> page=dishMapper.pageQuery(dishPageQueryDTO);return new PageResult(page.getTotal(), page.getResult());}

这里需要注意的是返回结果是DishVO,因为结果涉及两个表的联合查询,要查类别名称。

<select id="pageQuery" resultType="com.sky.vo.DishVO">select d.*,c.name as categoryNamefrom dish d left join category c on d.category_id = c.id<where><if test="name!=null">and d.name like concat('%',#{name},'%')</if><if test="categoryId!=null">and d.categoryId like concat('%',#{categoryId},'%')</if><if test="status!=null">and d.status like concat('%',#{status},'%')</if></where>order by d.create_time desc</select>

至此,完成菜品的分页查询功能。

菜品删除

业务规则:

  • 可以一次删除一个菜品,也可以批量删除菜品
  • 起售中的菜品不能删除
  • 被套餐关联的菜品不能删除
  • 删除菜品后,关联的口味数据也需要删除掉

在这里插入图片描述

根据上面的业务逻辑,这将涉及三个表:菜品表,口味表,套餐与菜品关系表

在这里插入图片描述
Controller层定义,前端发来的请求是?参数名=参数值的形式,ids原本为字符串类型,即1,3,5,7,8,我们希望将这种数据转换为List列表这种形式,我们可以自己处理,但SpringMVC为我们提供了一种方法,即在前面加一个@RequestParam注解即可将其转换为List形式。

 	@DeleteMapping@ApiOperation("菜品批量删除")public Result delete(@RequestParam List<Long> ids) {log.info("菜品批量删除:{}", ids);dishService.deleteBatch(ids);//      将所有的菜品缓存数据清理掉,所有以dish_开头的keyclearCache("dish_*");return Result.success();}

在这里插入图片描述

随后Service层定义业务逻辑,要求删除菜品时需检查在套餐中是否含有,还要检查是否在售,在删除时要连同菜品口味一并删除。

@Override@Transactionalpublic void deleteBatch(List<Long> ids) {
//        判断当前菜品是否能够删除---是否存在起售中的菜品??ids.forEach(id->{Dish dish = dishMapper.getById(id);if (dish.getStatus() == StatusConstant.ENABLE) {
//                当前菜品处于起售中,不能删除throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);}});//        判断当前菜品是否能够删除---是否被套餐关联了??List<Long> setmealIds = setmealDishMapper.getSetmealIdsByDishIds(ids);if (setmealIds != null && setmealIds.size() > 0) {
//            当前菜品被套餐关联了,不能删除throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);}//        删除菜品表中的菜品数据ids.forEach(id->{dishMapper.deleteById(id);//            删除菜单关联的口味数据dishFlavorMapper.deleteByDishId(id);});}

如果被套餐关联或者在售卖中,则会抛出异常,抛出异常后这个异常就会被捕获,因为这些异常都是BaseException的子类,因此都可以在全局异常处理中被捕获:

package com.sky.exception;
public class DeletionNotAllowedException extends BaseException {public DeletionNotAllowedException(String msg) {super(msg);}
}
 	/*** 全局异常处理器,处理项目中抛出的业务异常*/
@RestControllerAdvice//开启异常捕获
@Slf4j
public class GlobalExceptionHandler {/*** 捕获业务异常* @param ex* @return*/@ExceptionHandlerpublic Result exceptionHandler(BaseException ex){log.error("异常信息:{}", ex.getMessage());return Result.error(ex.getMessage());}
}

查看菜品是否与套餐相关联,原始的SQL语句

select setmeal_id form setmeal_dish where dish_id in (1,3,5,8)

Mybatis中的动态SQL:


<select id="getSetmealIdsByDishIds" resultType="java.lang.Long">select setmeal_idfrom setmeal_dish where dish_id in<foreach collection="ids" separator="," item="item" open="(" close=")">#{item}</foreach></select>

至此,菜品删除便完成了,但我们发现一个问题,在删除菜品时,如果id过多,则会执行多次删除语句,这样势必对性能会造成影响,那么我们可以考虑使用批量删除语句:

delete from tables where id in (1,3,5,6,7)
//        删除菜品表中的菜品数据
//        ids.forEach(id->{
//            dishMapper.deleteById(id);
//
            删除菜单关联的口味数据
//            dishFlavorMapper.deleteByDishId(id);
//        });//批量删除菜品dishMapper.deleteByIds(ids);dishFlavorMapper.deleteByDishIds(ids);

对应的动态删除xml配置:

 <delete id="deleteByIds">deletefrom dish where id in<foreach collection="ids" item="id" separator="," open="(" close=")">#{id}</foreach></delete>
<delete id="deleteByDishIds">deletefrom dish_flavor where dish_id in<foreach collection="ids" item="dishid" separator="," open="(" close=")">#{dishid}</foreach></delete>

菜品修改

菜品修改涉及到的接口有:

  • 根据id查询菜品
  • 上传文件
  • 查询菜品类别
  • 修改菜品

其中,上传文件与查询菜品类别我们已经完成了,接下来便是根据id查询菜品和修改菜品了。

首先定义要返回的数据类型:

package com.sky.vo;import com.sky.entity.DishFlavor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DishVO implements Serializable {private Long id;//菜品名称private String name;//菜品分类idprivate Long categoryId;//菜品价格private BigDecimal price;//图片private String image;//描述信息private String description;//0 停售 1 起售private Integer status;//更新时间private LocalDateTime updateTime;//分类名称private String categoryName;//菜品关联的口味private List<DishFlavor> flavors = new ArrayList<>();//private Integer copies;
}

Controller层接口实现:

@GetMapping("/{id}")
@ApiOperation("根据id查询菜品")
public Result<DishVO> getById(@PathVariable Long id) {log.info("根据id查询菜品:{}", id);DishVO dishVO = dishService.getByIdWithFlavor(id);return Result.success(dishVO);
}

业务层代码

@Overridepublic DishVO getByIdWithFlavor(Long id) {
//        根据id查询菜品数据Dish dish = dishMapper.getById(id);//        根据菜品id查询口味数据List<DishFlavor> dishFlavorList = dishFlavorMapper.getByDishId(id);//        将查询到的数据封装到voDishVO dishVO = new DishVO();BeanUtils.copyProperties(dish, dishVO);dishVO.setFlavors(dishFlavorList);return dishVO;}

Mybatis 对应的SQL语句:

<select id="getById" resultType="com.sky.entity.Dish">select *from dish where id=#{id};</select>

随后开始修改操作的接口开发,该修改涉及菜品表与口味表:

@PutMapping@ApiOperation("修改菜品")public Result update(@RequestBody DishDTO dishDTO) {log.info("修改菜品:{}", dishDTO);dishService.updateWithFlavor(dishDTO);//将所有的菜品缓存数据清理掉,所有以dish_开头的keyclearCache("dish_*");return Result.success();}

由于涉及口味的修改,而我们可能会对口味进行增删操作,这还要判断,因此可以进行先全部删除,再将现有口味重新添加的方式来实现,即逻辑上是修改,技术实现上是删除与添加。

@Overridepublic void updateWithFlavor(DishDTO dishDTO) {Dish dish = new Dish();BeanUtils.copyProperties(dishDTO, dish);//        修改菜品基本信息dishMapper.update(dish);//        删除原有的口味信息dishFlavorMapper.deleteByDishId(dishDTO.getId());//        重新插入口味数据List<DishFlavor> flavors = dishDTO.getFlavors();if (flavors != null && flavors.size() > 0) {flavors.forEach(dishFlavor -> dishFlavor.setDishId(dishDTO.getId()));//            向口味表插入n条数据dishFlavorMapper.insertBatch(flavors);}

菜品修改动态SQL

<update id="update">update dish<set><if test="name != null">name = #{name},</if><if test="categoryId != null">category_id = #{categoryId},</if><if test="price != null">price = #{price},</if><if test="image != null">image = #{image},</if><if test="description != null">description = #{description},</if><if test="status != null">status = #{status},</if><if test="updateTime != null">update_time = #{updateTime},</if><if test="updateUser != null">update_user = #{updateUser},</if></set>where id =#{id}</update>

批量删除与插入口味信息

 <delete id="deleteByDishIds">deletefrom dish_flavor where dish_id in<foreach collection="ids" item="dishid" separator="," open="(" close=")">#{dishid}</foreach></delete>
<insert id="insertBatch">insert into dish_flavor (dish_id, name, value) VALUES<foreach collection="flavors" separator="," item="item">(#{item.dishId},#{item.name},#{item.value})</foreach>
</insert>

至此,便完成了菜品信息的增删改查了。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

社交媒体数据恢复:Talkbox

Talkbox数据恢复&#xff1a;找回珍贵的回忆 在数字化时代&#xff0c;我们的许多珍贵回忆都存储在手机应用程序中&#xff0c;如Talkbox。当意外发生&#xff0c;导致这些回忆丢失时&#xff0c;我们不禁会问&#xff1a;能否找回那些失去的数据&#xff1f;本文将探讨Talkbox…

【随笔】Git 基础篇 -- 拉取数据 git pull(二十八)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

C++这个编程语言以后会消失吗,就像以前70后学的编程语言?

随着AI自举编程的到来&#xff0c;绝大多数人类编程语言都会消失&#xff0c;只有 Scratch 这类启智语言作为儿童玩具保留下来。目前看来这一天不远了。 AI自举编程首先无需遵循这种可读文本变为二进制操作码的套路&#xff0c;它本身就是二进制的。而后&#xff0c;一旦智能制…

PHP01——php快速入门 之 在Mac上使用phpstudy快速搭建PHP环境

PHP01——php快速入门 之 在Mac上使用phpstudy快速搭建PHP环境 0. 前言1. 下载小皮面板1.1 下载phpstudy&#xff08;小皮面板&#xff09;1.2 启动、简单访问1.2.1 启动Apache1.2.2 访问1.2.3 访问自定义文件或页面 2. 创建网站2.1 创建网站2.2 可能遇到的问题2.2.1 hosts权限…

【k8s】监控与报警(qq邮箱+钉钉):Prometheus + Grafana + Alertmanager(超详细)

【k8s】监控与报警&#xff08;qq邮箱钉钉&#xff09;&#xff1a;Prometheus Grafana Alertmanager&#xff08;超详细&#xff09; 1、部署环境2、基本概念简介2.1、Prometheus简介2.2、Grafana简介2.3、Alertmanager简介2.4、Prometheus GrafanaAlertmanager监控架构 3、…

Mamba论文笔记

Mamba论文 结合序列建模任务通俗地解释什么是状态空间模型&#xff1f;创新点和贡献 为什么Mamba模型擅长捕获long range dependencies&#xff1f; 结合序列建模任务通俗地解释什么是状态空间模型&#xff1f; 状态空间模型&#xff08;State Space Model, SSM&#xff09;是…

Java Web开发问题(二)

一、nginx 1.1、反向代理 在 nginx.conf 配置文件中增加如下配置&#xff1a; 1 server { 2 listen 80; 3 server_name www.123.com; 4 5 location / { 6 proxy_pass http://127.0.0.1:8080; 7 index index.ht…

数据结构之来链表——单链表

什么是单链表&#xff1a; 文字说明&#xff1a; 单链表顾名思义&#xff0c;就是指单项链表&#xff0c;即只有一个方向的链性线性表。 图解&#xff1a; 如下图所示&#xff0c;即为链表&#xff08;DATA为我们自己所定义的数据类型&#xff09;&#xff1a; 单链表的创建&am…

负荷预测 | Matlab基于TCN-GRU-Attention单变量时间序列多步预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于TCN-GRU-Attention单变量时间序列多步预测&#xff1b; 2.单变量时间序列数据集&#xff0c;采用前12个时刻预测未来96个时刻的数据&#xff1b; 3.excel数据方便替换&#xff0c;运行环境matlab2023及以…

MyBatis操作数据库(3)

其它查询操作 #{}和${} MyBatis参数赋值有两种方式, 咱们前面使用了#{}进行赋值, 接下来来看两者的区别: #{}和${}的使用 1.先看Integer类型的参数: Select("select username, password, age, gender, phone from userinfo where id #{id}") UserInfo queryByI…

护眼灯哪个品牌好?五大护眼灯品牌推荐

护眼灯哪个品牌好&#xff1f;在挑选对眼睛有益的台灯时&#xff0c;专业护眼台灯无疑是明智的选择。这类台灯不仅严格按照国家的标准制造&#xff0c;确保了安全与质量的可靠性&#xff0c;除此之外护眼台灯还采用了经过精心设计的发光结构&#xff0c;有效减少了光线对眼睛的…

MinIO + Prometheus + Grafana docker部署

文章目录 说明MinIO简介MinIO 容器化部署Prometheus服务地址配置方法一&#xff1a;先部署后修改方法二&#xff1a;部署时修改compose文件&#xff08;未验证&#xff09; MinIO Access Key配置Prometheus 容器化部署MinIO 生成抓取配置修改Prometheus配置文件Grafana 容器化部…