小熊家政帮day22-day23 订单系统优化(订单状态机、练习分库分表、索引、订单缓存)

目录

  • 1 状态机
    • 1.1 状态机介绍
      • 1.1.1 当前存在的问题
      • 1.1.2 使用状态机解决问题
    • 1.2 实现订单状态机
      • 1.2.1 编写订单状态机
        • 1.2.1.1 依赖引入
        • 1.2.1.2 订单状态枚举类
        • 1.2.1.3 状态变更事件枚举类
        • 1.2.1.4 定义订单快照类
        • 1.2.1.5 定义事件变更动作类
        • 1.2.1.5 定义订单状态机类
        • 1.2.1.6 状态机表设计
      • 1.2.2 测试订单状态机
        • 加载订单状态机
        • 测试启动状态机
      • 1.2.3 使用订单状态机
        • 下单时启动状态机
        • 支付成功使用状态机
        • 测试

1 状态机

1.1 状态机介绍

1.1.1 当前存在的问题

在预约下单模块设计订单状态共有7种,如下图:
在这里插入图片描述
目前我们使用了待支付、派单中两种状态,在代码中我们发现存在对订单状态进行硬编码的情况,但是随着开发的深入这种代码会越来越多,比如在实现对订单进行关闭时代码会写成如下的形式:

1//运营人员在订单完成时取消订单//执行此场景下的业务逻辑//更新订单状态为派单中update(id,已关闭)
)
if(订单状态==服务中){//运营人员在服务中时取消订单//执行此场景下的业务逻辑//更新订单状态为已关闭update(id,已关闭)
)
...

以上代码存在问题如下:
在业务代码中对订单状态进行硬编码如果有一天更改了业务逻辑就需要更改代码,不方便进行系统扩展和维护。
另外对订单状态的管理是散落在很多地方不方便对订单状态进行统一管理和维护。

1.1.2 使用状态机解决问题

针对以上问题如何解决呢?
我们可以使用状态机对订单状态进行统一管理。

什么是状态机?
上图在UML中叫状态图(又叫状态机图),UML是软件开发中的一种建模语言,用来辅助进行软件设计,常用的如:类图、对象、状态图、序列图等,注意状态机图并不是状态机,状态机是一种数学模型,应用在自动化控制、计算机科学、通信等很多领域,简单理解状态机就是对状态进行统一管理的数学模型。
我们画的状态图是状态机在计算机科学中的应用方法,还有状态机设计模式也是状态机在软件领域的应用方法。
状态机设计模式是状态机在软件中的应用,状态机设计模式描述了一个对象在内部状态发生变化时如何改变其行为,将状态之间的变更定义为事件,将事件暴露出去,通过执行状态变更事件去更改状态,这是状态机设计模式的核心内容。

理解状态机设计模式需要理解四个要素:现态、事件、动作、次态。

1、现态:是指当前所处的状态。
2、事件:当一个条件被满足,状态会由现态变为新的状态,事件发生会触发一个动作,或者执行一次状态的迁移。
3、动作:发生事件执行的动作,动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
4、次态:条件满足后要迁往的新状态。

我们拿待支付状态到派单中状态举例:
在这里插入图片描述
现态:订单当前处于待支付状态那么现态为待支付。
事件:用户支付成功为事件,支付成功是条件,当条件满足进行状态迁移。
动作:将订单状态由待支付更改为派单中。
次态:派单中。

使用状态机优化代码:
使用状态机之后对代码进行以下优化。
支付成功更改订单状态的代码优化如下:

if(支付状态==支付成功){//调用状态机执行支付成功事件orderStateMachine.changeStatus(id,支付成功事件);
}

订单取消的代码优化如下:

orderStateMachine.changeStatus(id,订单完成时取消订单事件);

我们发现使用状态机的代码并没有对订单状态进行硬编码,只是指定了订单id和事件名称,执行changeStatus方法后自动更改订单的状态。

1.2 实现订单状态机

1.2.1 编写订单状态机

1.2.1.1 依赖引入

