电商+支付双系统项目------电商系统中订单模块的开发

这篇文章是关于项目的订单模块的设计。这个模块其实相对来讲比之前的模块复杂了点,我这里说的复杂并不是说难以理解,而是说文件比较多,理解起来还是蛮轻松的。

我们还是老方法,一步一步的去设计,按照Dao->service->controller的顺序来设计。

先来设计Dao层。需要注意的是,这里非常特别,因为Dao层里面有两个文件,分别是OrderMapper和OrderItemMapper。这两者有什么区别呢?区别就是一个是关于订单的文件,一个是关于订单项的文件。那订单和订单项有什么区别呢?我觉得如果真的看定义的话其实根本看不清,我就举一个例子。

假如现在有一个用户在电商网站上购买了三个商品,商品A、商品B和商品C。这个用户将这三个商品添加到购物车并进行结算,生成了一个订单。

订单(Order):

  • 订单号:202402240001
  • 用户ID:12345
  • 订单状态:待支付
  • 支付状态:未支付
  • 下单时间:2024-02-24 10:00:00
  • 收货地址:123 Main Street, City, Country

订单项(Order Item):

  1. 订单号:202402240001

    • 商品ID:1001
    • 商品名称:商品A
    • 商品数量:2
    • 商品价格:$10
  2. 订单号:202402240001

    • 商品ID:1002
    • 商品名称:商品B
    • 商品数量:1
    • 商品价格:$20
  3. 订单号:202402240001

    • 商品ID:1003
    • 商品名称:商品C
    • 商品数量:3
    • 商品价格:$15

这下我估计你该懂了。因为这种东西,干巴巴的说定义真的是说不清的,必须要配合例子来理解,至少我是这样的。

我们先来设计Dao层里面的OrderMapper。

package com.imooc.mall.dao;import com.imooc.mall.pojo.Order;import java.util.List;public interface OrderMapper {int deleteByPrimaryKey(Integer id);//根据订单ID删除订单记录int insert(Order record);//插入一条完整的订单记录int insertSelective(Order record);//选择性地插入一条订单记录,只插入非空字段的值Order selectByPrimaryKey(Integer id);//根据订单ID查询订单记录int updateByPrimaryKeySelective(Order record);//选择性地更新一条订单记录,只更新非空字段的值int updateByPrimaryKey(Order record);//更新一条完整的订单记录List<Order> selectByUid(Integer uid);//根据用户ID查询该用户的订单列表Order selectByOrderNo(Long orderNo);//根据订单号查询订单记录
}

上面这些方法定义了对订单表的基本操作,包括插入、更新、删除和查询。通过调用这些方法,可以对订单数据进行持久化操作,实现订单的增删改查功能。

但是,这里的 OrderMapper 接口没有具体的实现代码,只定义了接口方法。具体的实现代码通常由框架或者开发者自行完成,其实就是 OrderMapper.xml文件夹。这个文件夹实现了这个接口里的方法。

然后再来设计Dao层的OrderItemMapper。

package com.imooc.mall.dao;import com.imooc.mall.pojo.OrderItem;
import org.apache.ibatis.annotations.Param;import java.util.List;
import java.util.Set;public interface OrderItemMapper {int deleteByPrimaryKey(Integer id);//根据订单项ID删除订单项记录int insert(OrderItem record);//插入一条完整的订单项记录int insertSelective(OrderItem record);//选择性地插入一条订单项记录,只插入非空字段的值OrderItem selectByPrimaryKey(Integer id);//根据订单项ID查询订单项记录int updateByPrimaryKeySelective(OrderItem record);//选择性地更新一条订单项记录,只更新非空字段的值int updateByPrimaryKey(OrderItem record);//更新一条完整的订单项记录int batchInsert(@Param("orderItemList") List<OrderItem> orderItemList);//批量插入订单项记录。该方法接收一个订单项列表作为参数,并批量插入到数据库中List<OrderItem> selectByOrderNoSet(@Param("orderNoSet") Set orderNoSet);//根据订单号集合查询订单项列表。该方法接收一个订单号的集合作为参数,并返回对应的订单项列表
}

