怎么给网站做百度优化,婚庆网站开发计划书,上海美术设计公司,广西住房城乡建设厅官网站目录 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 状态机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中添加状态机组件的依赖
dependencygroupIdcom.jzo2o/groupIdartifactIdjzo2o-statemachine/artifactIdversion1.0-SNAPSHOT/version
/dependency1.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 StatusChangeHandlerOrderSnapshotDTO {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 AbstractStateMachineOrderSnapshotDTO {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 PRIMARYprimary 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 PRIMARYprimary 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);
}支付成功使用状态机
定义状态变更动作类 在动作类中更新订单的状态在动作类中更新订单的状态要比在多处业务代码中对订单状态硬编码要强的多因为可以在动作类中统一对订单状态进行管理。 除了更新订单状态以外还需要填充订单快照的相关信息这里主要是支付相关的信息包括支付状态、支付时间、支付服务的交易单号、第三方支付的交易单号等。 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的支付事件处理失败);}}在支付成功方法中使用状态机 使用状态机执行支付成功状态变更。 /*** 支付成功 更新数据库的订单表及其他信息** param tradeStatusMsg 交易状态消息*/OverrideTransactional(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);}测试 测试成功