package com.smartor.statemachine; import com.ruoyi.common.utils.StringUtils; import com.smartor.common.enums.InhospStateEnum; import com.smartor.common.exception.StateTransitionException; import com.smartor.domain.PatMedInhosp; import com.smartor.service.IPatMedInhospService; import com.smartor.statemachine.handler.InhospStateHandler; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; /** * 住院状态机管理器 * 负责状态转换的协调和分布式锁管理 * * @author smartor */ @Slf4j @Component public class InhospStateMachine { @Autowired private IPatMedInhospService patMedInhospService; @Autowired private RedisTemplate redisTemplate; @Autowired private List handlers; /** * 状态处理器映射表 */ private final Map handlerMap = new ConcurrentHashMap<>(); /** * 分布式锁超时时间(秒) */ private static final int LOCK_TIMEOUT = 10; /** * 分布式锁前缀 */ private static final String LOCK_KEY_PREFIX = "inhosp:state:lock:"; @PostConstruct public void init() { // 初始化处理器映射 if (CollectionUtils.isNotEmpty(handlers)) { for (InhospStateHandler handler : handlers) { handlerMap.put(handler.getStateCode(), handler); log.info("【InhospStateMachine】注册状态处理器:{} -> {}", handler.getStateCode(), handler.getClass().getSimpleName()); } } } /** * 执行状态转换(带分布式锁) * * @param patMedInhosp 住院记录对象 * @param targetState 目标状态 * @return 是否成功 */ public boolean transitionState(PatMedInhosp patMedInhosp, String targetState) { String serialnum = StringUtils.trim(patMedInhosp.getSerialnum()); String orgid = StringUtils.trim(patMedInhosp.getOrgid()); if (StringUtils.isEmpty(serialnum) || StringUtils.isEmpty(orgid)) { log.error("【InhospStateMachine】serialnum或orgid为空,无法执行状态转换"); return false; } // 生成分布式锁key(不包含state,锁住整个患者记录) String lockKey = LOCK_KEY_PREFIX + serialnum + ":" + orgid; Boolean locked = false; try { // 尝试获取分布式锁 locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", LOCK_TIMEOUT, TimeUnit.SECONDS); if (locked == null) { log.warn("【InhospStateMachine】Redis不可用,降级为无锁执行,serialnum={}, orgid={}", serialnum, orgid); return executeStateTransition(patMedInhosp, targetState); } if (!Boolean.TRUE.equals(locked)) { log.warn("【InhospStateMachine】获取分布式锁失败,其他线程正在处理,serialnum={}, orgid={}", serialnum, orgid); return false; } // 获取锁成功,执行状态转换 return executeStateTransition(patMedInhosp, targetState); } catch (Exception e) { log.error("【InhospStateMachine】状态转换异常:serialnum={}, orgid={}, targetState={}, 错误:{}", serialnum, orgid, targetState, e.getMessage(), e); return false; } finally { // 释放锁 if (Boolean.TRUE.equals(locked)) { try { redisTemplate.delete(lockKey); log.debug("【InhospStateMachine】释放分布式锁成功,lockKey={}", lockKey); } catch (Exception e) { log.warn("【InhospStateMachine】释放分布式锁失败:lockKey={}, 错误:{}", lockKey, e.getMessage()); } } } } /** * 执行状态转换逻辑 * * @param patMedInhosp 住院记录对象 * @param targetState 目标状态 * @return 是否成功 */ private boolean executeStateTransition(PatMedInhosp patMedInhosp, String targetState) { String serialnum = patMedInhosp.getSerialnum(); String orgid = patMedInhosp.getOrgid(); log.info("【InhospStateMachine】开始执行状态转换:serialnum={}, orgid={}, targetState={}", serialnum, orgid, targetState); // 1. 查询当前状态 PatMedInhosp query = new PatMedInhosp(); query.setSerialnum(StringUtils.trim(serialnum)); query.setOrgid(StringUtils.trim(orgid)); query.setPatno(StringUtils.trim(patMedInhosp.getPatno())); query.setInhospstate(null); // 查询所有状态 List existingRecords = patMedInhospService.selectPatMedInhosp(query); String currentState = null; if (CollectionUtils.isNotEmpty(existingRecords)) { // 如果存在多条记录,取最新的一条(理论上经过状态机后不应该有多条) currentState = existingRecords.get(0).getInhospstate(); log.info("【InhospStateMachine】当前状态:{}", currentState); } // 2. 验证状态转换是否合法 if (!InhospStateEnum.isValidTransition(currentState, targetState)) { log.error("【InhospStateMachine】非法的状态转换:从 [{}] 到 [{}],serialnum={}, orgid={}", currentState, targetState, serialnum, orgid); throw new StateTransitionException(currentState, targetState); } // 3. 获取对应的状态处理器 InhospStateHandler handler = handlerMap.get(targetState); if (handler == null) { log.error("【InhospStateMachine】未找到状态处理器:targetState={}", targetState); return false; } // 4. 执行状态处理 boolean result = handler.handle(patMedInhosp); if (result) { log.info("【InhospStateMachine】状态转换成功:serialnum={}, orgid={}, {} -> {}", serialnum, orgid, currentState, targetState); } else { log.warn("【InhospStateMachine】状态转换失败:serialnum={}, orgid={}, {} -> {}", serialnum, orgid, currentState, targetState); } return result; } /** * 获取当前状态 * * @param serialnum 流水号 * @param orgid 机构ID * @param patno 患者编号 * @return 当前状态码,如果不存在返回null */ public String getCurrentState(String serialnum, String orgid, String patno) { PatMedInhosp query = new PatMedInhosp(); query.setSerialnum(StringUtils.trim(serialnum)); query.setOrgid(StringUtils.trim(orgid)); query.setPatno(StringUtils.trim(patno)); query.setInhospstate(null); List records = patMedInhospService.selectPatMedInhosp(query); if (CollectionUtils.isEmpty(records)) { return null; } return records.get(0).getInhospstate(); } }