ISDP010_基于DDD架构实现收银用例主成功场景

信息系统开发实践 | 系列文章传送门
ISDP001_课程概述
ISDP002_Maven上_创建Maven项目
ISDP003_Maven下_Maven项目依赖配置
ISDP004_创建SpringBoot3项目
ISDP005_Spring组件与自动装配
ISDP006_逻辑架构设计
ISDP007_Springboot日志配置与单元测试
ISDP008_SpringBoot Controller接口文档与测试
ISDP009_基于DDD架构设计ISDP的处理销售用例
ISDP010_基于DDD架构实现收银用例主成功场景

1 面向DDD重构mis-pos模块

重要说明:由于代码量增加,且经常需要重构。笔记将难以展示项目完整代码。本章笔记开始只展示部分代码。完整代码详见笔记最后项目仓库分支代码。

参考上篇分析与设计制品,参考DDD架构,重构的mis-pos模块的架构分层。

根据DDD架构分为application、domain、infrastructure三个包。
在这里插入图片描述

2 基础设施层

基础设施层暂时还没有写太多的类。只是添加了SaleFactory用于实例化Sale。

引入Hutool工具类,用于生成订单的雪花ID。

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.20</version>
        </dependency>

编写SaleFactory,实例化Sale并设置初始化值。

package edu.scau.mis.pos.infrastructure.factory;

import cn.hutool.core.util.IdUtil;
import edu.scau.mis.pos.domain.entity.Sale;
import edu.scau.mis.pos.domain.enums.SaleStatusEnum;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Date;

/**
 * Sale工厂类
 */
@Component
public class SaleFactory {
    public Sale initSale()
    {
        Sale sale = new Sale();
        sale.setSaleNo("so-" + IdUtil.getSnowflakeNextId());
        sale.setSaleStatus(SaleStatusEnum.CREATED);
        sale.setTotalAmount(BigDecimal.ZERO);
        sale.setTotalQuantity(0);
        sale.setSaleTime(new Date());
        return sale;
    }

}

3 领域层

在DDD架构中,领域层是重点关注层。

为了简化Setter和getter编写,引入了Lombok。

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

3.1 SaleProduct实体类

SaleProduct实体类包含了业务逻辑方法getSubTotal,计算每个订单明细的小计。

package edu.scau.mis.pos.domain.entity;

import edu.scau.mis.pos.domain.enums.SaleProductStatusEnum;
import lombok.Data;

import java.math.BigDecimal;

/**
 * 订单-产品明细实体类
 */
@Data
public class SaleProduct {
    private Long saleProductId;

    private Long saleId;

    private Long productId;

    private Product product;

    private Integer saleQuantity;

    private BigDecimal salePrice;

    private SaleProductStatusEnum saleProductStatus;

    /**
     * 计算小计
     * @return
     */
    public BigDecimal getSubTotal() {
        return salePrice.multiply(new BigDecimal(saleQuantity));
    }
}

3.2 支付实体类

支付类暂时还没有写业务逻辑方法。后期考虑通过适配器,连接第三方支付。

package edu.scau.mis.pos.domain.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import edu.scau.mis.pos.domain.enums.PaymentStatusEnum;
import edu.scau.mis.pos.domain.enums.PaymentStrategyEnum;
import lombok.Data;

import java.math.BigDecimal;
import java.util.Date;

/**
 * 支付实体类
 */
@Data
public class Payment {
    private Long paymentId;
    private Long paymentSaleId;
    private PaymentStrategyEnum paymentStrategy;
    private String paymentNo;
    private BigDecimal paymentAmount;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date paymentTime;
    private PaymentStatusEnum paymentStatus;

}

3.3 Sale聚合根

Sale类是收银领域层的聚合根。

该类内聚了两个业务逻辑方法,分别为添加订单明细和计算总金额。

package edu.scau.mis.pos.domain.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import edu.scau.mis.pos.domain.enums.SaleStatusEnum;
import lombok.Data;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 销售实体类
 * 聚合根
 */