本项目基于状态机设计模式开发了状态机组件,代码在jzo2o-framework中,如果在订单管理服务中实现订单状态机需要添加状态机的依赖。
在jzo2o-orders-base工程的pom.xml中添加状态机组件的依赖

<dependency><groupId>com.jzo2o</groupId><artifactId>jzo2o-statemachine</artifactId><version>1.0-SNAPSHOT</version>
</dependency>
1.2.1.2 订单状态枚举类

阅读订单状态枚举类,它实现了StatusDefine 状态接口,不论是现态还是次态都需要实现状态接口。
定义每个枚举需要注意见名知意,比如:NO_PAY(0, “待支付”, “NO_PAY”)表示待支付状态。
订单状态枚举类如下:

@Getter
@AllArgsConstructor
public enum OrderStatusEnum implements StatusDefine {NO_PAY(0, "待支付", "NO_PAY"),DISPATCHING(100, "派单中", "DISPATCHING"),NO_SERVE(200, "待服务", "NO_SERVE"),SERVING(300, "服务中", "SERVING"),FINISHED(500, "已完成", "FINISHED"),CANCELED(600, "已取消", "CANCELED"),CLOSED(700, "已关闭", "CLOSED");private final Integer status;private final String desc;private final String code;/*** 根据状态值获得对应枚举** @param status 状态* @return 状态对应枚举*/public static OrderStatusEnum codeOf(Integer status) {for (OrderStatusEnum orderStatusEnum : values()) {if (orderStatusEnum.status.equals(status)) {return orderStatusEnum;}}return null;}
}
1.2.1.3 状态变更事件枚举类

所有状态之间存在的变更都需要定义状态变更事件,它实现了StatusChangeEvent 状态变更事件接口,事件对应状态机四要素的事件
代码如下,重点看PAYED:
PAYED(OrderStatusEnum.NO_PAY, OrderStatusEnum.DISPATCHING, “支付成功”, “payed”)表示由NO_PAY(未支付)状态变化为DISPATCHING(派单中)状态,事件名称为“支付成功”(payed)。

