设计模式(实战项目)-状态模式

发布于:2024-07-01 ⋅ 阅读:(13) ⋅ 点赞:(0)

需求背景:存在状态流转的预约单

一.数据库设计

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 '预约单-状态流转表'; 

二.状态枚举类

@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;
	}

}

三. 上下文参数类:参数传递

@Builder
@Data
public class AppointContext implements Serializable {

	private static final long serialVersionUID = 3542771730176821092L;

	private UserTokenInfoDTO userTokenInfoDTO;

	private ClientEnum clientEnum;

	private AppointPO appointPO;

}

四.状态机流转上下文类:所有要执行的动作都在这里记录

@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);
	}

	

}

五.状态处理器工厂类

@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, "预约单状态异常"));
	}

}

六.状态接口类

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);

」

七.状态抽象实现类

@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, "当前状态不允许修改");
	}
}

八.每个状态节点实现类

@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) 
    {
		
	}

}
@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);
	}
@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) {
		
	}
}

九.具体行为调用代码类

@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());
	}