@Data
public class Sale {
    private Long saleId;
    private String saleNo;
    private BigDecimal totalAmount;
    private Integer totalQuantity;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date saleTime;
    private Payment payment;
    private List<SaleProduct> saleProducts = new ArrayList<>();
    private SaleStatusEnum saleStatus;

    /**
     * 计算总金额
     * @return
     */

    public BigDecimal getTotal(){
        totalAmount = BigDecimal.ZERO;
        totalQuantity = 0;
        for(SaleProduct saleProduct: saleProducts){
            totalAmount = totalAmount.add(saleProduct.getSubTotal());
            totalQuantity = totalQuantity + saleProduct.getSaleQuantity();
        }
        return totalAmount;
    }

    /**
     * 添加订单明细
     * @param product
     * @param saleQuantity
     * @return
     */

    public List<SaleProduct> makeLineItem(Product product, Integer saleQuantity) {
        // 判断商品是否已录入,未录入则新增。已录入则修改数量。
        if(!isEntered(product.getProductSn(),saleQuantity)){
            SaleProduct saleProduct = new SaleProduct();
            saleProduct.setProduct(product);
            saleProduct.setSaleQuantity(saleQuantity);
            saleProduct.setSalePrice(product.getProductPrice());
            saleProducts.add(saleProduct);
        }
        return saleProducts;
    }

    /**
     * 判断商品是否已录入
     * 业务逻辑:如果已录入,则修改数量,否则添加saleLineItem
     * @param productSn
     * @param saleQuantity
     * @return
     */
    private boolean isEntered(String productSn, Integer saleQuantity){
        boolean flag = false;
        for(SaleProduct sp : saleProducts){
            if(productSn.equals(sp.getProduct().getProductSn())) {
                flag = true;
                Integer quantityOriginal = sp.getSaleQuantity();
                sp.setSaleQuantity(quantityOriginal + saleQuantity);
            }
        }
        return flag;
    }
}

3.4 领域服务类SaleService

主要用于生成支付功能。ISDP项目POS系统设计支持挂单功能。

package edu.scau.mis.pos.domain.service.impl;

import cn.hutool.core.util.IdUtil;
import edu.scau.mis.pos.domain.entity.Payment;
import edu.scau.mis.pos.domain.entity.Sale;
import edu.scau.mis.pos.domain.enums.PaymentStatusEnum;
import edu.scau.mis.pos.domain.enums.PaymentStrategyEnum;
import edu.scau.mis.pos.domain.service.ISaleService;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;

/**
 * 领域服务
 */
@Service
public class SaleServiceImpl implements ISaleService {
    @Override
    public Payment makePayment(Sale sale, String paymentStrategy, BigDecimal paymentAmount) {
        Payment payment = new Payment();
        payment.setPaymentStrategy(PaymentStrategyEnum.valueOf(paymentStrategy));
        payment.setPaymentNo(paymentStrategy + "-" + IdUtil.getSnowflakeNextId());
        payment.setPaymentAmount(paymentAmount);
        payment.setPaymentTime(new Date());
        payment.setPaymentStatus(PaymentStatusEnum.PAID);
        // TODO: 根据不同支付策略调用不同外部接口
        return payment;
    }
}

3.5 其他

Domain层还有仓库、枚举等包。由于暂时还没有使用数据库和Redis,仓库代码暂时没写。

写了一些枚举类。由于只是教学项目,没有设计过多状态。

package edu.scau.mis.pos.domain.enums;

/**
 * 订单状态枚举
 */
public enum SaleStatusEnum {
    CREATED("0","已预订"),

    SUBMITTED("1","已提交"),

    PAID("2","已支付");

    private String value;

    private String label;

    SaleStatusEnum(String value, String label) {
        this.value = value;
        this.label = label;
    }

    public String getLabel() {
        return label;
    }

    public String getValue() {
        return value;
    }