@Getter
@AllArgsConstructor
public enum OrderStatusChangeEventEnum implements StatusChangeEvent {PAYED(OrderStatusEnum.NO_PAY, OrderStatusEnum.DISPATCHING, "支付成功", "payed"),DISPATCH(OrderStatusEnum.DISPATCHING, OrderStatusEnum.NO_SERVE, "接单/抢单成功", "dispatch"),START_SERVE(OrderStatusEnum.NO_SERVE, OrderStatusEnum.SERVING, "开始服务", "start_serve"),COMPLETE_SERVE(OrderStatusEnum.SERVING, OrderStatusEnum.FINISHED, "完成服务", "complete_serve"),
//    EVALUATE(OrderStatusEnum.NO_EVALUATION, OrderStatusEnum.FINISHED, "评价完成", "evaluate"),CANCEL(OrderStatusEnum.NO_PAY, OrderStatusEnum.CANCELED, "取消订单", "cancel"),SERVE_PROVIDER_CANCEL(OrderStatusEnum.NO_SERVE, OrderStatusEnum.DISPATCHING, "服务人员/机构取消订单", "serve_provider_cancel"),CLOSE_DISPATCHING_ORDER(OrderStatusEnum.DISPATCHING, OrderStatusEnum.CLOSED, "派单中订单关闭", "close_dispatching_order"),CLOSE_NO_SERVE_ORDER(OrderStatusEnum.NO_SERVE, OrderStatusEnum.CLOSED, "待服务订单关闭", "close_no_serve_order"),CLOSE_SERVING_ORDER(OrderStatusEnum.SERVING, OrderStatusEnum.CLOSED, "服务中订单关闭", "close_serving_order"),
//    CLOSE_NO_EVALUATION_ORDER(OrderStatusEnum.NO_EVALUATION, OrderStatusEnum.CLOSED, "待评价订单关闭", "close_no_evaluation_order"),CLOSE_FINISHED_ORDER(OrderStatusEnum.FINISHED, OrderStatusEnum.CLOSED, "已完成订单关闭", "close_finished_order");/*** 源状态*/private final OrderStatusEnum sourceStatus;/*** 目标状态*/private final OrderStatusEnum targetStatus;/*** 描述*/private final String desc;/*** 代码*/private final String code;
}
1.2.1.4 定义订单快照类
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderSnapshotDTO extends StateMachineSnapshot {/*** 订单id*/private Long id;/*** 订单所属人*/private Long userId;/*** 服务类型id*/private Long serveTypeId;/*** 服务类型名称*/private String serveTypeName;/*** 服务项id*/private Long serveItemId;/*** 服务项名称*/private String serveItemName;/*** 服务项图片*/private String serveItemImg;/*** 服务单位*/private Integer unit;/*** 服务id*/private Long serveId;/*** 订单状态,0:待支付,100:派单中,200:待服务,300:服务中,500:订单完成,600:订单取消,700已关闭*/private Integer ordersStatus;/*** 支付状态,2:待支付,4:支付成功*/private Integer payStatus;/*** 退款,0:发起退款,1:退款中,2:退款成功  3:退款失败*/private Integer refundStatus;/*** 单价*/private BigDecimal price;/*** 购买数量*/private Integer purNum;/*** 订单总金额*/private BigDecimal totalAmount;/*** 实际支付金额*/private BigDecimal realPayAmount;/*** 优惠金额*/private BigDecimal discountAmount;/*** 城市编码*/private String cityCode;/*** 服务详细地址*/private String serveAddress;/*** 联系人手机号*/private String contactsPhone;/*** 联系人姓名*/private String contactsName;/*** 服务开始时间*/private LocalDateTime serveStartTime;/*** 经度*/private String lon;/*** 纬度*/private String lat;/*** 支付时间*/private LocalDateTime payTime;/*** 评价时间*/private LocalDateTime evaluationTime;/*** 订单创建时间*/private LocalDateTime createTime;/*** 订单更新时间*/private LocalDateTime updateTime;/*** 支付服务交易单号*/private Long tradingOrderNo;/*** 支付服务退款单号*/private Long refundNo;/*** 支付渠道【支付宝、微信、现金、免单挂账】*/private String tradingChannel;/*** 三方流水,微信支付订单号或支付宝订单号*/private String thirdOrderId;/*** 退款三方流水,微信支付订单号或支付宝订单号*/private String thirdRefundOrderId;/*** 取消人id*/private Long cancellerId;/*** 取消人名称*/private String cancelerName;/*** 取消人类型*/private Integer cancellerType;/*** 取消时间*/private LocalDateTime cancelTime;/*** 取消原因*/private String cancelReason;/*** 实际服务完成时间*/private LocalDateTime realServeEndTime;/*** 评价状态*/private Integer evaluationStatus;@Overridepublic String getSnapshotId() {return String.valueOf(id);}@Overridepublic Integer getSnapshotStatus() {return ordersStatus;}@Overridepublic void setSnapshotId(String snapshotId) {this.id = Long.parseLong(snapshotId);}@Overridepublic void setSnapshotStatus(Integer snapshotStatus) {this.ordersStatus = snapshotStatus;}}
1.2.1.5 定义事件变更动作类
/*** 支付成功执行的动作*/
@Slf4j
@Component("order_payed")
public class OrderPayedHandler implements StatusChangeHandler<OrderSnapshotDTO> {@Resourceprivate IOrdersCommonService ordersService;/*** 订单支付处理逻辑** @param bizId   业务id* @param bizSnapshot 快照*/@Overridepublic void handler(String bizId, StatusChangeEvent statusChangeEventEnum, OrderSnapshotDTO bizSnapshot) {log.info("支付成功事件处理逻辑开始,订单号:{}", bizId);}
}
1.2.1.5 定义订单状态机类

AbstractStateMachine状态机抽象类是状态机的核心类,是具体的状态机要继承的抽象类,比如我们实现订单状态机就需要继承AbstractStateMachine抽象类。
在这里插入图片描述