这些方法定义了对订单项表的基本操作,包括插入、更新、删除和查询。通过调用这些方法,可以对订单项数据进行持久化操作,实现订单项的增删改查功能。

有没有看到,其实这个订单项和订单是差不多的,你只要理解了这两者的区别基本上就很好理解了。

再来设计service层。

package com.imooc.mall.service;import com.github.pagehelper.PageInfo;
import com.imooc.mall.vo.OrderVo;
import com.imooc.mall.vo.ResponseVo;public interface IOrderService {ResponseVo<OrderVo> create(Integer uid, Integer shippingId);//创建订单。接收用户ID和收货地址ID作为参数,返回一个包含订单信息的响应对象 ResponseVo<OrderVo>。ResponseVo<PageInfo> list(Integer uid, Integer pageNum, Integer pageSize);//获取订单列表。接收用户ID、页码和每页大小作为参数,返回一个包含分页订单信息的响应对象 ResponseVo<PageInfo>。ResponseVo<OrderVo> detail(Integer uid, Long orderNo);//获取订单详情。接收用户ID和订单号作为参数,返回一个包含订单详细信息的响应对象 ResponseVo<OrderVo>。ResponseVo cancel(Integer uid, Long orderNo);//取消订单。接收用户ID和订单号作为参数,返回一个响应对象 ResponseVo,表示取消订单的结果。void paid(Long orderNo);//订单支付完成后的回调方法。接收订单号作为参数,无返回值。
}

这里的接口方法返回的类型是根据业务需求定义的泛型对象 ResponseVo 和 ResponseVo<T>,用于封装响应结果和数据。OrderVo 是一个用于表示订单信息的值对象。

我们一个方法一个方法的来实现。首先实现create方法:

public ResponseVo<OrderVo> create(Integer uid, Integer shippingId) {//收货地址校验(总之要查出来的)Shipping shipping = shippingMapper.selectByUidAndShippingId(uid, shippingId);if (shipping == null) {return ResponseVo.error(ResponseEnum.SHIPPING_NOT_EXIST);}//获取购物车,校验(是否有商品、库存)List<Cart> cartList = cartService.listForCart(uid).stream().filter(Cart::getProductSelected).collect(Collectors.toList());if (CollectionUtils.isEmpty(cartList)) {return ResponseVo.error(ResponseEnum.CART_SELECTED_IS_EMPTY);}//获取cartList里的productIdsSet<Integer> productIdSet = cartList.stream().map(Cart::getProductId).collect(Collectors.toSet());List<Product> productList = productMapper.selectByProductIdSet(productIdSet);Map<Integer, Product> map  = productList.stream().collect(Collectors.toMap(Product::getId, product -> product));List<OrderItem> orderItemList = new ArrayList<>();Long orderNo = generateOrderNo();for (Cart cart : cartList) {//根据productId查数据库Product product = map.get(cart.getProductId());//是否有商品if (product == null) {return ResponseVo.error(ResponseEnum.PRODUCT_NOT_EXIST,"商品不存在. productId = " + cart.getProductId());}//商品上下架状态if (!ProductStatusEnum.ON_SALE.getCode().equals(product.getStatus())) {return ResponseVo.error(ResponseEnum.PRODUCT_OFF_SALE_OR_DELETE,"商品不是在售状态. " + product.getName());}//库存是否充足if (product.getStock() < cart.getQuantity()) {return ResponseVo.error(ResponseEnum.PROODUCT_STOCK_ERROR,"库存不正确. " + product.getName());}OrderItem orderItem = buildOrderItem(uid, orderNo, cart.getQuantity(), product);orderItemList.add(orderItem);//减库存product.setStock(product.getStock() - cart.getQuantity());int row = productMapper.updateByPrimaryKeySelective(product);if (row <= 0) {return ResponseVo.error(ResponseEnum.ERROR);}}//计算总价,只计算选中的商品//生成订单,入库:order和order_item,事务Order order = buildOrder(uid, orderNo, shippingId, orderItemList);int rowForOrder = orderMapper.insertSelective(order);if (rowForOrder <= 0) {return ResponseVo.error(ResponseEnum.ERROR);}int rowForOrderItem = orderItemMapper.batchInsert(orderItemList);if (rowForOrderItem <= 0) {return ResponseVo.error(ResponseEnum.ERROR);}//更新购物车(选中的商品)//Redis有事务(打包命令),不能回滚for (Cart cart : cartList) {cartService.delete(uid, cart.getProductId());}//构造orderVoOrderVo orderVo = buildOrderVo(order, orderItemList, shipping);return ResponseVo.success(orderVo);}

这个方法太长了,我理解了好久,就说一下我自己的理解。

  1. 根据用户ID和收货地址ID查询对应的收货地址信息。
  2. 判断收货地址是否存在,如果不存在则返回错误响应。
  3. 获取用户购物车中选中的商品列表(已经勾选的商品)。
  4. 判断购物车是否为空,如果为空则返回错误响应。
  5. 从购物车列表中获取商品ID集合,并根据商品ID集合查询对应的商品信息。
  6. 构建订单项列表,遍历购物车列表,根据购物车项信息和商品信息构建订单项,并将订单项添加到订单项列表中。
  7. 生成订单号(orderNo)。
  8. 遍历订单项列表,依次处理每个订单项:
    • 根据商品ID获取对应的商品信息。
    • 判断商品是否存在,如果不存在则返回错误响应。
    • 判断商品的上下架状态,如果不是在售状态则返回错误响应。
    • 判断商品库存是否充足,如果不充足则返回错误响应。
    • 构建订单项对象,并将其添加到订单项列表中。
    • 减少商品库存数量,并更新数据库中的商品库存信息。
  9. 构建订单对象,包括用户ID、订单号、收货地址ID和订单项列表等信息。
  10. 将订单信息插入到数据库中。
  11. 将订单项列表插入到数据库中。
  12. 遍历购物车列表,删除购物车中已选中的商品。
  13. 构建订单视图对象(OrderVo),包括订单信息、订单项列表和收货地址信息等。
  14. 返回成功响应,并将订单视图对象作为响应数据返回。

这是真的复杂,但是我感觉就稍微看看,理解下原来是这么回事儿,脑子里有创建订单的流程就好了。

再来实现list方法。

@Overridepublic ResponseVo<PageInfo> list(Integer uid, Integer pageNum, Integer pageSize) {PageHelper.startPage(pageNum, pageSize);List<Order> orderList = orderMapper.selectByUid(uid);Set<Long> orderNoSet = orderList.stream().map(Order::getOrderNo).collect(Collectors.toSet());List<OrderItem> orderItemList = orderItemMapper.selectByOrderNoSet(orderNoSet);Map<Long, List<OrderItem>> orderItemMap = orderItemList.stream().collect(Collectors.groupingBy(OrderItem::getOrderNo));Set<Integer> shippingIdSet = orderList.stream().map(Order::getShippingId).collect(Collectors.toSet());List<Shipping> shippingList = shippingMapper.selectByIdSet(shippingIdSet);Map<Integer, Shipping> shippingMap = shippingList.stream().collect(Collectors.toMap(Shipping::getId, shipping -> shipping));List<OrderVo> orderVoList = new ArrayList<>();for (Order order : orderList) {OrderVo orderVo = buildOrderVo(order,orderItemMap.get(order.getOrderNo()),shippingMap.get(order.getShippingId()));orderVoList.add(orderVo);}PageInfo pageInfo = new PageInfo<>(orderList);pageInfo.setList(orderVoList);return ResponseVo.success(pageInfo);}
  1. 使用PageHelper工具类设置分页参数,即设置页码和每页显示数量。
  2. 根据用户ID查询对应的订单列表。
  3. 从订单列表中提取订单号集合。
  4. 根据订单号集合查询对应的订单项列表。
  5. 将订单项列表按订单号进行分组,构建订单号与订单项列表的映射关系。
  6. 从订单列表中提取收货地址ID集合。
  7. 根据收货地址ID集合查询对应的收货地址列表。
  8. 将收货地址列表按ID进行映射,构建收货地址ID与收货地址对象的映射关系。
  9. 遍历订单列表,逐个构建订单视图对象(OrderVo):
    • 根据订单信息、订单项映射和收货地址映射构建订单视图对象。
    • 将订单视图对象添加到订单视图列表中。
  10. 构建分页信息对象(PageInfo),其中包括订单列表和总记录数等信息。
  11. 将订单视图列表设置到分页信息对象中。
  12. 返回成功响应,将分页信息对象作为响应数据返回。

这也很复杂,就随便看看吧。

再来实现detail方法。

@Overridepublic ResponseVo<OrderVo> detail(Integer uid, Long orderNo) {Order order = orderMapper.selectByOrderNo(orderNo);if (order == null || !order.getUserId().equals(uid)) {return ResponseVo.error(ResponseEnum.ORDER_NOT_EXIST);}Set<Long> orderNoSet = new HashSet<>();orderNoSet.add(order.getOrderNo());List<OrderItem> orderItemList = orderItemMapper.selectByOrderNoSet(orderNoSet);Shipping shipping = shippingMapper.selectByPrimaryKey(order.getShippingId());OrderVo orderVo = buildOrderVo(order, orderItemList, shipping);return ResponseVo.success(orderVo);}
  1. 根据订单号查询对应的订单信息。
  2. 判断订单是否存在或者订单所属用户是否与传入的用户ID匹配,如果不匹配则返回错误响应。
  3. 创建订单号集合,并将当前订单号添加到集合中。
  4. 根据订单号集合查询对应的订单项列表。
  5. 根据订单的收货地址ID查询对应的收货地址信息。
  6. 根据订单信息、订单项列表和收货地址信息构建订单视图对象(OrderVo)。
  7. 返回成功响应,将订单视图对象作为响应数据返回。

再来实现cancel方法。

@Overridepublic ResponseVo cancel(Integer uid, Long orderNo) {Order order = orderMapper.selectByOrderNo(orderNo);if (order == null || !order.getUserId().equals(uid)) {return ResponseVo.error(ResponseEnum.ORDER_NOT_EXIST);}//只有[未付款]订单可以取消,看自己公司业务if (!order.getStatus().equals(OrderStatusEnum.NO_PAY.getCode())) {return ResponseVo.error(ResponseEnum.ORDER_STATUS_ERROR);}order.setStatus(OrderStatusEnum.CANCELED.getCode());order.setCloseTime(new Date());int row = orderMapper.updateByPrimaryKeySelective(order);if (row <= 0) {return ResponseVo.error(ResponseEnum.ERROR);}return ResponseVo.success();}
  1. 根据订单号查询对应的订单信息。
  2. 判断订单是否存在或订单所属用户是否与传入的用户ID匹配,如果不匹配则返回错误响应。
  3. 判断订单状态是否为未付款状态,如果不是则返回错误响应。
  4. 将订单状态设置为已取消状态,并设置取消时间为当前时间。
  5. 更新数据库中的订单信息。
  6. 判断更新结果,如果影响的行数小于等于0,则返回错误响应。
  7. 返回成功响应。

这里也是随便看看。只要你脑补到有这个画面就行。

最后设计paid方法。

@Overridepublic void paid(Long orderNo) {Order order = orderMapper.selectByOrderNo(orderNo);if (order == null) {throw new RuntimeException(ResponseEnum.ORDER_NOT_EXIST.getDesc() + "订单id:" + orderNo);}//只有[未付款]订单可以变成[已付款],看自己公司业务if (!order.getStatus().equals(OrderStatusEnum.NO_PAY.getCode())) {throw new RuntimeException(ResponseEnum.ORDER_STATUS_ERROR.getDesc() + "订单id:" + orderNo);}order.setStatus(OrderStatusEnum.PAID.getCode());order.setPaymentTime(new Date());int row = orderMapper.updateByPrimaryKeySelective(order);if (row <= 0) {throw new RuntimeException("将订单更新为已支付状态失败,订单id:" + orderNo);}}
  • 根据订单号查询订单信息。
  • 如果订单不存在,则抛出运行时异常。
  • 判断订单状态是否为未付款状态,如果不是则抛出运行时异常。
  • 将订单状态设置为已支付状态,设置支付时间为当前时间。
  • 更新数据库中的订单信息。
  • 判断更新结果,如果影响的行数小于等于0,则抛出运行时异常。

OK了!service层就已经设计完毕了。确实订单模块这里比较繁琐,所以我感觉最重要还是理解,不需要说背,背就没意思了,脑补到每个实现方法的画面就很好了。

然后设计controller层。

package com.imooc.mall.controller;import com.github.pagehelper.PageInfo;
import com.imooc.mall.consts.MallConst;
import com.imooc.mall.form.OrderCreateForm;
import com.imooc.mall.pojo.User;
import com.imooc.mall.service.IOrderService;
import com.imooc.mall.vo.OrderVo;
import com.imooc.mall.vo.ResponseVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpSession;
import javax.validation.Valid;/*** Created by 廖师兄*/
@RestController
public class OrderController {@Autowiredprivate IOrderService orderService;@PostMapping("/orders")public ResponseVo<OrderVo> create(@Valid @RequestBody OrderCreateForm form,HttpSession session) {User user = (User) session.getAttribute(MallConst.CURRENT_USER);return orderService.create(user.getId(), form.getShippingId());}@GetMapping("/orders")public ResponseVo<PageInfo> list(@RequestParam Integer pageNum,@RequestParam Integer pageSize,HttpSession session) {User user = (User) session.getAttribute(MallConst.CURRENT_USER);return orderService.list(user.getId(), pageNum, pageSize);}@GetMapping("/orders/{orderNo}")public ResponseVo<OrderVo> detail(@PathVariable Long orderNo,HttpSession session) {User user = (User) session.getAttribute(MallConst.CURRENT_USER);return orderService.detail(user.getId(), orderNo);}@PutMapping("/orders/{orderNo}")public ResponseVo cancel(@PathVariable Long orderNo,HttpSession session) {User user = (User) session.getAttribute(MallConst.CURRENT_USER);return orderService.cancel(user.getId(), orderNo);}
}
  1. create() 方法用于创建订单。通过接收一个包含订单创建表单数据的 POST 请求,并从会话中获取当前用户信息,调用订单服务的 create() 方法来创建订单,并返回相应的响应结果。

  2. list() 方法用于获取订单列表。通过接收 GET 请求中的页码和每页数量参数,并从会话中获取当前用户信息,调用订单服务的 list() 方法来获取当前用户的订单列表,并返回相应的响应结果。

  3. detail() 方法用于获取订单详情。通过接收 GET 请求中的订单号参数,并从会话中获取当前用户信息,调用订单服务的 detail() 方法来获取指定订单的详情,并返回相应的响应结果。

  4. cancel() 方法用于取消订单。通过接收 PUT 请求中的订单号参数,并从会话中获取当前用户信息,调用订单服务的 cancel() 方法来取消指定订单,并返回相应的响应结果。

这些方法使用了依赖注入(@Autowired)来获取订单服务(IOrderService)的实例,以便调用订单相关的业务逻辑。同时,使用了会话(HttpSession)来获取当前用户信息。根据不同的请求类型和参数,调用相应的订单服务方法,并返回相应的响应给客户端。

这就是订单模块的所有实现,我感觉订单模块其实理解起来并不难,主要是太多了,所以我感觉应该静下心来仔细想想service层的整个流程,service层一旦掌握了,那整个模块应该很容易就掌握了。

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

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

相关文章

建立不同类型网站分别大概需要多少钱??

如今&#xff0c;越来越多的企业会考虑建立一个企业官方网站来展示企业形象&#xff0c;或者建立一个电子商务网站平台来拓展业务渠道&#xff0c;或者建立一个企业内部网来协助企业进行网上工作。 网站建设的类型有很多种&#xff0c;不同类型的网站成本差异很大。 因此&#…

FairyGUI × Cocos Creator 3.x 场景切换

前言 前文提要&#xff1a; FariyGUI Cocos Creator 入门 FairyGUI Cocos Creator 3.x 使用方式 个人demo&#xff1a;https://gitcode.net/qq_36286039/fgui_cocos_demo_dust 个人demo可能会更新其他代码&#xff0c;还请读者阅读本文内容&#xff0c;自行理解并实现。 官…

C++的文件操作详解

目录 1.文本文件 1.写文件 2.读文件 2.二进制文件 1.写文件 2.读文件 1.文本文件 1.写文件 #include<bits/stdc.h> #include<fstream> using namespace std;int main() {ofstream ofs;ofs.open("text.txt",ios::out);ofs << "abc&qu…

2024最新版Python 3.12.2安装使用指南

2024最新版Python 3.12.2安装使用指南 Installation and Usage Guide to the Latest Version - Python 3.12.2 for Windows in 2024 By JacksonML 0. Python的受欢迎程度 据TechRepublic报道&#xff0c;截至2024年2月16日&#xff0c;全球最流行的编程语言之中&#xff0c…

go使用trpc案例

1.go下载trpc go install trpc.group/trpc-go/trpc-cmdline/trpclatest 有报错的话尝试配置一些代理&#xff08;选一个&#xff09; go env -w GOPROXYhttps://goproxy.cn,direct go env -w GOPROXYhttps://goproxy.io,direct go env -w GOPROXYhttps://goproxy.baidu.com/…

普中51单片机学习(红外通信)

红外通信 红外线系统的组成 外线遥控器已被广泛使用在各种类型的家电产品上&#xff0c;它的出现给使用电器提供了很多的便利。红外线系统一般由红外发射装置和红外接收设备两大部分组成。红外发射装置又可由键盘电路、红外编码芯片、电源和红外发射电路组成。红外接收设备可由…

igolang学习2,golang开发配置国内镜像

go env -w GO111MODULEon go env -w GOPROXYhttps://goproxy.cn,direct

Modern C++ std::visit从实践到原理

前言 std::visit 是 C17 中引入的一个模板函数&#xff0c;它用于对给定的 variant、union 类型或任何其他兼容的类型执行一个访问者操作。这个函数为多种可能类型的值提供了一种统一的访问机制。使用 std::visit&#xff0c;你可以编写更通用和灵活的代码&#xff0c;而无需关…

持续集成,持续交付和持续部署的概念,以及GitLab CI / CD的介绍

引言&#xff1a;上一期我们部署好了gitlab极狐网页版&#xff0c;今天我们介绍一下GitLabCI / CD 目录 一、为什么要 CI / CD 方法 1、持续集成 2、持续交付 3、持续部署 二、GitLab CI / CD简介 三、GitLab CI / CD 的工作原理 4、基本CI / CD工作流程 5、首次设置 …

SQL防止注入工具类,可能用于SQL注入的字符有哪些

SQL注入是一种攻击技术&#xff0c;攻击者试图通过在输入中注入恶意的SQL代码来干扰应用程序的数据库查询。为了防止SQL注入&#xff0c;你需要了解可能用于注入的一些常见字符和技术。以下是一些常见的SQL注入字符和技术&#xff1a; 单引号 ​&#xff1a; 攻击者可能会尝试…

【Spring】声明式事务 spring-tx

文章目录 声明式事务是什么&#xff1f;一、Spring事务管理器二、基于注解的声明式事务1.1 准备工作1.2 基本事务控制1.3 事务属性&#xff1a;只读1.4 事务属性&#xff1a;超时时间1.5 事务属性&#xff1a;事务异常1.6 事务属性&#xff1a;事务隔离级别1.7 事务属性&#x…

【vscode】按F5无法执行调试python或go

原因&#xff1a; 找不到解析器&#xff0c;需要安装插件&#xff08;python&#xff0c;或go 等&#xff09; 安装插件后&#xff0c;还是无法执行&#xff0c;按 ctrlshiftp,看不到解析器 正常应该是&#xff1a; 解决方法&#xff1a; 1、判断python是否安装成功 pyth…

2024Node.js零基础教程(小白友好型),nodejs新手到高手,(八)NodeJS入门——http模块

一念心清净&#xff0c;处处莲花开。 055_http模块_网页资源加载基本过程 哈喽&#xff0c;大家好&#xff0c;这一课节我们来介绍一下网页资源加载的基本过程。首先先强调一点&#xff0c;这个内容对于我们后续学习非常非常的关键&#xff0c;所以大家务必要将其掌握。 首先先…

安科瑞企业微电网智慧能源管理系统生态交流会顺利举行

2024年1月12日&#xff0c;安科瑞企业微电网智慧能源管理系统生态交流会顺利举行&#xff0c;本次会议旨在围绕双碳目标&#xff0c;共同探讨如何抓住新机遇、新市场&#xff0c;充分利用安科瑞企业微电网智慧能源的一站式服务&#xff0c;为企业节能、减碳、降本赋能&#xff…

Ansible 简介及部署 基础模块学习 ansible部署rsync 及时监控远程同步

Ansible介绍&#xff1a; Ansible 是一个配置管理系统&#xff0c;当下最流行的批量自动化运维工具之一&#xff0c;它是一款开源的自动化工具&#xff0c;基于Python开发的配置管理和应用部署的工具。 Ansible 是基于模块工作的&#xff0c;它只是提供了一种运行框架&#xff…

STM32控制max30102读取血氧心率数据(keil5工程)

一、前言 MAX30102是一款由Maxim Integrated推出的低功耗、高精度的心率和血氧饱和度检测传感器模块&#xff0c;适用于可穿戴设备如智能手环、智能手表等健康管理类电子产品。 该传感器主要特性如下&#xff1a; &#xff08;1&#xff09;光学测量&#xff1a;MAX30102内置…

nginx-------- 高性能的 Web服务端 (三) 验证模块 页面配置

一、http设置 1.1 验证模块 需要输入用户名和密码 htpasswd 此命令来自于 httpd-tools 包&#xff0c;如果没有安装 安装一下即可 也可以安装httpd 直接yum install httpd -y 也一样 第一次生成文件htpasswd -c 文件路径 姓名 交互式生成密码 htpasswd -bc 文…

c语言的数据结构:找环状链表入口处

一起<(&#xffe3;︶&#xffe3;)↗[GO!] 1.如何判断一个链表是否有环 思路:设定两个快慢指针fast和slow,fast每次走两个结点,slow每次走一个节点 如果fast指针遇到了Null,那么这个链表没有环,如果fast和slow可以相遇,则代表这个链表有环 代码如下 N:fast先进环,slow后…

[C#]winform基于opencvsharp结合CSRNet算法实现低光图像增强黑暗图片变亮变清晰

【算法介绍】 "Conditional Sequential Modulation for Efficient Global Image Retouching" 是一种图像修饰方法&#xff0c;主要用于对图像进行全局的高效调整。该方法基于深度学习技术&#xff0c;通过引入条件向量来实现对图像特征的调制&#xff0c;以达到改善…

DAY28--learning English

一、积累 1.hood 2.peripheral 3.gallery 4.cord 5.cart 6.permanent 7.democratic 8.republican 9.bride 10.jet-lagged 11.vet 12.lease 13.landlord 14.aisle 15.lousy 16.via 17.order in 18.forecast 19.initiation 20.credit 二、练习 1.牛津原译 Hood /hʊd/ 1. a par…