    /**
     * 根据匹配value的值获取Label
     *
     * @param value
     * @return
     */
    public static String getLabelByValue(String value){
        for (SaleStatusEnum s : SaleStatusEnum.values()) {
            if(value.equals(s.getValue())){
                return s.getLabel();
            }
        }
        return "";
    }

    /**
     * 获取StatusEnum
     *
     * @param value
     * @return
     */
    public static SaleStatusEnum getStatusEnum(String value){
        for (SaleStatusEnum s : SaleStatusEnum.values()) {
            if(value.equals(s.getValue())){
                return s;
            }
        }
        return null;
    }
}

支付策略枚举类

package edu.scau.mis.pos.domain.enums;

/**
 * 支付策略枚举
 */
public enum PaymentStrategyEnum {
    WECHAT("wechat","微信支付"),

    ALIPAY("alipay","支付宝"),

    CASH("cash","现金");

    private String value;

    private String label;

    PaymentStrategyEnum(String value, String label) {
        this.value = value;
        this.label = label;
    }

    public String getLabel() {
        return label;
    }

    public String getValue() {
        return value;
    }

    /**
     * 根据匹配value的值获取Label
     *
     * @param value
     * @return
     */
    public static String getLabelByValue(String value){
        for (PaymentStrategyEnum s : PaymentStrategyEnum.values()) {
            if(value.equals(s.getValue())){
                return s.getLabel();
            }
        }
        return "";
    }

    /**
     * 获取StatusEnum
     *
     * @param value
     * @return
     */
    public static PaymentStrategyEnum getStrategyEnum(String value){
        for (PaymentStrategyEnum s : PaymentStrategyEnum.values()) {
            if(value.equals(s.getValue())){
                return s;
            }
        }
        return null;
    }
}

4 应用层

4.1 应用服务类SaleApplicationService

编写SaleApplicationService类。该类主要负责跨领域协作。

目前主要就两个领域Sale(SaleProduct、Payment)和Product(Category)。

如果使用微服务,可以分别针对这两个领域创建两个微服务模块。

package edu.scau.mis.pos.application.service;

import edu.scau.mis.pos.application.assembler.SaleAssembler;
import edu.scau.mis.pos.application.dto.command.EnterItemCommand;
import edu.scau.mis.pos.application.dto.command.MakePaymentCommand;
import edu.scau.mis.pos.application.dto.vo.*;
import edu.scau.mis.pos.domain.entity.Payment;
import edu.scau.mis.pos.domain.entity.Product;
import edu.scau.mis.pos.domain.entity.Sale;
import edu.scau.mis.pos.domain.entity.SaleProduct;
import edu.scau.mis.pos.domain.enums.SaleStatusEnum;
import edu.scau.mis.pos.domain.service.IProductService;
import edu.scau.mis.pos.infrastructure.factory.SaleFactory;
import edu.scau.mis.pos.domain.service.ISaleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class SaleApplicationService {
    @Autowired
    private IProductService productService;

    @Autowired
    private ISaleService saleService;

    @Autowired
    private SaleFactory saleFactory;

    @Autowired
    private SaleAssembler saleAssembler;

    private Sale currentSale; // 后期改成Redis缓存CurrentSale

    /**
     * 开始一次新销售
     * @return
     */
    public SaleVo makeNewSale(){
        SaleVo saleVo = new SaleVo();
        currentSale = saleFactory.initSale();
        // TODO:引入Redis缓存
        return saleAssembler.toSaleVo(currentSale);
    }

    /**
     * 录入商品
     * @param command
     * @return
     */
    public SaleAndProductListVo enterItem(EnterItemCommand command){
        SaleAndProductListVo saleAndProductListVo = new SaleAndProductListVo();
        Product product = productService.selectProductBySn(command.getProductSn());
        List<SaleProduct> saleProducts = currentSale.makeLineItem(product, command.getSaleQuantity());
        currentSale.getTotal();
        List<SaleProductVo> saleProductVoList = saleProducts.stream()
                .map(saleProduct -> new SaleProductVo(saleProduct.getProduct().getProductSn(), saleProduct.getProduct().getProductName(), saleProduct.getSalePrice(), saleProduct.getSaleQuantity()))
                .toList();
        saleAndProductListVo.setSaleVo(saleAssembler.toSaleVo(currentSale));
        saleAndProductListVo.setSaleProductVoList(saleProductVoList);
        return saleAndProductListVo;
    }

    /**
     * 结束销售
     * 计算优惠、持久化订单等
     * @return
     */
    public SaleVo endSale(){
        currentSale.setSaleStatus(SaleStatusEnum.SUBMITTED);
        // TODO: 持久化Sale和SaleProduct,添加事务注解
        return saleAssembler.toSaleVo(currentSale);
    }

    /**
     * 完成支付
     * @param command
     * @return
     */
    public SaleAndPaymentVo makePayment(MakePaymentCommand command){
        SaleAndPaymentVo saleAndPaymentVo = new SaleAndPaymentVo();
        // TODO: 挂单--根据saleNo获取Sale
        Payment payment = saleService.makePayment(currentSale,command.getPaymentStrategy(), command.getPaymentAmount());
        currentSale.setPayment(payment);
        currentSale.setSaleStatus(SaleStatusEnum.PAID);
        // TODO: 持久化Sale和Payment,添加事务注解
        // payment.setPaymentSaleId(sale.getSaleId());
        saleAndPaymentVo.setSaleVo(saleAssembler.toSaleVo(currentSale));
        saleAndPaymentVo.setPaymentVo(saleAssembler.toPaymentVo(payment));
        return saleAndPaymentVo;
    }
}