@Component
public class OrderStateMachine extends AbstractStateMachine<OrderSnapshotDTO> {protected OrderStateMachine(StateMachinePersister stateMachinePersister, BizSnapshotService bizSnapshotService, RedisTemplate redisTemplate) {super(stateMachinePersister, bizSnapshotService, redisTemplate);}/*** 指定状态机的名称* @return*/@Overrideprotected String getName() {return "order";}/*** * @param bizSnapshot*/@Overrideprotected void postProcessor(OrderSnapshotDTO bizSnapshot) {}/*** 指定订单的初始状态* @return*/@Overrideprotected StatusDefine getInitState() {return OrderStatusEnum.NO_PAY;}
}
1.2.1.6 状态机表设计

状态机使用MySQL对状态进行持久化,涉及到如下表:
状态机持久化表:
每个订单对应状态机表中的一条记录。
state_machine_name :针对订单的状态机起个名称叫order,针对服务单的状态机可以起个名称为serve。
biz_id:存储订单id
state:记录该订单的当前状态

create table `jzo2o-orders`.state_persister
(id                 bigint auto_increment comment '主键'constraint `PRIMARY`primary key,state_machine_name varchar(255)                       null comment '状态机名称',biz_id             varchar(255)                       null comment '业务id',state              varchar(255)                       null comment '状态',create_time        datetime default CURRENT_TIMESTAMP null comment '创建时间',update_time        datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',constraint 唯一索引unique (state_machine_name, biz_id)
)

状态机快照表:
一个订单在快照表有多条记录,每变一个状态会记录该状态下的快照信息(即订单相关的详细信息)便于查询订单变化的历史记录。
state_machine_name :同上
biz_id :同上
db_shard_id:暂时用不到
state:对应快照的状态
biz_data:快照信息(json格式),用在订单状态机就是记录订单相关的信息。

create table `jzo2o-orders`.biz_snapshot
(id                 bigint auto_increment comment '主键'constraint `PRIMARY`primary key,state_machine_name varchar(50)                        null comment '状态机名称',biz_id             varchar(50)                        null comment '业务id',db_shard_id        bigint                             null comment '分库键',state              varchar(50)                        null comment '状态代码',biz_data           varchar(5000)                      null comment '业务数据',create_time        datetime default CURRENT_TIMESTAMP not null comment '创建时间',update_time        datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间'
)

1.2.2 测试订单状态机

加载订单状态机

在base工程进行导入状态机

@Configuration
@ComponentScan({"com.jzo2o.orders.base.service","com.jzo2o.orders.base.handler"})
@MapperScan("com.jzo2o.orders.base.mapper")
@Import({OrderStateMachine.class})
@EnableConfigurationProperties({DispatchProperties.class, ExecutorProperties.class})
public class AutoImportConfiguration {
}
测试启动状态机

调用OrderStateMachine的start()方法启动一个订单的状态机,启动状态机表示订单用状态机管理状态,启动状态机后会设置订单的初始状态。
观察state_persister表有一条10100号订单的状态持久化记录,每条订单对应state_persister表的一条记录。
观察biz_snapshot表有一条10100号订单的快照信息,一条订单在biz_snapshot表对应多个条记录,每次订单状态变更都会产生一个快照

