liusheng
昨天 b57304d9917beab4671442a0018c1c3a2d681640
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
 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<String, String> redisTemplate;
 
    @Autowired
    private List<InhospStateHandler> handlers;
 
    /**
     * 状态处理器映射表
     */
    private final Map<String, InhospStateHandler> 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<PatMedInhosp> 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<PatMedInhosp> records = patMedInhospService.selectPatMedInhosp(query);
 
        if (CollectionUtils.isEmpty(records)) {
            return null;
        }
 
        return records.get(0).getInhospstate();
    }
}