4.2 数据传输对象DTO

ISDP项目采用CQRS思想,该层编写大量的数据传输对象DTO。笔记只展示部分代码。详细参加项目仓库。

EnterItemCommand参考代码如下。

后期将使用Redis缓存currentSale,设计saleNo作为key。保留saleNo备用。

package edu.scau.mis.pos.application.dto.command;

import lombok.Data;

import java.io.Serializable;

/**
 * 输入订单明细命令
 */
@Data
public class EnterItemCommand implements Serializable {
    private String saleNo;

    private String productSn;

    private Integer saleQuantity;
}

MakePaymentCommand代码参考如下:

同上,saleNo暂时不需要。

package edu.scau.mis.pos.application.dto.command;

import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;

/**
 * 创建支付命令
 */
@Data
public class MakePaymentCommand implements Serializable {
    private String saleNo;
    private BigDecimal paymentAmount;
    private String paymentStrategy;
}

SaleVo类

package edu.scau.mis.pos.application.dto.vo;

import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
@Data
public class SaleVo implements Serializable {
    private String saleNo;
    private BigDecimal totalAmount;
    private Integer totalQuantity;
    private Date saleTime;
    private String saleStatus;
}

SaleAndPaymentVo

package edu.scau.mis.pos.application.dto.vo;

import lombok.Data;

import java.io.Serializable;
@Data
public class SaleAndPaymentVo implements Serializable {
    private SaleVo saleVo;
    private PaymentVo paymentVo;
}

4.3 对象转换器SaleAssembler

面向接口层主要使用DTO对象,因此不可避免涉及到DTO与领域对象的转换。

package edu.scau.mis.pos.application.assembler;

import edu.scau.mis.pos.application.dto.vo.PaymentVo;
import edu.scau.mis.pos.application.dto.vo.SaleVo;
import edu.scau.mis.pos.domain.entity.Payment;
import edu.scau.mis.pos.domain.entity.Sale;
import org.springframework.stereotype.Component;

/**
 * 订单转换器
 * 实现DTO与Entity的转换
 */