@SpringBootTest
@Slf4j
public class OrderStateMachineTest {@Resourceprivate OrderStateMachine orderStateMachine;@Testpublic void test_start() {//启动状态机String start = orderStateMachine.start("10100");log.info("返回初始状态:{}", start);}@Testpublic void test_changeStatus() {//状态变更orderStateMachine.changeStatus("10100", OrderStatusChangeEventEnum.PAYED);}}

根据状态变更事件定义可知,执行测试方法后1010订单的状态由NO_PAY(待支付)变更为DISPATCHING(派单中)。

1.2.3 使用订单状态机

目标:
下单时使用状态机
在支付成功时使用状态机

下单时启动状态机

下单后创建一个新订单,使用状态机的启动方法表示用状态机对该订单的状态开始进行管理。

@Transactional(rollbackFor = Exception.class)
public void add(Orders orders) {boolean save = this.save(orders);if (!save) {throw new DbRuntimeException("下单失败");}//构建快照对象OrderSnapshotDTO orderSnapshotDTO = BeanUtil.toBean(baseMapper.selectById(orders.getId()), OrderSnapshotDTO.class);//状态机启动orderStateMachine.start(null,String.valueOf(orders.getId()),orderSnapshotDTO);
}
支付成功使用状态机
  1. 定义状态变更动作类
    在动作类中更新订单的状态,在动作类中更新订单的状态要比在多处业务代码中对订单状态硬编码要强的多,因为可以在动作类中统一对订单状态进行管理。
    除了更新订单状态以外还需要填充订单快照的相关信息,这里主要是支付相关的信息,包括:支付状态、支付时间、支付服务的交易单号、第三方支付的交易单号等。
    @Resourceprivate IOrdersCommonService ordersService;/*** 订单支付处理逻辑** @param bizId   业务id* @param bizSnapshot 快照*/@Overridepublic void handler(String bizId, StatusChangeEvent statusChangeEventEnum, OrderSnapshotDTO bizSnapshot) {log.info("支付成功事件处理逻辑开始,订单号:{}", bizId);//统一对订单状态进行更新 未支付变为派单中OrderUpdateStatusDTO orderUpdateStatusDTO = new OrderUpdateStatusDTO();orderUpdateStatusDTO.setId(bizSnapshot.getId());orderUpdateStatusDTO.setOriginStatus(OrderStatusEnum.NO_PAY.getStatus());orderUpdateStatusDTO.setTargetStatus(OrderStatusEnum.DISPATCHING.getStatus());orderUpdateStatusDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());orderUpdateStatusDTO.setTradingOrderNo(bizSnapshot.getTradingOrderNo());orderUpdateStatusDTO.setTransactionId(bizSnapshot.getThirdOrderId());orderUpdateStatusDTO.setPayTime(bizSnapshot.getPayTime());orderUpdateStatusDTO.setTradingChannel(bizSnapshot.getTradingChannel());Integer res = ordersService.updateStatus(orderUpdateStatusDTO);if(res < 1){throw new CommonException("订单号为"+bizId+"的支付事件处理失败");}}
  1. 在支付成功方法中使用状态机
    使用状态机执行支付成功状态变更。
    /*** 支付成功, 更新数据库的订单表及其他信息** @param tradeStatusMsg 交易状态消息*/@Override@Transactional(rollbackFor = Exception.class)public void paySuccess(TradeStatusMsg tradeStatusMsg){/*        boolean update = lambdaUpdate().eq(Orders::getId, tradeStatusMsg.getProductOrderNo()).eq(Orders::getOrdersStatus,0) //订单状态只能由待支付 才可以变为派单中.set(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus()).set(Orders::getTransactionId, tradeStatusMsg.getTransactionId()).set(Orders::getOrdersStatus, OrderStatusEnum.DISPATCHING.getStatus()) //订单状态变为派单中.set(Orders::getPayTime, LocalDateTime.now()).update();if(!update){throw new CommonException("支付成功,更新"+tradeStatusMsg.getProductOrderNo()+"订单状态为派单中失败");}*///使用状态机将待支付状态变为派单中OrderSnapshotDTO orderSnapshotDTO = OrderSnapshotDTO.builder().payTime(LocalDateTime.now()).tradingOrderNo(tradeStatusMsg.getTradingOrderNo()).tradingChannel(tradeStatusMsg.getTradingChannel()).thirdOrderId(tradeStatusMsg.getTransactionId()).build();orderStateMachine.changeStatus(null,tradeStatusMsg.getProductAppId().toString(), OrderStatusChangeEventEnum.PAYED,orderSnapshotDTO);}
测试

在这里插入图片描述
在这里插入图片描述
测试成功

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

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

相关文章

【linux网络(四)】传输层协议详解(上)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux网络 1. 前言2. UDP协议…

【ARM】MDK出现报错error: A\L3903U的解决方法

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 解决MDK出现报错error: A\L3903U这样类型的报错 2、 问题场景 电脑或者软件因为意外情况导致崩溃&#xff0c;无法正常关闭&#xff0c;强制电脑重启之后&#xff0c;打开工程去编译出现下面的报错信息&#xff08;…

WPF 深入理解二、布局

布局与控件 常用得布局属性 HorizontalAlignment:用于设置元素的水平位置VerticalAlignment: 用于设置元素的垂直位置Margin: 指定元素与容器的边距Height: 指定元素的高度Width: 指定元素的宽度WinHeight/winWidth:指定元素的最小高度和宽度MaxHeight/MaxWidth: 指定元素的最…

压缩映射定理证明

收缩映射定理&#xff08;又称Banach不动点定理&#xff09;是一个重要的结果&#xff0c;特别是在分析和应用数学中。 定理&#xff08;收缩映射定理&#xff09;&#xff1a;假设是一个从度量空间 (X,d) 到自身的函数&#xff0c;如果 是一个收缩映射&#xff0c;即存在常数 …

华为---VLAN-配置Eth-Trunk链路聚合(三)

6.3 配置Eth-Trunk链路聚合 6.3.1 原理概述 在没有使用Eth-Trunk前&#xff0c;百兆以太网的双绞线在两个互连的网络设备间的带宽仅为100Mbit/s。若想达到更高的数据传输速率&#xff0c;则需要更换传输媒介&#xff0c;使用千兆光纤或升级成为千兆以太网。这样的解决方案成本…

GenICam标准(五)

系列文章目录 GenICam标准&#xff08;一&#xff09; GenICam标准&#xff08;二&#xff09; GenICam标准&#xff08;三&#xff09; GenICam标准&#xff08;四&#xff09; GenICam标准&#xff08;五&#xff09; GenICam标准&#xff08;六&#xff09; 文章目录 系列文…

【Stable Diffusion教程】AI绘画工具SD如何安装使用?三种方法带你轻松上手!(附安装包和云端部署教程)

大家好&#xff0c;我是向阳 AI绘画专业工具Stable Diffusion在哪里用怎么安装&#xff1f;这一期给大家介绍三种使用SD的方法&#xff0c;无论你有没有专业显卡都能轻松上手SD哦&#xff5e; 一、SD本地部署秋葉安装包安装方法 如果你有进一步的需求&#xff0c;想要学习SD…

丘钛微注册陷入“停滞”IPO中止:营收净利润连年下滑,毛利率骤降

《港湾商业观察》施子夫 王璐 从2021年6月末算起&#xff0c;在冲刺创业板这条道路上&#xff0c;昆山丘钛微电子科技股份有限公司&#xff08;以下简称&#xff0c;丘钛微&#xff09;已经耗时了三年。 实际上在三年中&#xff0c;丘钛微早在2022年8月17日就首发过会&#…

52.Python-web框架-Django - 多语言编译-fuzzy错误

目录 1.起因 2.原因 3.解决方法 3.1手动移除fuzzy标记 3.2重新生成po文件&#xff0c;并检查是否还存在fuzzy标记 3.3重新编译生成mo文件 1.起因 在Django的国际化和本地化过程中&#xff0c;当你发现某些字段仅显示msgid&#xff0c;而不显示msgstr时&#xff0c;可能是…

燃气守护神:燃气管网安全运行监测解决方案

在这个智能科技日新月异的时代&#xff0c;燃气安全却时有发生&#xff0c;严重危害人们的生命财产安全&#xff0c;因此旭华智能根据相关政策要求并结合自身优势&#xff0c;打造了一套燃气管网安全运行监测解决方案&#xff0c;他犹如一位“燃气守护神”&#xff0c;悄然守护…

计算机组成原理之存储器

文章目录 存储器概述存储器的分类情况按照存储器在系统中的作用分类按存储介质分类按存取方式分类 主存储器的技术指标 存储器概述 程序的局部性原理&#xff08;构成多级存储系统的依据&#xff09;&#xff1a;在某一个时间段你频繁访问某一局部的存储器地址空间&#xff0c;…

綦江蜘蛛池四川官网下载

baidu搜索&#xff1a;如何联系八爪鱼SEO? baidu搜索&#xff1a;如何联系八爪鱼SEO? baidu搜索&#xff1a;如何联系八爪鱼SEO? CCSEO蜘蛛统计开发思路一般包括以下几个步骤: 定义需求:明确统计蜘蛛访问数据的目标和要求,例如需要获取哪些信息,统计的精度和频率等。 确定数…

重生之 SpringBoot3 入门保姆级学习(18、事件驱动开发解耦合)

重生之 SpringBoot3 入门保姆级学习&#xff08;18、事件驱动开发解耦合&#xff09; 5、SpringBoot3 核心5.1 原始开发5.2 事件驱动开发 5、SpringBoot3 核心 5.1 原始开发 LoginController package com.zhong.bootcenter.controller;import com.zhong.bootcenter.service.A…

爬虫初学篇——看完这些还怕自己入门不了?

初次学习爬虫&#xff0c;知识笔记小分享 学scrapy框架可看&#xff1a;孤寒者博主的【Python爬虫必备—&#xff1e;Scrapy框架快速入门篇——上】 目录&#x1f31f; 一、&#x1f349;基础知识二、&#x1f349;http协议&#xff1a;三、&#x1f349;解析网页(1) xpath的用…

基于单片机的无线遥控自动翻书机械臂设计

摘 要&#xff1a; 本设备的重点控制部件为单片机&#xff0c;充分实现了其自动化的目的。相关研究表明&#xff0c;它操作简单便捷&#xff0c;使残疾人在翻书时提供了较大的便利&#xff0c;使用价值性极高&#xff0c;具有很大的发展空间。 关键词&#xff1a; 机械臂&…

25天录用!快到飞起的宝藏SSCI,免版面费,1天见刊!毕业评职即刻拿下

本周投稿推荐 SSCI • 中科院2区&#xff0c;6.0-7.0&#xff08;录用友好&#xff09; EI • 各领域沾边均可&#xff08;2天录用&#xff09; CNKI • 7天录用-检索&#xff08;急录友好&#xff09; SCI&EI • 4区生物医学类&#xff0c;0.5-1.0&#xff08;录用…

【odoo17】富文本小部件widget=“html“的使用

概要 HTML富文本字段通常用于在模型中存储和显示格式化的文本。通过这种字段&#xff0c;用户可以利用HTML标签来格式化文本&#xff0c;从而在前端呈现更丰富的内容。 在Odoo中&#xff0c;HTML字段在没有明确指定widget"html"的情况下&#xff0c;也会默认显示为富…

Windows NT 3.5程序员讲述微软标志性“3D管道”屏幕保护程序的起源故事

人们使用屏保程序来防止 CRT 显示器"烧毁"&#xff0c;因为静态图像会永久损坏屏幕。像 3D Pipes 这样的屏保程序能在显示器处于非活动状态时为其提供动画效果&#xff0c;从而保护屏幕并延长其使用寿命。此外&#xff0c;它们还能在用户不使用电脑时为其提供可定制的…

软件安全漏洞分析与发现 复习笔记

1 绪论 本节无考点&#xff0c;仅供了解。 2 基础知识 考点&#xff1a; 汇编码理解和撰写&#xff0c;三种内存地址&#xff0c;不同的页管理方式。windows保护模式可能出题 汇编算法的阅读理解给出汇编片段&#xff0c;理解其意思&#xff0c;输入->输出保护模式…

Aigtek功率放大器参数怎么选型的

功率放大器是电子系统中重要的组成部分&#xff0c;选型合适的功率放大器对系统的性能和可靠性至关重要。本文下面安泰电子将介绍如何选型功率放大器的关键步骤和考虑因素。 首先&#xff0c;确定应用需求。在选型功率放大器之前&#xff0c;确定应用需求是至关重要的第一步。了…