需求背景:存在状态流转的预约单
一.数据库设计
sql
复制代码
CREATE TABLE `appointment` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
`appoint_type` int(11) NOT NULL COMMENT '预约类型(0:线下查房...)',
`appoint_user_id` bigint(20) NOT NULL COMMENT '预约人userId',
`appoint_store_id` bigint(20) NOT NULL COMMENT '预约门店',
`appoint_service_type` int(11) DEFAULT NULL COMMENT '预约服务类型(9:儿科查房 7:产科查房 8:中医查房)',
`appoint_doctor_id` bigint(11) DEFAULT NULL COMMENT '预约医生id',
`appoint_date` date DEFAULT NULL COMMENT '预约日期(精确到日)',
`appoint_time_start` time NOT NULL COMMENT '预约开始时间',
`appoint_time_end` time NOT NULL COMMENT '预约结束时间',
`status` int(11) NOT NULL COMMENT '状态(-1:已取消 0:待接单 1:待分配(已拒绝) 2:待查房 3:待小结 4:待签名 100:已完成 )',
`drive_appointment_id` bigint(20) DEFAULT NULL COMMENT '驱动预约单id(null:代表驱动预约单)',
`appointment_setting_id` bigint(20) DEFAULT NULL COMMENT '预约单-配置id',
`create_id` bigint(20) DEFAULT NULL COMMENT '创建人id',
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`modify_id` bigint(20) DEFAULT NULL COMMENT '修改人',
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`deleted` tinyint(1) DEFAULT '0' COMMENT '删除标记;0-正常 ;1-删除',
PRIMARY KEY (`id`),
KEY `idx_drive_appointment_id` (`drive_appointment_id`) USING BTREE,
KEY `idx_appointment_setting_id` (`appointment_setting_id`) USING BTREE,
KEY `idx_appoint_user_id` (`appoint_user_id`) USING BTREE
) AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='预约单';
-- 预约单-状态流转表
CREATE TABLE `stbella-his`.appointment_status_log
(
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
appointment_id bigint(20) not null COMMENT '预约单id',
before_status int(11) NOT null COMMENT '前状态',
after_status int(11) NOT null COMMENT '后状态',
handle_type int(11) not null COMMENT '操作类型',
handle_user_type int(11) not null COMMENT '操作人类型',
create_id bigint(20) DEFAULT NULL COMMENT '创建人id',
gmt_create datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
modify_id bigint(20) DEFAULT NULL COMMENT '修改人',
gmt_modified datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
deleted tinyint(1) DEFAULT '0' COMMENT '删除标记;0-正常 ;1-删除',
PRIMARY KEY (`id`),
KEY `idx_appointment_id` (`appointment_id`) USING BTREE
)
comment '预约单-状态流转表';
二.状态枚举类
java
复制代码
@Getter
@AllArgsConstructor
public enum AppointStatusEnum {
INIT(-100, "初始化"),
CANCEL(-1, "已取消"),
WAIT_RECEIVE(0, "待接单"),
WAIT_DISTRIBUTE(1, "待分配"),
WAIT_CHECK_ROOM(2, "待查房"),
WAIT_SUMMARY(3, "待小结"),
WAIT_SIGN(4, "待签名"),
COMPLETE(100, "已完成");
private final int code;
private final String name;
public static AppointStatusEnum getEnum(int code) {
for (AppointStatusEnum statusEnum : AppointStatusEnum.values()) {
if (statusEnum.getCode() == code) {
return statusEnum;
}
}
return null;
}
}
三. 上下文参数类:参数传递
java
复制代码
@Builder
@Data
public class AppointContext implements Serializable {
private static final long serialVersionUID = 3542771730176821092L;
private UserTokenInfoDTO userTokenInfoDTO;
private ClientEnum clientEnum;
private AppointPO appointPO;
}
四.状态机流转上下文类:所有要执行的动作都在这里记录
java
复制代码
@Data
public class AppointHandleContext implements Serializable {
private static final long serialVersionUID = 1658366511210864400L;
private IAppointStatusHandler statusHandler = AppointStatusHandlerFactory.getStatusHandler(AppointStatusEnum.INIT);
public AppointDetailVO detail(AppointHandleContext handleContext,
public Boolean cancel(AppointHandleContext handleContext, AppointContext context) {
return statusHandler.cancel(handleContext, context);
}
public String add(AppointHandleContext handleContext, AppointContext context) {
return statusHandler.add(handleContext, context);
}
public Boolean update(AppointHandleContext handleContext, AppointContext context) {
return statusHandler.update(handleContext, context);
}
}
五.状态处理器工厂类
java
复制代码
@Component
public class AppointStatusHandlerFactory implements ApplicationContextAware {
private static final Map<AppointStatusEnum, IAppointStatusHandler> MAP = Maps.newHashMapWithExpectedSize(AppointStatusEnum.values().length);
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IAppointStatusHandler> beansOfType = applicationContext.getBeansOfType(IAppointStatusHandler.class);
if (CollectionUtil.isEmpty(beansOfType)) {
return;
}
beansOfType.forEach((key, statusHandler) -> MAP.put(statusHandler.getStatus(), statusHandler));
}
public static IAppointStatusHandler getStatusHandler(AppointStatusEnum appointStatusEnum) {
return Optional.ofNullable(MAP.get(appointStatusEnum))
.orElseThrow(() -> new BusinessException(ResultEnum.PARAM_ERROR, "预约单状态异常"));
}
}
六.状态接口类
java
复制代码
public interface IAppointStatusHandler {
AppointStatusEnum getStatus();
AppointDetailVO detail(AppointHandleContext handleContext, AppointContext context);
Boolean cancel(AppointHandleContext handleContext, AppointContext context);
String add(AppointHandleContext handleContext, AppointContext context);
Boolean update(AppointHandleContext handleContext, AppointContext context);
」
七.状态抽象实现类
java
复制代码
@Slf4j
@Component
public abstract class AbstractAppointStatusHandler implements IAppointStatusHandler {
private static final String LOG_PRE = "预约单状态流转异常,method:{},handleContext:{},context:{}";
@Override
public AppointDetailVO detail(AppointHandleContext handleContext, AppointContext context) {
log.error(LOG_PRE, "detail", JSONUtil.toJsonStr(handleContext), JSONUtil.toJsonStr(context));
throw new BusinessException(ResultEnum.PARAM_ERROR, "当前状态不允许查询");
}
@Override
public Boolean cancel(AppointHandleContext handleContext, AppointContext context) {
log.error(LOG_PRE, "cancel", JSONUtil.toJsonStr(handleContext), JSONUtil.toJsonStr(context));
throw new BusinessException(ResultEnum.PARAM_ERROR, "当前状态不允许取消");
}
@Override
public String add(AppointHandleContext handleContext, AppointContext context) {
log.error(LOG_PRE, "add", JSONUtil.toJsonStr(handleContext), JSONUtil.toJsonStr(context));
throw new BusinessException(ResultEnum.PARAM_ERROR, "当前状态不允许新增");
}
@Override
public Boolean update(AppointHandleContext handleContext, AppointContext context) {
log.error(LOG_PRE, "update", JSONUtil.toJsonStr(handleContext), JSONUtil.toJsonStr(context));
throw new BusinessException(ResultEnum.PARAM_ERROR, "当前状态不允许修改");
}
}
八.每个状态节点实现类
java
复制代码
@Slf4j
@Component
public class AppointInitHandler extends AbstractAppointStatusHandler {
@Override
public AppointStatusEnum getStatus() {
return AppointStatusEnum.INIT;
}
@Override
public AppointDetailVO detail(AppointHandleContext handleContext, AppointContext context) {
return appointSupport.detail(context);
}
@Override
public String add(AppointHandleContext handleContext, AppointContext context)
{
}
}
java
复制代码
@Slf4j
@Component
public class AppointWaitCheckRoomHandler extends AbstractAppointStatusHandler {
@Override
public AppointStatusEnum getStatus() {
return AppointStatusEnum.WAIT_CHECK_ROOM;
}
@Resource
private AppointSupport appointSupport;
@Override
public AppointDetailVO detail(AppointHandleContext handleContext, AppointContext context) {
AppointDetailVO detail = appointSupport.detail(context);
ClientEnum clientEnum = context.getClientEnum();
AppointPO appointPO = context.getAppointPO();
switch (clientEnum) {
case HIS_NURSE:
this.nurseDetail(detail, appointPO);
break;
case HIS_DOCTOR:
this.doctorDetail(detail, appointPO);
break;
default:
break;
}
return detail;
}
@Override
public Boolean cancel(AppointHandleContext handleContext, AppointContext context) {
return appointSupport.handleOnlyStatus(context, AppointStatusEnum.CANCEL, AppointHandleTypeEnum.CANCEL);
}
@Override
public Boolean startCheckRoom(AppointHandleContext handleContext, AppointContext context) {
return appointSupport.handleOnlyStatus(context, AppointStatusEnum.WAIT_SUMMARY, AppointHandleTypeEnum.START_CHECK_ROOM);
}
java
复制代码
@Slf4j
@Component
public class AppointCompleteHandler extends AbstractAppointStatusHandler {
@Override
public AppointStatusEnum getStatus() {
return AppointStatusEnum.COMPLETE;
}
@Override
public AppointDetailVO detail(AppointHandleContext handleContext, AppointContext context) {
AppointDetailVO detail = appointSupport.detail(context);
ClientEnum clientEnum = context.getClientEnum();
AppointPO appointPO = context.getAppointPO();
switch (clientEnum) {
case HIS_NURSE:
this.nurseDetail(detail, appointPO);
break;
case HIS_DOCTOR:
this.doctorDetail(detail, appointPO);
break;
default:
break;
}
return detail;
}
@Override
public AppointCheckRoomSignNotifyVO getAppointCheckRoomSignNotifyVO(AppointHandleContext handleContext, AppointContext context) {
}
}
九.具体行为调用代码类
java
复制代码
@Override
public void bindPicpCustomer(AppointClinicBindReq req, UserTokenInfoDTO userTokenInfoDTO) {
AppointPO appointPO = Optional.ofNullable(appointRepository.getOnePOById(req.getAppointmentId())).orElseThrow(() ->
new BusinessException(ResultEnum.PARAM_ERROR, "预约单数据不存在或已被删除"));
AppointHandleContext handleContext = new AppointHandleContext();
handleContext.setStatusHandler(AppointStatusHandlerFactory.getStatusHandler(AppointStatusEnum.getEnum(appointPO.getStatus())));
handleContext.bindPicpCustomer(handleContext, AppointContext.builder()
.appointPO(appointPO)
.clientEnum(appointSupport.getLoginClientEnum(userTokenInfoDTO))
.userTokenInfoDTO(userTokenInfoDTO)
.picpUsers(req.getPicpUsers())
.build());
}