@Component
public class SaleAssembler {
    public SaleVo toSaleVo(Sale sale){
        SaleVo saleVo = new SaleVo();
        saleVo.setSaleNo(sale.getSaleNo());
        saleVo.setTotalAmount(sale.getTotalAmount());
        saleVo.setTotalQuantity(sale.getTotalQuantity());
        saleVo.setSaleTime(sale.getSaleTime());
        saleVo.setSaleStatus(sale.getSaleStatus().getLabel());
        return saleVo;
    }

    public PaymentVo toPaymentVo(Payment payment){
        PaymentVo paymentVo = new PaymentVo();
        paymentVo.setPaymentId(payment.getPaymentId());
        paymentVo.setPaymentSaleId(payment.getPaymentSaleId());
        paymentVo.setPaymentNo(payment.getPaymentNo());
        paymentVo.setPaymentAmount(payment.getPaymentAmount());
        paymentVo.setPaymentTime(payment.getPaymentTime());
        paymentVo.setPaymentStrategy(payment.getPaymentStrategy().getLabel());
        paymentVo.setPaymentStatus(payment.getPaymentStatus().getLabel());
        return paymentVo;
    }
}

5 接口层

5.1 Controller接口

SaleController参考如下:

package edu.scau.mis.web.controller;

import edu.scau.mis.pos.application.dto.command.EnterItemCommand;
import edu.scau.mis.pos.application.dto.command.MakePaymentCommand;
import edu.scau.mis.pos.application.dto.vo.SaleAndPaymentVo;
import edu.scau.mis.pos.application.dto.vo.SaleAndProductListVo;
import edu.scau.mis.pos.application.dto.vo.SaleVo;
import edu.scau.mis.pos.application.service.SaleApplicationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/sale")
public class SaleController {
    @Autowired
    private SaleApplicationService saleApplicationService;
    @GetMapping("/makeNewSale")
    public ResponseEntity<SaleVo> makeNewSale()
    {
        return ResponseEntity.ok(saleApplicationService.makeNewSale());
    }

    @PostMapping("/enterItem")
    public ResponseEntity<SaleAndProductListVo> enterItem(@RequestBody  EnterItemCommand enterItemCommand)
    {
        return ResponseEntity.ok(saleApplicationService.enterItem(enterItemCommand));
    }

    @GetMapping("/endSale")
    public ResponseEntity<SaleVo> endSale()
    {
        return ResponseEntity.ok(saleApplicationService.endSale());
    }

    @PostMapping("/makePayment")
    public ResponseEntity<SaleAndPaymentVo> makePayment(@RequestBody MakePaymentCommand makePaymentCommand)
    {
        return ResponseEntity.ok(saleApplicationService.makePayment(makePaymentCommand));
    }
}

5.2 接口测试

使用Knife4j对SaleController接口进行测试,简单验证后端业务逻辑。

5.2.1 makeNewSale接口

该接口目前只是初始化currentSale数据。
在这里插入图片描述

5.2.2 enterItem接口

接口接收产品编号和订购数量。

接口返回订单和订购商品集合的json数据。

在这里插入图片描述

5.2.3 endSale接口

该接口暂时未写太多业务逻辑,只是提交订单,更新订单状。

后期将会从redis中清除缓存currentSale,然后持久化currentSale数据。
在这里插入图片描述

5.2.4 makePayment接口

接口接收支付金额和支付方式两个参数。

接口返回订单和支付json数据。
在这里插入图片描述
本章笔记基于上篇的分析与设计模型,编写DDD架构基础设施层、领域层、应用层和接口层的代码。实现了收银用例的4个主要步骤makeNewSale、enterItem、endSale和makePayment。

下一篇笔记将应用适配器模式调用支付宝沙箱支付接口。

本笔记项目仓库地址:
https://gitcode.com/tiger2704/isdp-boot3/tree/isdp010

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

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

相关文章

ElementPlus 自定义封装 el-date-picker 的快捷功能

文章目录 需求分析 需求 分析 我们看到官网上给出的案例如下&#xff0c;但是不太满足我们用户想要的快捷功能&#xff0c;因为不太多&#xff0c;因此需要我们自己封装一些&#xff0c;方法如下 外部自定义该组件的快捷内容 export const getPickerOptions () > {cons…

