liusheng
2026-10-12 0c1f8c479065739f3c7a5126de8e667035d00b58
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/**
 * ============================================================================
 * 住院状态机集成指南
 * ============================================================================
 * 
 * 一、状态机文件清单
 * ============================================================================
 * 
 * 1. 核心枚举
 *    └─ com.smartor.common.enums.InhospStateEnum
 *       定义状态及转换规则
 * 
 * 2. 异常类
 *    └─ com.smartor.common.exception.StateTransitionException
 *       处理非法状态转换
 * 
 * 3. 处理器接口
 *    └─ com.smartor.statemachine.handler.InhospStateHandler
 *       状态处理器接口
 * 
 * 4. 处理器实现
 *    ├─ com.smartor.statemachine.handler.impl.PreAdmissionHandler
 *    │  处理预入院(state=3)
 *    ├─ com.smartor.statemachine.handler.impl.InHospitalHandler
 *    │  处理入院(state=0)
 *    └─ com.smartor.statemachine.handler.impl.DischargeHandler
 *       处理出院(state=1)
 * 
 * 5. 状态机管理器
 *    └─ com.smartor.statemachine.InhospStateMachine
 *       协调状态转换、管理分布式锁
 * 
 * 6. 门面服务
 *    └─ com.smartor.statemachine.InhospStateMachineService
 *       对外API接口
 * 
 * 7. 使用示例
 *    └─ com.smartor.statemachine.InhospStateMachineExample
 *       集成示例代码
 * 
 * 
 * 二、如何在现有代码中集成
 * ============================================================================
 * 
 * 原代码位置:
 * ServiceSLTDHealthcareRecordServiceImpl.processPatientInhospInfo (L561-677)
 * 
 * 【方案1】完全替换(推荐用于新功能)
 * -----------------------------------------
 * 
 * @Autowired
 * private InhospStateMachineService stateMachineService;
 * 
 * private void processPatientInhospInfo(ServiceSLTDInhospResDTO dto, PatArchive patArchive, String cry) {
 *     PatMedInhosp patMedInhosp = buildPatientInhospInfo(dto, patArchive, cry);
 *     stateMachineService.processStateChange(patMedInhosp, cry);
 * }
 * 
 * 
 * 【方案2】双写模式(推荐用于生产环境验证)
 * -----------------------------------------
 * 
 * @Autowired
 * private InhospStateMachineService stateMachineService;
 * 
 * private void processPatientInhospInfo(ServiceSLTDInhospResDTO dto, PatArchive patArchive, String cry) {
 *     PatMedInhosp patMedInhosp = buildPatientInhospInfo(dto, patArchive, cry);
 *     
 *     // 方式A:保留原有逻辑
 *     // processPatientInhospInfoOld(dto, patArchive, cry);
 *     
 *     // 方式B:使用状态机
 *     try {
 *         boolean success = stateMachineService.processStateChange(patMedInhosp, cry);
 *         log.info("【状态机】处理结果:{}, serialnum={}, cry={}", success, patMedInhosp.getSerialnum(), cry);
 *     } catch (Exception e) {
 *         log.error("【状态机】处理异常:serialnum={}, cry={}, 错误:{}", patMedInhosp.getSerialnum(), cry, e.getMessage());
 *     }
 * }
 * 
 * 
 * 【方案3】开关控制(推荐用于灰度发布)
 * -----------------------------------------
 * 
 * @Value("${inhosp.statemachine.enabled:false}")
 * private boolean stateMachineEnabled;
 * 
 * @Autowired
 * private InhospStateMachineService stateMachineService;
 * 
 * private void processPatientInhospInfo(ServiceSLTDInhospResDTO dto, PatArchive patArchive, String cry) {
 *     PatMedInhosp patMedInhosp = buildPatientInhospInfo(dto, patArchive, cry);
 *     
 *     if (stateMachineEnabled) {
 *         // 使用状态机
 *         stateMachineService.processStateChange(patMedInhosp, cry);
 *     } else {
 *         // 使用原有逻辑
 *         processPatientInhospInfoOld(dto, patArchive, cry);
 *     }
 * }
 * 
 * 配置文件 application.yml:
 * inhosp:
 *   statemachine:
 *     enabled: true  # 开启状态机
 * 
 * 
 * 三、数据库索引建议
 * ============================================================================
 * 
 * 【选项1】允许多状态记录(推荐,与现有逻辑兼容)
 * -----------------------------------------
 * CREATE UNIQUE INDEX uk_serialnum_orgid_state 
 * ON pat_med_inhosp(serialnum, orgid, inhospstate);
 * 
 * 优点:
 * - 与现有数据兼容
 * - 只拦截相同状态的重复插入
 * - 状态机处理器可以决定是新增还是更新
 * 
 * 
 * 【选项2】强制单记录(需数据迁移)
 * -----------------------------------------
 * CREATE UNIQUE INDEX uk_serialnum_orgid 
 * ON pat_med_inhosp(serialnum, orgid);
 * 
 * 优点:
 * - 数据一致性更强
 * - 避免一患多记录问题
 * 
 * 缺点:
 * - 需要清理历史数据
 * - 所有状态变更必须走更新
 * 
 * 数据清理脚本:
 * -- 保留每个患者最新状态的记录,删除其他记录
 * DELETE t1 FROM pat_med_inhosp t1
 * INNER JOIN (
 *     SELECT serialnum, orgid, MAX(inhospid) as max_id
 *     FROM pat_med_inhosp
 *     GROUP BY serialnum, orgid
 *     HAVING COUNT(*) > 1
 * ) t2 ON t1.serialnum = t2.serialnum 
 *     AND t1.orgid = t2.orgid 
 *     AND t1.inhospid < t2.max_id;
 * 
 * 
 * 四、状态机核心优势
 * ============================================================================
 * 
 * 1. 并发安全
 *    ✅ 自动加分布式锁(Redis)
 *    ✅ 锁粒度:serialnum + orgid
 *    ✅ 超时时间:10秒(可配置)
 *    ✅ Redis不可用时自动降级
 * 
 * 2. 状态校验
 *    ✅ 强制单向流转:3(预入院) → 0(入院) → 1(出院)
 *    ✅ 阻止非法转换(如:预入院→出院)
 *    ✅ 阻止重复操作(如:重复入院)
 * 
 * 3. 代码简化
 *    ✅ 100+行if-else → 10行状态机调用
 *    ✅ 策略模式,易扩展
 *    ✅ 集中式状态管理
 * 
 * 4. 日志完善
 *    ✅ 每个状态转换都有日志
 *    ✅ 异常信息详细
 *    ✅ 便于问题排查
 * 
 * 
 * 五、测试验证建议
 * ============================================================================
 * 
 * 1. 单元测试
 *    - 测试状态枚举的转换规则
 *    - 测试每个Handler的逻辑
 *    - 测试状态机的锁机制
 * 
 * 2. 集成测试
 *    - 正常流程:预入院→入院→出院
 *    - 跳过预入院:直接入院→出院
 *    - 异常流程:重复操作、非法转换
 * 
 * 3. 并发测试
 *    - 使用JMeter模拟并发请求
 *    - 验证同一患者100并发不会重复插入
 *    - 验证Redis锁的有效性
 * 
 * 4. 生产验证
 *    - 先开启双写模式
 *    - 对比原逻辑与状态机结果
 *    - 验证1-2周后完全切换
 * 
 * 
 * 六、配置参数(可选)
 * ============================================================================
 * 
 * application.yml 可添加以下配置:
 * 
 * inhosp:
 *   statemachine:
 *     enabled: true                  # 是否启用状态机
 *     lock-timeout: 10               # 分布式锁超时时间(秒)
 *     allow-direct-discharge: false  # 是否允许无入院记录直接出院
 * 
 * 
 * 七、常见问题
 * ============================================================================
 * 
 * Q1: 状态机是否会影响性能?
 * A: 不会。状态机内部逻辑与原代码相似,只是结构更清晰。Redis锁的获取耗时在毫秒级。
 * 
 * Q2: Redis不可用时会怎样?
 * A: 状态机会自动降级为无锁模式,依然可以执行,但并发安全依赖数据库唯一索引。
 * 
 * Q3: 如何扩展新状态?
 * A: 
 *    1. 在InhospStateEnum中添加新状态
 *    2. 创建对应的Handler实现
 *    3. 状态机会自动注册并识别
 * 
 * Q4: 状态机是否支持事务?
 * A: 支持。状态机调用本身可以在@Transactional方法中使用。
 * 
 * Q5: 出院后能否再入院?
 * A: 目前逻辑不允许。如果需要支持,建议在数据模型中增加"就诊批次"字段。
 * 
 * 
 * 八、后续优化方向
 * ============================================================================
 * 
 * 1. 状态变更历史表
 *    - 记录每次状态转换
 *    - 支持状态回溯
 *    - 便于审计
 * 
 * 2. 状态机可视化
 *    - 提供状态流转图
 *    - 实时监控状态分布
 * 
 * 3. 补偿机制
 *    - 支持状态回滚
 *    - 异常状态修复
 * 
 * 4. 配置化状态规则
 *    - 从数据库读取状态转换规则
 *    - 动态调整不需要重启
 * 
 * 
 * ============================================================================
 * 联系人:smartor开发团队
 * 创建日期:2026-01-29
 * ============================================================================
 */
package com.smartor.statemachine;