怎么模仿磁盘 IO 慢的情况?并用于MySQL进行测试

今天给大家分享一篇在测试环境或者是自己想检验自己MySQL性能的文章 实验环境&#xff1a; Rocky Linux 8 镜像&#xff1a;Rocky-8.6-x86_64-dvd.iso 1. 创建一个大文件作为虚拟磁盘 [rootlocalhost ~] dd if/dev/zero of/tmp/slowdisk.img bs1M count100 记录了1000 的读入…

C++--------继承

一、继承的基本概念 继承是 C 中的一个重要特性&#xff0c;它允许一个类&#xff08;派生类或子类&#xff09;继承另一个类&#xff08;基类或父类&#xff09;的属性和方法。这样可以实现代码的重用和建立类之间的层次关系。 #include <iostream>// 基类 class Base…

Ubuntu24.04安装NVIDIA驱动及工具包

Ubuntu24.04安装NVIDIA驱动及工具包 安装nvidia显卡驱动安装cuda驱动安装cuDNN安装Anaconda 安装nvidia显卡驱动 NVIDIA 驱动程序&#xff08;NVIDIA Driver&#xff09;是专为 NVIDIA 图形处理单元&#xff08;GPU&#xff09;设计的软件&#xff0c;它充当操作系统与硬件之间…

探秘“香水的 ChatGPT”:AI 开启嗅觉奇幻之旅!

你没有看错&#xff0c;AI也能闻到味道了&#xff01;这是一家名为Osmo公司公布的信息&#xff0c;他们成功创造出了由AI生成的李子味道&#xff0c;快跟着小编一探究竟吧~ 【图片来源于网络&#xff0c;侵删】 Osmo公司的这项技术&#xff0c;通过分析香味的化学成分和人类嗅…

vue之axios基本使用

文章目录 1. axios 网络请求库2. axiosvue 1. axios 网络请求库 <body> <input type"button" value"get请求" class"get"> <input type"button" value"post请求" class"post"> <!-- 官网提供…

高通 ISP pipeline

目录 ISP理解&#xff1a; 1. IFE : Image front-end engine&#xff08;图像前端引擎&#xff09; 1.1 相关特点 1.2 IFE作用 2. BPS : Bayer processing segment&#xff08;拜耳加工段&#xff09; 2.1 相关特点 2.2 BPS基本概念 2.3 BPS与IFE区别&#xff1a; 3. …

Linux知识点回顾(期末提分篇)

前言&#xff1a;本篇文章为WK学子量身打造&#xff0c;其余读者也可根据题目进行巩固提升。 目录 前言&#xff1a;本篇文章为WK学子量身打造&#xff0c;其余读者也可根据题目进行巩固提升。 一、Linux的内核版本每一部分的含义 二、查看当前系统中所有用户的详细信息的文…

【图像处理lec10】图像压缩

目录 一、图像压缩基础 1、图像压缩的基本概念 2、数据冗余与压缩比 3、三种主要的数据冗余类型 4、保真度评估标准&#xff08;Fidelity Criteria&#xff09; 5、应用与实践 二、图像压缩模型 1、图像压缩模型概述 &#xff08;1&#xff09;压缩系统的结构 &#…

Java和Go语言的优劣势对比

文章目录 Java和Go语言的优劣势对比一、引言二、设计哲学与语法特性1、设计哲学2、语法特性 三、性能与内存管理1、性能2、内存管理和垃圾回收 四、并发编程模型五、使用示例1、Go语言示例代码2、Java语言示例代码 六、对比表格七、总结 Java和Go语言的优劣势对比 一、引言 在…

CH340系列芯片驱动电路·CH340系列芯片驱动!!!

目录 CH340基础知识 CH340常见类型 CH340引脚功能讲解 CH340驱动电路 CH340系列芯片数据手册 编写不易&#xff0c;仅供学习&#xff0c;请勿搬运&#xff0c;感谢理解 常见元器件驱动电路文章专栏连接 LM7805系列降压芯片驱动电路降压芯片驱动电路详解-CSDN博客 ME62…

[Python3] Sanic中间件

在 Sanic 中&#xff0c;中间件&#xff08;middleware&#xff09;是指在请求和响应之间执行的代码。它们是一个非常强大的工具&#xff0c;用于处理请求的预处理、响应的后处理、全局错误处理、日志记录、认证、权限校验、跨域资源共享&#xff08;CORS&#xff09;等任务。中…

pikachu靶场搭建详细步骤

一、靶场下载 点我去下载 二、靶场安装 需要的环境&#xff1a; mysqlApaches&#xff08;直接使用小皮面板Phpstudy&#xff1a;https://www.xp.cn/&#xff09;&#xff0c;启动他们 设置网站&#xff0c;把靶场的路径对应过来 对应数据库的信息 由于没有核对数据库的信…

Goland 安装与使用

GoLand安装 官方网址&#xff1a; JetBrains GoLand&#xff1a;不只是 Go IDE 1. 进入官网&#xff0c;点击下载&#xff1a; ​ 2. 如下图一步步安装 ​ ​ ​ ​ ​ 3. 如下图一步步安装

计算属性 简写和 完整写法

计算属性渲染不加上括号 methods方法和computed属性区别&#xff1a; computed只计算一次&#xff0c;然后缓存&#xff0c;后续直接拿出来使用&#xff0c;而methods每次使用每次计算&#xff0c;不会缓存 计算属性完整写法&#xff1a; 既获取又设置 slice 截取 成绩案例 …

2024最新鸿蒙开发面试题合集(二)-HarmonyOS NEXT Release(API 12 Release)

上一篇面试题链接&#xff1a;https://mp.csdn.net/mp_blog/creation/editor/144685078 1. 鸿蒙简单介绍和发展历程 HarmonyOS 是新一代的智能终端操作系统&#xff0c;为不同设备的智能化、互联与协同提供了统一的语言。带来简洁&#xff0c;流畅&#xff0c;连续&#xff0…

【C++】——精细化哈希表架构:理论与实践的综合分析

先找出你的能力在哪里&#xff0c;然后再决定你是谁。 —— 塔拉韦斯特弗 《你当像鸟飞往你的山》 目录 1. C 与哈希表&#xff1a;核心概念与引入 2. 哈希表的底层机制&#xff1a;原理与挑战 2.1 核心功能解析&#xff1a;效率与灵活性的平衡 2.2 哈希冲突的本质&#x…

修改 ssh 默认访问端口

Linux 最小化安装后默认带有 ssh 服务并正常运行&#xff0c;服务默认端口为“22”。为了确保访问网络的安全&#xff0c;很多用户的网络设备对“22”端口做了限制&#xff0c;这时我们需要修改 ssh 服务默认的端口。 此步骤建议直接在服务器上通过鼠标键盘操作 修改配置文件 …

HCIA-Access V2.5_6_3_GPON组网保护

Type B单归属保护 在PON网络中&#xff0c;从OLT到ONU,整个链路上只有一根光纤&#xff0c;如果光纤出现断裂&#xff0c;业务就会中断&#xff0c;如果断的是分支链路一般主要影响个别用户&#xff0c;一旦主干光纤出现问题&#xff0c;PON口下所有的用户都会造成中断&#xf…

Mybatis-Plus中的Page方法出现Records的值大于0但是total的值一直是0

最近在学习mybatis-plus的时候&#xff0c;做分页查询&#xff0c;出现了一个诡异的情况&#xff0c;就是 Records的值大于0但是total的值一直是0&#xff0c;经过一顿百度之后发现&#xff0c;是缺少了一个分页的bean 加上这个配置类就好了&#xff0c;网上说这是个分页的插件…